yinsui 2012-10-17
某些时候需要获取已安装的apk或者是未安装的apk的签名信息,以下代码片段将会很有用。
1.通过app的packageName获取已安装的apk的签名信息
public Signature getPackageSignature(Context context, String packageName){ PackageManager pm = context.getPackageManager(); List<PackageInfo> apps = pm.getInstalledPackages(PackageManager.GET_SIGNATURES); Iterator<PackageInfo> it = apps.iterator(); while(it.hasNext()){ PackageInfo info = it.next(); if(info.packageName.equals(packageName)){ return info.signatures[0]; } } return null; }
2.根据文件路径获取未安装的apk的签名信息
由于android平台本身的一个小bug,使用PackageManager方式获取未安装的apk文件的签名会稍微费事一点。
(android平台的这个小bug有些狗血,是因为PackageManager的的getPackageArchiveInfo中少了如下代码的原因导致)
缺少的代码:
if ((flags & GET_SIGNATURES) != 0) packageParser.collectCertificates(pkg, 0);
问题的详细信息可以查看以下链接:
https://code.google.com/p/android/issues/detail?id=9151#c8
https://android-review.googlesource.com/#/c/18769/1/core/java/android/content/pm/PackageManager.java
这样一来,获取apk文件的签名方式将会变得稍微啰嗦一点
(1)首先自定义一个getPackageArchiveInfo函数,如下:
@SuppressWarnings("unchecked") public PackageInfo getPackageArchiveInfo(String archiveFilePath, int flags){ // Workaround for https://code.google.com/p/android/issues/detail?id=9151#c8 try{ Class packageParserClass = Class.forName( "android.content.pm.PackageParser"); Class[] innerClasses = packageParserClass.getDeclaredClasses(); Class packageParserPackageClass = null; for (Class innerClass : innerClasses){ if (0 == innerClass.getName().compareTo("android.content.pm.PackageParser$Package")){ packageParserPackageClass = innerClass; break; } } Constructor packageParserConstructor = packageParserClass.getConstructor( String.class); Method parsePackageMethod = packageParserClass.getDeclaredMethod( "parsePackage", File.class, String.class, DisplayMetrics.class, int.class); Method collectCertificatesMethod = packageParserClass.getDeclaredMethod( "collectCertificates", packageParserPackageClass, int.class); Method generatePackageInfoMethod = packageParserClass.getDeclaredMethod( "generatePackageInfo", packageParserPackageClass, int[].class, int.class, long.class, long.class); packageParserConstructor.setAccessible(true); parsePackageMethod.setAccessible(true); collectCertificatesMethod.setAccessible(true); generatePackageInfoMethod.setAccessible(true); Object packageParser = packageParserConstructor.newInstance(archiveFilePath); DisplayMetrics metrics = new DisplayMetrics(); metrics.setToDefaults(); final File sourceFile = new File(archiveFilePath); Object pkg = parsePackageMethod.invoke( packageParser, sourceFile, archiveFilePath, metrics, 0); if (pkg == null){ return null; } if ((flags & android.content.pm.PackageManager.GET_SIGNATURES) != 0){ collectCertificatesMethod.invoke(packageParser, pkg, 0); } return (PackageInfo)generatePackageInfoMethod.invoke(null, pkg, null, flags, 0, 0); } catch (Exception e) { Log.e("Signature Monitor", "android.content.pm.PackageParser reflection failed: " + e.toString()); } return null; }
(2)使用自定义的getPackageArchiveInfo函数获取PackageInfo,从而获取签名信息,如下:
/** * * @param context * @param apkFile 文件的全路径信息(包括apk文件的名称),如果是无效的apk文件,返回值为null * @return */ public Signature getApkSignatureByFilePath(Context context, String apkFile){ PackageInfo newInfo = getPackageArchiveInfo(apkFile, PackageManager.GET_ACTIVITIES | PackageManager.GET_SIGNATURES); if(newInfo != null){ if(newInfo.signatures != null && newInfo.signatures.length >0){ return newInfo.signatures[0]; } } return null; }
adb shell cd system/app rm *.apk21. 获取管理员权限: adb root22. 启动Activity: adb shell am start -n 包名/包名+类名。