对安装的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;        }    }

服务VerifyServiceextends 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);    }