I've implemented the validation for incompatible app in my android application by comparing package name and signature of PackageInfo. I get information about installed packages by:
val packages = context.ctx.packageManager.getInstalledPackages(PackageManager.GET_META_DATA or PackageManager.GET_SIGNATURES)
and after that just compare strings of package names and signatures. I convert PackageInfo.signatures into Hex by this function:
fun bytesToHex(bytes: ByteArray): String {
val hexArray = charArrayOf('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F')
val hexChars = CharArray(bytes.size * 2)
var v: Int
for (j in bytes.indices) {
v = bytes[j].toInt() and 0xFF
hexChars[j * 2] = hexArray[v.ushr(4)]
hexChars[j * 2 + 1] = hexArray[v and 0x0F]
}
return String(hexChars)
}
and it works fine.
Now I want to implement several unit tests for this functionality and faced difficulties: how can I mock PackageInfo.signatures? How can I translate my SHA1 fingerprint string into android.content.pm.Signature?
I wouldn't recommend mocking the framework classes as that will be fragile. Instead, just create your own interface for providing the signatures and have an implementation used in the real application that relies on the framework. For example:
interface SignatureProvider {
val signatures: List<String>
}
class AndroidSignatureProvider(private val packageManager: PackageManager): SignatureProvider {
private val flags = PackageManager.GET_META_DATA or PackageManager.GET_SIGNATURES
override val signatures: List<String>
get() = packageManager.getInstalledPackages(flags)
.flatMap { it.packageInfo.signatures.toList() }
.map { it.toCharsString() }
}
Then in your tests, you can just mock a SignatureProvider
to return the different signature strings that you want to test against:
private lateinit var signatureProvider: SignatureProvider
@Before fun setup() {
signatureProvider = mock(SignatureProvider::class.java)
doReturn(listOf("sigA", "sigB")).`when`(signatureProvider)`.signatures
}