对安装的apk进行校验,除了系统应用市场中下载的,其它渠道的apk都进行安装拦截,并且弹框提示。
首先需要把验证的证书保存在数据库本地,后面需要用到
然后注册系统广播,用于接收 apk 安装时的监听,这个广播由系统发出
新装时的
action 'android.intent.action.PACKAGE_ADDED
替换时的 action
android.intent.action.PACKAGE_REMOVED
android.intent.action.PACKAGE_ADDED
android.intent.action.PACKAGE_REPLACED
删除时的 action
android.intent.action.PACKAGE_REMOVED
android.intent.action.PACKAGE_FULLY_REMOVED
java
<receiver android:name\="com.ecarx.verifier.main.VerifyReceiver"
android:exported\="true"\>
<intent-filter\>
<action android:name\="android.intent.action.PACKAGE\_NEEDS\_VERIFICATION" />
<data android:mimeType\="application/vnd.android.package-archive" />
</intent-filter\>
</receiver\>
安卓 4.0 新增了 verifyPendingInstall,用于监听包管理,验证时发送广播
验证通过 PackageManager.VERIFICATION_ALLOW 跟验证失败 PackageManager.VERIFICATION_REJECT
在注册一个服务,用于 apk 安装接收到广播后的校验处理
java
<service android:name\="com.ecarx.verifier.main.VerifyService"
android:exported\="true"\>
<intent-filter\>
<action android:name\="ecarx.install.verify" />
</intent-filter\>
</service\>
接下来就是监听跟验证,首先在安装apk时会收到广播,然后可以拿到信息开启服务验证
java
public void onReceive(Context context, Intent intent) {
Log.e(TAG, "onReceive: start");
String action \= intent.getAction();
if (Intent.ACTION\_PACKAGE\_NEEDS\_VERIFICATION.equals(action)) {
Log.e(TAG, "onReceive: " + "ACTION\_PACKAGE\_NEEDS\_VERIFICATION");
Intent verification \= new Intent();
verification.setAction(VerifyConstant.VERIFY\_SERVICE\_ACTION);
verification.setPackage(VerifyConstant.VERIFY\_SERVICE\_PACKAGE\_NAME);
Bundle sExtras \= new Bundle(intent.getExtras());
sExtras.putInt(VerifyManager.Cmd.CMD\_KEY, VerifyManager.Cmd.CMD\_VERIFY);
String path \= intent.getData().getPath();
Log.e(TAG, "path: " + path);
if (!path.endsWith(VerifyConstant.INSTALL\_FILE\_SUFFIX)) {
path \= path + "/base.apk";
}
sExtras.putString("PACKAGE\_PATH", path);
verification.putExtras(sExtras);
if (Build.VERSION.SDK\_INT >= Build.VERSION\_CODES.O) {
AppPlugins.appCtx().startForegroundService(verification);
} else {
AppPlugins.appCtx().startService(verification);
}
return;
}
if (Intent.ACTION\_PACKAGE\_VERIFIED.equals(action)) {
Log.d(TAG, "onReceive: " + "ACTION\_PACKAGE\_VERIFIED");
return;
}
}
服务 VerifyService extends IntentService
系统校验时间默认为10秒超时,如果下载的是大型游戏可能会anr,所以需要默认改下超时时间
并且在服务中需要弹框处理,这两个操作放到 onCreate 中
java
@Override
public void onCreate() {
super.onCreate();
Log.d(TAG, "onCreate");
Settings.Global.putLong(getContentResolver(), PACKAGE\_VERIFIER\_TIMEOUT, MAX\_VERIFICATION\_TIMEOUT);
VerifyUtils.setVerifyInstallListener(isAllow \-> {
Log.d(TAG, "setVerifyInstallListener isAllow : " + isAllow);
if (!isAllow) {
ArchTaskExecutor.getInstance().postToMainThread(this::showInstallTipDialog);
}
});
}
然后处理具体的验证逻辑
java
@Override
protected void onHandleIntent(@Nullable Intent intent) {
if (intent == null) return;
if (intent.getExtras() == null) return;
int cmd = intent.getIntExtra(VerifyManager.Cmd.CMD\_KEY, 0);
switch (cmd) {
case VerifyManager.Cmd.CMD\_VERIFY: {
Bundle extras \= intent.getExtras();
int id = extras.getInt(PackageManager.EXTRA\_VERIFICATION\_ID);
String pkgName \= extras.getString(EXTRA\_VERIFICATION\_PACKAGE\_NAME);
String packagePath \= extras.getString("PACKAGE\_PATH", null);
String installerPkg \= extras.getString(EXTAR\_VERIFICATION\_INSTALLER\_PKG);
PackageManager pm \= this.getPackageManager();
boolean isSystemApp = VerifyUtils.isSystemApp(pkgName);
Log.e(TAG, "isSystemApp " + isSystemApp);
if (isSystemApp) {
pm.verifyPendingInstall(id, PackageManager.VERIFICATION\_ALLOW);
if (DEBUG) {
Log.e(TAG, pkgName \+ " : System App ALLOW");
}
return;
}
boolean isOverlayApp = VerifyUtils.isOverlayApp(packagePath);
Log.e(TAG, "isOverlayApp " + isOverlayApp);
if (isOverlayApp) {
pm.verifyPendingInstall(id, PackageManager.VERIFICATION\_ALLOW);
if (DEBUG) {
Log.e(TAG, pkgName \+ " : Overlay App ALLOW");
}
return;
}
Log.e(TAG, "installerPkg " + installerPkg);
if (DEVICESERVICE\_PACKAGE\_NAME.equals(installerPkg) && VerifyUtils.isSystemPackage(DEVICESERVICE\_PACKAGE\_NAME)) {
pm.verifyPendingInstall(id, PackageManager.VERIFICATION\_ALLOW);
if (DEBUG) {
Log.e(TAG, pkgName \+ " : Debugtool install App ALLOW");
}
return;
}
String sha1 \= getMd5VerifyResult(packagePath);
boolean isLegal = VerifyUtils.isLegal(sha1, pkgName);
Log.e(TAG, "isLegal " + isLegal);
if (isLegal) {
pm.verifyPendingInstall(id, PackageManager.VERIFICATION\_ALLOW);
if (DEBUG) {
Log.e(TAG, pkgName \+ " : Legal App ALLOW");
}
return;
}
VerifyUtils.verifyOnlineSign(id, packagePath, pkgName, sha1);
return;
}
}
}
}
具体验证方法
java
public static void verifyOnlineResp(int id, String localSha1, String pkgName, SignatureResp signatureResp) {
String signText \= signatureResp.getSign();
int signType = signatureResp.getSignType();
String sha1 \= signatureResp.getApkSign();
String content \= generateContent(signType, pkgName, sha1);
boolean verifyResult = VerifyUtils.verifySign(content, signText, publicKeyStr);
if (verifyResult) {
SignEntity signEntity \= new SignEntity(pkgName, sha1, content, signType, signText);
AppDatabase.getInstance().signDao().insertSignSync(signEntity);
}
reportVerifyResult(id, pkgName, verifyResult);
}
public static void reportVerifyResult(int id, String pkgName, boolean isAllow) {
int resultCode = isAllow ? PackageManager.VERIFICATION\_ALLOW : PackageManager.VERIFICATION\_REJECT;
String resultStr \= isAllow ? "Allow" : "Reject";
AppPlugins.appCtx().getPackageManager().**verifyPendingInstall**(id, resultCode);
if (listener != null) {
listener.verifyResult(isAllow);
}
Log.e(TAG, pkgName \+ " : " + resultStr);
}
👀关注公众号:Android老皮!!!欢迎大家来找我探讨交流👀