Android

安卓实现静默安装

2021-11-27  本文已影响0人  我是少年520
在做医院项目的时候,遇到了医院病人床头屏app需要静默安装的需求。静默安装就是被安装的app在升级或者安装的时候不需要用户手动点击确认安装,让app自动在后台安装或者升级成功。

要想实现app静默安装我们需要下面4个步骤:

1.首先我们需要在配置清单文件里面添加INSTALL_PACKAGE权限
 <uses-permission android:name="android.permission.INSTALL_PACKAGES" />  
2.把该应用的uid设置为系统级别的,在manifest标签下添加以下属性
 android:sharedUserId="android.uid.system"
3.仅仅设置uid,还是没法实现静默安装,因为系统并不认为你这个app是系统级别的应用,所以,还应该对该应用的APK进行系统签名(注意:不是那个静默安装的APK,是这个实现静默安装程序的APK)。签名过程如下:

总共需要三个文件:

4.静默安装代码
/**
     * 静默安装
     * 会依次调用Stream-->反射-->Shell
     *
     * @param apkFile APK文件
     * @return 成功或失败
     */
    @SuppressLint("PackageManagerGetSignatures")
    @RequiresPermission(Manifest.permission.INSTALL_PACKAGES)
    public static synchronized boolean install(Context context, String apkFile) throws InterruptedException {
        File file;
        if (TextUtils.isEmpty(apkFile) || !(file = new File(apkFile)).exists())
            return false;
        context = context.getApplicationContext();
        //加上apk合法性判断
        AppUtils.AppInfo apkInfo = AppUtils.getApkInfo(file);
        if (apkInfo == null || TextUtils.isEmpty(apkInfo.getPackageName())) {
            LogUtils.iTag(TAG, "apk info is null, the file maybe damaged: " + file.getAbsolutePath());
            return false;
        }

        //加上本地apk版本判断
        AppUtils.AppInfo appInfo = AppUtils.getAppInfo(apkInfo.getPackageName());
        if (appInfo != null) {

            //已安装的版本比apk版本要高, 则不需要安装
            if (appInfo.getVersionCode() >= apkInfo.getVersionCode()) {
                LogUtils.iTag(TAG, "The latest version has been installed locally: " + file.getAbsolutePath(),
                        "app info: packageName: " + appInfo.getPackageName() + "; app name: " + appInfo.getName(),
                        "apk version code: " + apkInfo.getVersionCode(),
                        "app version code: " + appInfo.getVersionCode());
                return true;
            }

            //已安装的版本比apk要低, 则需要进一步校验签名和ShellUID

            PackageManager pm = context.getPackageManager();
            try {
                PackageInfo appPackageInfo, apkPackageInfo;
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
                    appPackageInfo = pm.getPackageInfo(appInfo.getPackageName(), PackageManager.GET_SIGNING_CERTIFICATES);
                    apkPackageInfo = pm.getPackageArchiveInfo(file.getAbsolutePath(), PackageManager.GET_SIGNING_CERTIFICATES);
                } else {
                    appPackageInfo = pm.getPackageInfo(appInfo.getPackageName(), PackageManager.GET_SIGNATURES);
                    apkPackageInfo = pm.getPackageArchiveInfo(file.getAbsolutePath(), PackageManager.GET_SIGNATURES);
                }

                if (appPackageInfo != null && apkPackageInfo != null &&
                        !compareSharedUserId(appPackageInfo.sharedUserId, apkPackageInfo.sharedUserId)) {
                    LogUtils.wTag(TAG, "Apk sharedUserId is not match",
                            "app shellUid: " + appPackageInfo.sharedUserId,
                            "apk shellUid: " + apkPackageInfo.sharedUserId);
                    return false;
                }

            } catch (Throwable ignored) {

            }


        }
        //        try {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            //由于调用PackageInstaller安装失败的情况下, 重复安装会导致内存占用无限增长的问题.
            //所以在安装之前需要判断当前包名是否有过失败记录, 如果以前有过失败记录, 则不能再使用该方法进行安装
            if (sPreferences == null) {
                sPreferences = context.getSharedPreferences(SP_NAME_PACKAGE_INSTALL_RESULT, Context.MODE_PRIVATE);
            }
            String packageName = apkInfo.getPackageName();
            boolean canInstall = sPreferences.getBoolean(packageName, true);
            if (canInstall) {
                boolean success = installByPackageInstaller(context, file, apkInfo);
                sPreferences.edit().putBoolean(packageName, success).apply();
                if (success) {
                    LogUtils.iTag(TAG, "Install Success[PackageInstaller]: " + file.getAbsolutePath());
                    return true;
                }
            }
        }

        if (installByReflect(context, file)) {
            if (sPreferences != null)
                sPreferences.edit().putBoolean(apkInfo.getPackageName(), true).apply();
            LogUtils.iTag(TAG, "Install Success[Reflect]", file.getPath());
            return true;
        }

        if (installByShell(file, DeviceUtils.isDeviceRooted())) {
            if (sPreferences != null)
                sPreferences.edit().putBoolean(apkInfo.getPackageName(), true).apply();
            LogUtils.iTag(TAG, "Install Success[Shell]", file.getPath());
            return true;
        }
        //        } catch (InterruptedException e) {
        //            throw e;
        //        } catch (Throwable e) {
        //            e.printStackTrace();
        //            LogUtils.wTag(TAG, e);
        //        }
        LogUtils.iTag(TAG, "Install Failure: " + file.getAbsolutePath());
        return false;
    }
附上Demo地址
上一篇下一篇

猜你喜欢

热点阅读