Android完全禁止第三方软件安装的方法

这段时间在给公司的产品做CTA认证,公司的产品声明不允许第三方软件安装,所以需要禁止掉APK的安装功能。一开始我把Packageinstaller.apk从系统里面删了,试了一下,放一个APK到SD卡,点击安装,确实安装不了,我以为就成功了,心想还挺简单么!!

不过,CTA认证实验室说,不行,还是可以安装第三方软件!仔细问了一下,他们是通过PC端类似于“手机助手”的软件安装上去的。自己赶紧试了一下,确实可以安装,又试了一下adb install命令,也可以安装!!

得,还是问问软件开发的同事吧,给个patch也行呀,谁知道人家太忙,没时间搞,但是这事情分给我干了,还是硬着头皮搞吧。不过,对于一个搞驱动的,碰到这种应用的问题,半天都没思路呀!!不过,功夫不负有心人,百度了一天,终于搞定了。

啰嗦完了,说正事!!

通过网上大神们对于Android系统APK安装过程的分析,系统安装APK主要的源码在文件“frameworks\base\services\core\java\com\android\server\pm\PackageManagerService.java”里,顺便说一下,我用的是Android 5.0系统。

Android系统安装APK有4种方式:
1. 开机时系统应用的安装;
2. 手机APP下载应用安装;
3. ADB命令安装;
4. 点击APK文件安装;(我之前删除的Packageinstaller.apk就是负责这种安装)

第1种安装方式函数调用结构大致如下:

PackageManagerService
->scanDirLI
–>scanPackageLI(File scanFile, …)
—>(省略)
—->mInstaller.install

剩下3种安装方式,虽然开始是从不同接口进入,但是最后统一的调用如下:

installPackage
->scanPackageLI(PackageParser.Package pkg, …)
–>scanPackageDirtyLI
—>createDataDirsLI
—->mInstaller.install

由于不是很懂JAVA语法规则,所以,一开始我以为,第1个“scanPackageLI”函数应该不会像下面的“scanPackageLI”函数一样调用“createDataDirsLI”函数,所以我做如下修改“createDataDirsLI”函数:

    private int createDataDirsLI(String packageName, int uid, String seinfo) {
        int[] users = sUserManager.getUserIds();

        //直接返回失败,禁止安装APK
        return PackageManager.INSTALL_FAILED_INVALID_APK;

        /*
        int res = mInstaller.install(packageName, uid, uid, seinfo);
        if (res < 0) {
            return res;
        }
        for (int user : users) {
            if (user != 0) {
                res = mInstaller.createUserData(packageName,
                        UserHandle.getUid(user, uid), user, seinfo);
                if (res < 0) {
                    return res;
                }
            }
        }
        return res;
        */
    }

编译运行之后发现,系统卡在开机动画那里不动了!!

所以,我估计第1种安装方式的函数“scanPackageLI”一样调用了“createDataDirsLI”函数,这个时候就麻烦了,系统APK的安装和其他方式APK安装调用一样的函数!!

然后又去百度,看了大神的关于Android系统启动时APK安装过程的分析后,想到一个办法,定义一个标志位,用来标记系统启动时APK是否安装完成,当系统APK安装没有完成时,“createDataDirsLI”函数正常工作,当系统启动完成,让“createDataDirsLI”函数失效。

系统启动时安装APK调用的函数为上面第1种安装方式的“PackageManagerService”,然后调用“scanDirLI”函数完成安装APK,安装完成后,“调用updatePermissionsLPw来申请为了特定的资源访问权限的apk分配相应的linux用户组ID”,这是文章原话。

所以先定义一个标志位:private static boolean APK_install_finish = false;
初始化为“false”代表系统APK安装还没有完成。

找到“PackageManagerService”函数内“updatePermissionsLPw”函数的调用处,发现只有一处。
在“updatePermissionsLPw”函数调用之前,“scanDirLI”函数调用之后,改变“APK_install_finish ”变量的值,表示系统APK安装完成,代码如下:

    ...

    Slog.i(TAG, "Time to scan packages: "
            + ((SystemClock.uptimeMillis()-startTime)/1000f)
            + " seconds");

    //系统APK安装完成
    APK_install_finish = true;

    // If the platform SDK has changed since the last time we booted,
    // we need to re-grant app permission to catch any new ones that
    // appear.  This is really a hack, and means that apps can in some
    // cases get permissions that the user didn't initially explicitly
    // allow...  it would be nice to have some better way to handle
    // this situation.
    final boolean regrantPermissions = mSettings.mInternalSdkPlatform
            != mSdkVersion;
    if (regrantPermissions) Slog.i(TAG, "Platform changed from "
            + mSettings.mInternalSdkPlatform + " to " + mSdkVersion
            + "; regranting permissions for internal storage");
    mSettings.mInternalSdkPlatform = mSdkVersion;

    updatePermissionsLPw(null, null, UPDATE_PERMISSIONS_ALL
            | (regrantPermissions
                    ? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL)
                    : 0));

    ...

然后重新修改“createDataDirsLI”函数:

    private int createDataDirsLI(String packageName, int uid, String seinfo) {
        int[] users = sUserManager.getUserIds();

        //如果系统APK安装完成,则禁止安装任何APK
        if(APK_install_finish)
        {
            return PackageManager.INSTALL_FAILED_INVALID_APK;
        }

        int res = mInstaller.install(packageName, uid, uid, seinfo);
        if (res < 0) {
            return res;
        }
        for (int user : users) {
            if (user != 0) {
                res = mInstaller.createUserData(packageName,
                        UserHandle.getUid(user, uid), user, seinfo);
                if (res < 0) {
                    return res;
                }
            }
        }
        return res;
    }

只有当系统APK安装完成之后,才让“createDataDirsLI”函数失效。

编译下载运行,可以正常开机,开机之后,先使用ADB安装APK,失败;然后点击APK装包,安装失败;再使用PC端“手机助手”安装APK,依然失败!!

至此,大功告成~

相关参考文章链接:
http://blog.csdn.net/hdhd588/article/details/6739281
https://www.2cto.com/kf/201607/528367.html
http://blog.csdn.net/dkbdkbdkb/article/details/45313911

你可能感兴趣的:(技术随笔)