Android6.0关于预置三方app卸载(一) copy到data/app下

我们知道每个手机会预装一些三方的应用,但我们需要用户把这些应用删除的权限。这可怎么操作呢,我们在编译的时候可以把应用放在data/app下,但是这样的话,手机一恢复出厂设置预留应用就没有了。这篇博客我们就来解决这个问题。


一、安装预置应用方法

这里提供一个思路,把预留应用放在system/third-app下,然后在第一次开机的时候把这些应用copy到data/app下,当然要在PKMS扫描data/app之前,这样第一次开机的时候就能安装上这些应用了。因为应用在data/app下也能删除。因为应用在system/third-app中也有,而恢复出厂设置的时候system的目录不会清空。在恢复出厂设置后,还会把这些应用copy到data/app下(恢复出厂设置开机等于第一次开机),下面我们看下如何实现。

先看下apk的编译时放在了third_app下,

include $(CLEAR_VARS)
LOCAL_MODULE_PATH := $(TARGET_OUT)/third_app
LOCAL_MODULE := MobileMusic
LOCAL_MODULE_TAGS := optional
LOCAL_MODULE_CLASS := APPS
LOCAL_SRC_FILES := $(LOCAL_MODULE).apk
LOCAL_CERTIFICATE := PRESIGNED
include $(BUILD_PREBUILT)

功能如下,就是把system/third-app下面的apk复制到data/app, 但是在哪里调用这个函数呢?

    //install the third apps when system is first boot
    private void installThirdApps(){
        //the source directory not exists
        File storeDir = new File("/system/third_app");
        if(!storeDir.exists()){
            Log.e(TAG,"/system/third_app is not exist");
            return;
        }

        //get the apk files in /system/third_app
        String apkFilesNames[] = storeDir.list();
        if(apkFilesNames == null){
            Log.e(TAG,"apk file name is null");
            return;
        }

        //copy the apk files to /data/app
        boolean installSucc = false;
        for(int i = 0; i < apkFilesNames.length; i++){
            //Uri srcFileUri = Uri.parse(storeDir+"/"+apkFilesNames[i]);
            File srcFile = new File("/system/third_app",apkFilesNames[i]);
            Log.e(TAG,"srcFile="+srcFile);
            File destFile = new File("/data/app",apkFilesNames[i]);
            Log.e(TAG,"destFile="+destFile.toString());
            boolean installResult = copyThirdApps(srcFile,destFile);
            if(!installResult){
                Log.d(TAG,"install failed");
                return;
            }
        }
    }

    /**
     * File copy function.
     * It will be used when installThirdApps
     * @param srcFile  just like '/system/third_app/***.apk'
     * @param destDir  just like '/data/app/***.apk'
     * @return
     */
    private boolean copyThirdApps(File srcFile, File destDir) {
        //do some check actions
        if (srcFile == null || destDir == null || !srcFile.exists()) {
            Log.e(TAG, "invalid arguments for movePreinstallApkFile()");
            Log.e(TAG, "move " + srcFile + " to " + destDir + " failed");
            return false;
        }

        //create new file
        try{
            destDir.createNewFile();
        }catch(Exception e){
            Log.e(TAG, "create file faild! due to:" + e);
            return false;
        }

        //set permission
        try{
            Runtime.getRuntime().exec("chmod 644 "+destDir.getAbsolutePath());
        }catch(Exception e){
            Log.e(TAG, "chmod file faild! due to:"+e);
        }

        //do copy
        try{
            boolean ret = FileUtils.copyFile(srcFile,destDir);
            if(!ret){
                Log.e(TAG,"copy file faild!");
                return false;
            }
        }catch(Exception e){
            Log.e(TAG, "copy file faild! due to:"+e);
        }

        return true && destDir.exists();
    }

在PKMS的构造函数,开始处理非系统应用的时候,但是一定要在扫描data/app之前,这样才能后面扫描到data/app这些复制进去的app,才会第一次开机安装成功。

            if (!mOnlyCore) {
                EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
                        SystemClock.uptimeMillis());
                if(isFirstBoot()){//判断第一次开机
                    Log.i(TAG, "It's first boot, install the third apps");
                    installThirdApps();//安装三方应用(copy到data/app下)
                }                        
                scanDirLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);

                scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
                        scanFlags | SCAN_REQUIRE_KNOWN, 0);


二、adb install处理

下面几个问题,我们知道PKMS会在开机的时候扫描data/app,所以一般我们把一个apk push到data/app下的时候,再把手机重启也会安装应用。但是一定要重启。


那adb install又是如何做到安装应用的,我们来看下代码,下面这段代码是pc侧的

    else if (!strcmp(argv[0], "install")) {
        if (argc < 2) return usage();
        return install_app(ttype, serial, argc, argv);
    }

我们来看下install_app这个函数,也是把apk文件push到/data/local/tmp下,然后调用了pm命令安装应用。

static int install_app(transport_type transport, const char* serial, int argc,
                       const char** argv)
{
    static const char *const DATA_DEST = "/data/local/tmp/%s";
    static const char *const SD_DEST = "/sdcard/tmp/%s";
    const char* where = DATA_DEST;
    int i;
    struct stat sb;

    for (i = 1; i < argc; i++) {
        if (!strcmp(argv[i], "-s")) {
            where = SD_DEST;
        }
    }

    // Find last APK argument.
    // All other arguments passed through verbatim.
    int last_apk = -1;
    for (i = argc - 1; i >= 0; i--) {
        const char* file = argv[i];
        char* dot = strrchr(file, '.');
        if (dot && !strcasecmp(dot, ".apk")) {
            if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {
                fprintf(stderr, "Invalid APK file: %s\n", file);
                return -1;
            }

            last_apk = i;
            break;
        }
    }

    if (last_apk == -1) {
        fprintf(stderr, "Missing APK file\n");
        return -1;
    }

    const char* apk_file = argv[last_apk];
    char apk_dest[PATH_MAX];
    snprintf(apk_dest, sizeof apk_dest, where, get_basename(apk_file));
    int err = do_sync_push(apk_file, apk_dest, 0 /* no show progress */);//把apk文件push到/data/local/tmp/目录下
    if (err) {
        goto cleanup_apk;
    } else {
        argv[last_apk] = apk_dest; /* destination name, not source location */
    }

    err = pm_command(transport, serial, argc, argv);//调用了pm命令

cleanup_apk:
    delete_file(transport, serial, apk_dest);
    return err;
}

看下pm_command函数,是调用了pm install命令来安装应用。

static int pm_command(transport_type transport, const char* serial,
                      int argc, const char** argv)
{
    std::string cmd = "shell:pm";

    while (argc-- > 0) {
        cmd += " " + escape_arg(*argv++);
    }

    return send_shell_command(transport, serial, cmd);
}
我们来看下pm install的处理,调用了runInstall函数
        if ("install".equals(op)) {
            return runInstall();
        }
在runInstall函数会调用PKMS的installPackageAsUser函数安装应用
            VerificationParams verificationParams = new VerificationParams(verificationURI,
                    originatingURI, referrerURI, VerificationParams.NO_UID, null);

            mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags,
                    installerPackageName, verificationParams, abi, userId);


三、注意

上面PKMS中把system/third_app下面的apk放在data/app有点问题,原因是android6.0 编译apk文件,会自动生成一个目录,所以代码需要修改一下。

主要是下面这个函数,system/third_app目录下多了一层目录。

    private void installThirdApps(){
        //the source directory not exists
        File storeDir = new File("/system/third_app");
        if(!storeDir.exists()){
            Log.e(TAG,"/system/third_app is not exist");
            return;
        }

        //get the apk files in /system/third_app
        String apkDirFilesNames[] = storeDir.list();
        if(apkDirFilesNames == null){
            Log.e(TAG,"apk file name is null");
            return;
        }

        //copy the apk files to /data/app
        boolean installSucc = false;
        for(int i = 0; i < apkDirFilesNames.length; i++){
            File srcFileDir = new File("/system/third_app", apkDirFilesNames[i]);
            Log.e(TAG,"srcFile=" + srcFileDir);
            String srcFileNames[] = srcFileDir.list();
            for(int j = 0; j < srcFileNames.length; j++) {
                File srcFile = new File(srcFileDir, srcFileNames[j]);
                File destFile = new File("/data/app", srcFileNames[j]);
                boolean installResult = copyThirdApps(srcFile, destFile);
                if(!installResult){
                    Log.d(TAG,"install failed");
                    return;
                }
            }
        }
    }





你可能感兴趣的:(android,PackageManager)