對安裝的apk進行校驗,除了系統應用市場中下載的,其它渠道的apk都進行安裝攔截,並且彈框提示。 首先需要把驗證的證書保存在資料庫本地,後面需要用到 然後註冊系統廣播,用於接收 apk 安裝時的監聽,這個廣播由系統發出 新裝時的 action ‘android.intent.action.PACKA ...
對安裝的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
<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 安裝接收到廣播後的校驗處理
<service android:name="com.ecarx.verifier.main.VerifyService" android:exported="true"> <intent-filter> <action android:name="ecarx.install.verify" /> </intent-filter> </service>
接下來就是監聽跟驗證,首先在安裝apk時會收到廣播,然後可以拿到信息開啟服務驗證
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 中
@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); } }); }
然後處理具體的驗證邏輯
@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; } } } }
具體驗證方法
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); }