通过 SHA1 指纹字符串模拟 PackageInfo.signatures
Mock PackageInfo.signatures by SHA1 fingerprint string
我通过比较包名和 PackageInfo 的签名在我的 android 应用程序中实现了对不兼容应用程序的验证。我通过以下方式获取有关已安装软件包的信息:
val packages = context.ctx.packageManager.getInstalledPackages(PackageManager.GET_META_DATA or PackageManager.GET_SIGNATURES)
然后比较包名和签名的字符串。我通过这个函数将 PackageInfo.signatures 转换为十六进制:
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)
}
而且效果很好。
现在我想为此功能实现几个单元测试并遇到困难:我如何模拟 PackageInfo.signatures?如何将我的 SHA1 指纹字符串转换为 android.content.pm.Signature?
我不建议模拟框架 类,因为它很脆弱。相反,只需创建您自己的接口来提供签名,并在依赖框架的实际应用程序中使用一个实现。例如:
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() }
}
然后在你的测试中,你可以模拟一个 SignatureProvider
到 return 你想要测试的不同签名字符串:
private lateinit var signatureProvider: SignatureProvider
@Before fun setup() {
signatureProvider = mock(SignatureProvider::class.java)
doReturn(listOf("sigA", "sigB")).`when`(signatureProvider)`.signatures
}
我相信你可以通过这样做来实现:
testPackage() {
final ShadowPackageManager packageManager = shadowOf(RuntimeEnvironment.application.getPackageManager());
packageManager.installPackage(getPackageInfo(com.yourapp));
final PackageInfo packageInfo = packageManager.getInternalMutablePackageInfo(com.yourapp);
packageInfo.signatures = new Signature[1];
packageInfo.signatures[0] = new Signature("your_signature");
}
private static PackageInfo getPackageInfo(String packageName) {
final PackageInfoBuilder builder = PackageInfoBuilder.newBuilder();
builder.setPackageName(packageName);
return builder.build();
}
预期格式是由 Signature#toChars()
生成的格式。
现在,我同意 @kcoppock 的观点,因为使用签名及其不同的表示形式进行操作很麻烦,而且 error-prone.
我通过比较包名和 PackageInfo 的签名在我的 android 应用程序中实现了对不兼容应用程序的验证。我通过以下方式获取有关已安装软件包的信息:
val packages = context.ctx.packageManager.getInstalledPackages(PackageManager.GET_META_DATA or PackageManager.GET_SIGNATURES)
然后比较包名和签名的字符串。我通过这个函数将 PackageInfo.signatures 转换为十六进制:
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)
}
而且效果很好。
现在我想为此功能实现几个单元测试并遇到困难:我如何模拟 PackageInfo.signatures?如何将我的 SHA1 指纹字符串转换为 android.content.pm.Signature?
我不建议模拟框架 类,因为它很脆弱。相反,只需创建您自己的接口来提供签名,并在依赖框架的实际应用程序中使用一个实现。例如:
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() }
}
然后在你的测试中,你可以模拟一个 SignatureProvider
到 return 你想要测试的不同签名字符串:
private lateinit var signatureProvider: SignatureProvider
@Before fun setup() {
signatureProvider = mock(SignatureProvider::class.java)
doReturn(listOf("sigA", "sigB")).`when`(signatureProvider)`.signatures
}
我相信你可以通过这样做来实现:
testPackage() {
final ShadowPackageManager packageManager = shadowOf(RuntimeEnvironment.application.getPackageManager());
packageManager.installPackage(getPackageInfo(com.yourapp));
final PackageInfo packageInfo = packageManager.getInternalMutablePackageInfo(com.yourapp);
packageInfo.signatures = new Signature[1];
packageInfo.signatures[0] = new Signature("your_signature");
}
private static PackageInfo getPackageInfo(String packageName) {
final PackageInfoBuilder builder = PackageInfoBuilder.newBuilder();
builder.setPackageName(packageName);
return builder.build();
}
预期格式是由 Signature#toChars()
生成的格式。
现在,我同意 @kcoppock 的观点,因为使用签名及其不同的表示形式进行操作很麻烦,而且 error-prone.