精华内容
下载资源
问答
  • 本文基于OKGO3.0.4版本编写 首先导入依赖 ...一.okgo跳过https验证 1.导入自定义SSLSocketClient类 public class SSLSocketClient { //获取这个SSLSocketFactory public static SSLSocketF...

    本文基于OKGO3.0.4版本编写

    首先导入依赖

     implementation 'com.lzy.net:okgo:3.0.4'

    一.okgo跳过https验证

    1.导入自定义SSLSocketClient类

    public class SSLSocketClient {
    
        //获取这个SSLSocketFactory
        public static SSLSocketFactory getSSLSocketFactory() {
            try {
                SSLContext sslContext = SSLContext.getInstance("SSL");
                sslContext.init(null, getTrustManager(), new SecureRandom());
                return sslContext.getSocketFactory();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    
        //获取TrustManager
        private static TrustManager[] getTrustManager() {
            TrustManager[] trustAllCerts = new TrustManager[]{
                    new X509TrustManager() {
                        @Override
                        public void checkClientTrusted(X509Certificate[] chain, String authType) {
                        }
    
                        @Override
                        public void checkServerTrusted(X509Certificate[] chain, String authType) {
                        }
    
                        @Override
                        public X509Certificate[] getAcceptedIssuers() {
                            return new X509Certificate[]{};
                        }
                    }
            };
            return trustAllCerts;
        }
    
        //获取HostnameVerifier
        public static HostnameVerifier getHostnameVerifier() {
            HostnameVerifier hostnameVerifier = new HostnameVerifier() {
                @Override
                public boolean verify(String s, SSLSession sslSession) {
                    return true;
                }
            };
            return hostnameVerifier;
        }
    }
    

    2.在Application里配置

     OkHttpClient  build = new OkHttpClient.Builder()
                    .sslSocketFactory(SSLSocketClient.getSSLSocketFactory())
                    .hostnameVerifier(SSLSocketClient.getHostnameVerifier())
                    .build();
              //初始化okgo并配置给httpclient
            OkGo.getInstance().init(this).setOkHttpClient(build);

    至此https配置完毕了就

    接下来就去请求文件吧  

    二:OKGO下载文件(本文下载的是一个apk文件)

     final String destfiledir = Environment.getExternalStorageDirectory().getPath() + "/OAdownload";//下载路径
                 final    String destfilename = "OA.apk";//文件名
                    
                            OkGo
    .tag("Download tips")//添加标记  用于取消单个请求    取消: OkGo.getInstance().cancelTag("Download tips");
                            .<File>get("http://immobile.r93535.com:8086/LN/GetExtApp/extapp/ExtApp/A/1111/LatestVersion")
                            .execute(new FileCallback(destfiledir, destfilename) {
                                @Override
                                public void onStart(Request<File, ? extends Request> request) {
                                    super.onStart(request);
                                    Log.e("gogogo", "正在加载中");
                                }
    
                                @Override
                                public void downloadProgress(Progress progress) {
                                    super.downloadProgress(progress);
                                    long totalSize = progress.totalSize;//总长度
                                    long currentSize = progress.currentSize;//本次下载的大小
                                    //进度条
                                    progressBar.setMax((int) totalSize);
                                    progressBar.setProgress((int) currentSize);
                                   
    
                                    textView.setText(String.valueOf(progress.fraction * 100 + "%"));  //下载进度
                                }
    
                                @Override
                                public void onError(Response<File> response) {
                                    super.onError(response);
                                    Log.e("gogogo", "下载出错");
                                   
                                }
    
                                @Override
                                public void onSuccess(Response<File> response) {
                                    Log.e("gogogo", "下载成功");
                                      // 安装apk
                                    install(destfiledir + destfilename);
                                }
    
                                @Override
                                public void onFinish() {
                                    super.onFinish();
                                    Log.e("gogogo", "结束");
    
                                }
                            });

    三:安装apk

    开始安装apk:

     File apkFile = new File(filePath);
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
     startActivity(intent);

    注意:在android 7.0之前这样写没有一点瑕疵,但是随着Android 7.0的到来引入“私有目录被限制访问”,“StrictMode API 政策”,为了进一步提高私有文件的安全性,Android不再由开发者放宽私有文件的访问权限,之前我们一直使用”file:///”绝对路径来传递文件地址的方式,在接收方访问时很容易触发SecurityException的异常。

    因此,为了更好的适配Android 7.0,例如相机拍照这类涉及到文件地址传递的地方就用上了FileProvider,FileProvider也更好地进入了大家的视野。

    不多bb,看代码!

    1.首先在manifest里面配置provider(跟activity同级)

     <provider
                    android:name="android.support.v4.content.FileProvider"
                    android:authorities="你的包名.fileprovider"
                    android:exported="false"
                    android:grantUriPermissions="true">
                    <meta-data
                        android:name="android.support.FILE_PROVIDER_PATHS"
                        android:resource="@xml/file_paths" />
                </provider>

    2.在res文件下建立xml文件夹然后建立file_paths文件

    <resources>
        <paths>
            <external-path
                name="OA.apk"
                path="/OAdownload" />
        </paths>
    </resources>
    

    path就是文件路径,那么是文件名字  然后external-path就相当于Environment.getExternalStorageDirectory() 。 

    别的路径可以参考下图:

    files-pathContext.getFilesDir()
    cache-pathContext.getCacheDir()
    external-pathEnvironment.getExternalStorageDirectory()
    external-files-pathContext.getExternalFilesDir(null)
    external-cache-pathContext.getExternalCacheDir()

    3.开始安装

     private void install(String filePath) {
            Log.i("file", "开始执行安装: " + filePath);
            File apkFile = new File(filePath);
            Intent intent = new Intent(Intent.ACTION_VIEW);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                Log.w("file", "版本大于 N ,开始使用 fileProvider 进行安装");
                intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                Uri contentUri = FileProvider.getUriForFile(
                        this
                        , "com.example.duqianlong.okdownladdemo.fileprovider"
                        , apkFile);
                intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
            } else {
                Log.w("file", "正常进行安装");
                intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
            }
            startActivity(intent);
        }

    至此差不多就大功告成了,当然权限是少不了的

    4.添加权限(因为不是危险权限,所以不需要动态申请,添加到manifest里面就行)

      <uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />

    读写权限&网络权限 是少不了的,当然这个就需要动态申请了,这个可以参考以前的博客

     <uses-permission android:name="android.permission.INTERNET" />
        <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    自此全部完毕大功告成,可以测试啦!

    由于动态图太大传不上来,同学们可以去Github下载下来自己测试

     

    展开全文
  • Android7.0 PackageManagerService APK安装

    转载:http://blog.csdn.net/gaugamela/article

    在本篇博客中,我们分析一下Android中的APK是如何安装的,以及PKMS在这个过程中进行了哪些工作。

    APK的安装方式有很多,我们先来看看如何用adb命令进行安装。
    我们从adb install开始分析,该命令有多个参数,这里仅考虑最基本的adb install xxxx.apk。

    一、adb命令
    看看system/core/adb/commandline.cpp中的adb_commandline函数:

    int adb_commandline(int argc, const char **argv) {
        ...........
        else if (!strcmp(argv[0], "install")) {
            if (argc < 2) return usage();
            FeatureSet features;
            std::string error;
            if (!adb_get_feature_set(&features, &error)) {
                fprintf(stderr, "error: %s\n", error.c_str());
                return 1;
            }
    
            if (CanUseFeature(features, kFeatureCmd)) {
                //支持FeatureCmd时调用install_app
                return install_app(transport_type, serial, argc, argv);
            }
            //否则,利用install_app_legacy
            return install_app_legacy(transport_type, serial, argc, argv);
        }
        ...........
    }

    1、install_app_legacy
    我看先看看传统的install_app_legacy:

    static int install_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) {
        //待安装的APK目前还在源机器上,现在需要把APK的文件复制到手机里
        //如果安装在手机内部存储,那么目的地址为DATA_DEST
        //如果安装在SD卡上,则目的地址为SD_DEST
        static const char *const DATA_DEST = "/data/local/tmp/%s";
        static const char *const SD_DEST = "/sdcard/tmp/%s";
        .........
        //默认安装到手机内部
        const char* where = DATA_DEST;
        for (i = 1; i < argc; i++) {
            //携带参数-s时,才安装到SD卡
            if (!strcmp(argv[i], "-s")) {
                where = SD_DEST;
            }
        }
    
        //解析参数,判断adb命令中是否携带了有效的apk文件名
        ...........
    
        //取出apk名
        std::vector<const char*> apk_file = {argv[last_apk]};
        //构造apk目的地址
        std::string apk_dest = android::base::StringPrintf(
                where, adb_basename(argv[last_apk]).c_str());
    
        //do_sync_push将此APK文件传输到手机的目标路径,失败的话将跳转到clenaup_apk
        if (!do_sync_push(apk_file, apk_dest.c_str())) goto cleanup_apk;
    
        //执行pm_command
        result = pm_command(transport, serial, argc, argv);
    
    cleanup_apk:
        //删除刚才传输的文件
        //PKMS在安装过程中会将该APK复制一份到/data/app目录下,所有data/local/tmp目录下对应的文件可以删除
        delete_file(transport, serial, apk_dest);
        return result;
    }

    从代码来看,传统的安装方式就是将源机器中的APK文件拷贝到目的手机的tmp目录下,然后调用pm_command进行处理。

    2、install_app
    我们再看看支持FeatureCmd的机器,如何安装APK:

    static int install_app(TransportType transport, const char* serial, int argc, const char** argv) {
        //利用参数创建出本地文件的名称
        const char* file = argv[argc - 1];
    
        //解析参数,判断adb命令中是否携带了有效的apk文件名
        .........
    
        //adb_open中将创建出这个file对应的文件
        int localFd = adb_open(file, O_RDONLY);
        ............
        std::string cmd = "exec:cmd package";
        //添加cmd参数
        ............
        //连接源端,获取源APK文件的描述符
        int remoteFd = adb_connect(cmd, &error);
        ............
        //将remoteFd中的数据写入到localFd
        copy_to_file(localFd, remoteFd);
        //得到结果
        read_status_line(remoteFd, buf, sizeof(buf));
    
        adb_close(localFd);
        adb_close(remoteFd);
        ..........
        return 0;
    }

    从代码来看install_app就是将源机器的文件复制到了目的机器中,并没有进行额外的操作。猜想可能是支持特殊FeatureCmd的机器,PKMS能够监听到这个拷贝,然后触发后续的扫描工作。这个过程没有研究过对应代码,暂时不做深入分析。

    对于传统的安装方式,我们需要继续往下看看pm_command。

    二、pm_command
    我们先看看pm_command函数:

    static int pm_command(TransportType transport, const char* serial, int argc, const char** argv) {
        std::string cmd = "pm";
    
        //构造pm cmd
        while (argc-- > 0) {
            cmd += " " + escape_arg(*argv++);
        }
    
        //发送shell命令给adbd
        return send_shell_command(transport, serial, cmd, false);
    }

    我们跟进下send_shell_command:

    // Connects to the device "shell" service with |command| and prints the
    // resulting output.
    static int send_shell_command(TransportType transport_type, const char* serial,
                                  const std::string& command,
                                  bool disable_shell_protocol,
                                  std::string* output=nullptr,
                                  std::string* err=nullptr) {
        ...........
        while (true) {
            bool attempt_connection = true;
    
            // Use shell protocol if it's supported and the caller doesn't explicitly disable it.
            if (!disable_shell_protocol) {
                .......
                if (adb_get_feature_set(&features, &error)) {
                    //如果定义了feature,则替换shell protocol
                    use_shell_protocol = CanUseFeature(features, kFeatureShell2);
                } else {
                    // Device was unreachable.
                    attempt_connection = false;
                }
            }
    
            if (attempt_connection) {
                std::string error;
                //此时command中携带的就是以pm开头的命令
                std::string service_string = ShellServiceString(use_shell_protocol, "", command);
    
                //向shell服务发送命令
                fd = adb_connect(service_string, &error);
                if (fd >= 0) {
                    break;
                }
            }
            ............
        }
    
        //读取返回结果
        int exit_code = read_and_dump(fd, use_shell_protocol, output, err);
        if (adb_close(fd) < 0) {
            ..........
        }
    
        return int exit_code;
    }

    从上面的代码来看,pm_command就是向shell服务发送pm命令。

    pm是一个可执行脚本,我们在终端上调用adb shell,然后执行pm,可以得到以下结果:

    root:/ # pm
    usage: pm list packages [-f] [-d] [-e] [-s] [-3] [-i] [-u] [--user USER_ID] [FILTER]
           pm list permission-groups
           pm list permissions [-g] [-f] [-d] [-u] [GROUP]
           pm list instrumentation [-f] [TARGET-PACKAGE]
    ..........

    pm脚本定义在frameworks/base/cmds/pm中:

    base=/system
    export CLASSPATH=$base/framework/pm.jar
    exec app_process $base/bin <span class="hljs-keyword">com</span><span class="hljs-preprocessor">.android</span><span class="hljs-preprocessor">.commands</span><span class="hljs-preprocessor">.pm</span><span class="hljs-preprocessor">.Pm</span> <span class="hljs-string">"$@"

    在编译system.img时,会根据Android.mk将该脚本复制到system/bin目录下。
    从脚本的内容来看,当调用pm时,将向app_process目录的main函数传入Pm对应的参数:

    我们看看对应的定义于app_main.cpp的main函数(前面的博客分析过,这个其实也是zygote启动的函数):

    //app_process的main函数
    int main(int argc, char* const argv[]) {
        ........
        //解析参数
        while (i < argc) {
            const char* arg = argv[i++];
            if (strcmp(arg, "--zygote") == 0) {
                zygote = true;
                niceName = ZYGOTE_NICE_NAME;
            } else if (strcmp(arg, "--start-system-server") == 0) {
                startSystemServer = true;
            } else if (strcmp(arg, "--application") == 0) {
                application = true;
            } else if (strncmp(arg, "--nice-name=", 12) == 0) {
                niceName.setTo(arg + 12);
            } else if (strncmp(arg, "--", 2) != 0) {
                //此时我们有参数,进入该分支设置className
                className.setTo(arg);
                break;
            } else {
                --i;
                break;
            }
        }
        ...........
        if (zygote) {
            runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
        } else if (className) {
            //此时不再是启动zygote,而是启动className对应的类
            runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
        } else {
            .........
        }
        ........
    }

    我们跟进AndroidRuntime.cpp的start函数:

    void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
    {
        ..........
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
                "([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
        } else {
            //反射调用main函数,从native层进入java世界
            env->CallStaticVoidMethod(startClass, startMeth, strArray);
        }
        .........
    }

    于是流程会进入到RuntimeInit的main函数:

    public static final void main(String[] argv) {
        ........
        //进行一些常规的初始化工作
        commonInit();
        /*
        * Now that we're running in interpreted code, call back into native code
        * to run the system.
        */
        nativeFinishInit();
        .........
    }

    native函数定义在framework/base/core/jni/AndroidRuntime.cpp中,对应的函数为:

    static void com_android_internal_os_RuntimeInit_nativeFinishInit(JNIEnv* env, jobject clazz)
    {
        //gCurRuntime保存AndroidRuntime,实际上是AndroidRuntime的子类
        gCurRuntime->onStarted();
    }

    App_main.cpp中定义的AppRuntime继承AndroidRuntime,实现了onStarted函数:

    virtual void onStarted()
    {
        //binder通信相关的
        sp<ProcessState> proc = ProcessState::self();
        ALOGV("App process: starting thread pool.\n");
        proc->startThreadPool();
    
        AndroidRuntime* ar = AndroidRuntime::getRuntime();
        //调用AndroidRuntime.cpp的callMain函数,参数与Pm.java相关
        ar->callMain(mClassName, mClass, mArgs);
    
        IPCThreadState::self()->stopProcess();
    }
    status_t AndroidRuntime::callMain(const String8& className, jclass clazz,
            const Vector<String8>& args) {
        ..........
        env = getJNIEnv();
        ..........
        methodId = env->GetStaticMethodID(clazz, "main", "([Ljava/lang/String;)V");
        ..........
        const size_t numArgs = args.size();
        stringClass = env->FindClass("java/lang/String");
        strArray = env->NewObjectArray(numArgs, stringClass, NULL);
    
        for (size_t i = 0; i < numArgs; i++) {
            jstring argStr = env->NewStringUTF(args[i].string());
            env->SetObjectArrayElement(strArray, i, argStr);
        }
        ...........
        //最终调用了Pm.java的main函数
        env->CallStaticVoidMethod(clazz, methodId, strArray);
        return NO_ERROR;
    }

    这里自己初次看时,认为这里没有fork新的进程,那么APK安装运行在zygote进程中。
    实际上这是一个错误的理解,说明自己的理解还不到位。
    init创建zygote进程时,是fork出一个子进程,然后才调用app_main中的函数,此时整个zygote严格来讲只是一个native进程;当app_main函数最终通过AndroidRuntime等反射调用zygoteInit.java的main函数后,才演变成了Java层的zygote进程。
    这里的情况是类似的,adb进程发送消息给Shell服务,Shell服务执行Pm脚本,由于exec函数并未创建出新的进程,因此调用app_main后整个代码仍然是运行在Shell服务对应的native进程中,同样通过反射后演变为Java层中的进程。

    这里自己花了很多的笔墨来分析如何从执行脚本文件,到启动Java进程。
    主要是弄懂这个机制后,我们实际上完全可以学习pm的写法,依葫芦画瓢写一个脚本文件,然后定义对应的Java文件。
    通过脚本命令,来让Java层的进程提供服务。

    最后,我们通过一个图来总结一下这个过程:

    三、Pm中的流程
    现在我们进入了Pm.java的main函数:

    public static void main(String[] args) {
        int exitCode = 1;
        try {
            //别被写法欺骗了,Pm并没有继承Runnable
            exitCode = new Pm().run(args);
        } catch (Exception e) {
            .......
        }
        System.exit(exitCode);
    }
    
    //根据参数进行对应的操作,现在我们仅关注APK安装
    public int run(String[] args) throws RemoteException {
        ...........
        //利用Binder通信,得到PKMS服务端代理
        mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
    
        //保存参数
        mArgs = args;
        String op = args[0];
        mNextArg = 1;
    
        ............
        //返回PKMS中保存的PackageInstallerService
        mInstaller = mPm.getPackageInstaller();
        ........
        if ("install".equals(op)) {
            //安装APK将调用runInstall
            return runInstall();
        }
        .......
    }

    我们跟进runInstall函数:

    private int runInstall() throws RemoteException {
        //根据参数创建InstallParams,其中包含了SessionParams,标志为MODE_FULL_INSTALL
        final InstallParams params = makeInstallParams();
        //1 创建Session
        final int sessionId = doCreateSession(params.sessionParams,
                params.installerPackageName, params.userId);
        try {
            //inPath对应于安装的APK文件
            final String inPath = nextArg();
            .......
            //2 wirite session
            if (doWriteSession(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk",
                    false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
                return 1;
            }
            //3 commit session
            if (doCommitSession(sessionId, false /*logSuccess*/)
                    != PackageInstaller.STATUS_SUCCESS) {
                return 1;
            }
            System.out.println("Success");
            return 0;
        } finally {
            ........
        }
    }

    从上面的代码来看,runInstall主要进行了三件事,即创建session、对session进行写操作,最后提交session。
    接下来,我们来看看每一步究竟在干些什么:

    1、 create session

    private int doCreateSession(SessionParams params, String installerPackageName, int userId)
            throws RemoteException {
        //通过ActivityManagerService得到"runInstallCreate"(作为Context对应的字符串)对应的uid
        userId = translateUserId(userId, "runInstallCreate");
        if (userId == UserHandle.USER_ALL) {
            userId = UserHandle.USER_SYSTEM;
            params.installFlags |= PackageManager.INSTALL_ALL_USERS;
        }
    
        //通过PackageInstallerService创建session
        final int sessionId = mInstaller.createSession(params, installerPackageName, userId);
        return sessionId;
    }

    跟进一下PackageInstallerService的createSession函数:

    @Override
    public int createSession(SessionParams params, String installerPackageName, int userId) {
        try {
            return createSessionInternal(params, installerPackageName, userId);
        } catch (IOException e) {
            throw ExceptionUtils.wrap(e);
        }
    }
    
    private int createSessionInternal(SessionParams params, String installerPackageName, int userId)
            throws IOException {
        //安装权限检查
        .......
        //修改SessionParams的installFlags
        if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
            params.installFlags |= PackageManager.INSTALL_FROM_ADB;
        } else {
            .........
        }
        ..........
        // Defensively resize giant app icons
        //调整app图标大小,这里应该是不同安装方式共用的代码
        //通过adb安装apk时,应该还没有解析到app图标
        if (params.appIcon != null) {
            ........
        }
    
        //根据SessionParams的installFlags进行一些操作
        ..........
        } else {
            // For now, installs to adopted media are treated as internal from
            // an install flag point-of-view.
            //adb安装应该进入这个分支(不添加参数指定安装在sd card时),为SessionParams设置InstallInternal Flag,后文会用到
            params.setInstallFlagsInternal();
            ...........
        }
    
        final int sessionId;
        final PackageInstallerSession session;
        synchronized (mSessions) {
            // Sanity check that installer isn't going crazy
            //确保同一个uid没有提交过多的Session,MAX_ACTIVE_SESSIONS为1024
            final int activeCount = getSessionCount(mSessions, callingUid);
            if (activeCount >= MAX_ACTIVE_SESSIONS) {
                throw new IllegalStateException(
                        "Too many active sessions for UID " + callingUid);
            }
    
            //同样确保同一个uid没有提交过多的Session,MAX_HISTORICAL_SESSIONS为1048576
            final int historicalCount = getSessionCount(mHistoricalSessions, callingUid);
            if (historicalCount >= MAX_HISTORICAL_SESSIONS) {
                throw new IllegalStateException(
                        "Too many historical sessions for UID " + callingUid);
            }
            ........
            //sessionId是个随机值
            sessionId = allocateSessionIdLocked();
    
            // We're staging to exactly one location
            File stageDir = null;
            String stageCid = null;
            //根据installFlags,决定安装目录,前文已经提到,过默认将安装到internal目录下
            if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
                final boolean isEphemeral =
                        (params.installFlags & PackageManager.INSTALL_EPHEMERAL) != 0;
                //此处将会在临时性的data目录下创建出file,应该是作为copy的目的地址
                stageDir = buildStageDir(params.volumeUuid, sessionId, isEphemeral);
            } else {
                stageCid = buildExternalStageCid(sessionId);
            }
    
            session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
                    mInstallThread.getLooper(), sessionId, userId, installerPackageName, callingUid,
                    params, createdMillis, stageDir, stageCid, false, false);
                    mSessions.put(sessionId, session);
            mSessions.put(sessionId, session);
        }
        //进行回调
        mCallbacks.notifySessionCreated(session.sessionId, session.userId);
        //在mSessionsFile中进行记录
        writeSessionsAsync();
    
        return sessionId;
    }

    从代码来看,上述代码的目的就是为APK安装做好准备工作,例如权限检查、目的临时文件的创建等, 最终创建出PackageInstallerSession对象。PackageInstallerSession可以看做是”安装APK”这个请求的封装,其中包含了处理这个请求需要的一些信息。
    这种设计方式,大致可以按照命令模式来理解。

    实际上PackageInstallerSession不仅是分装请求的对象,其自身还是个服务端:

    public class PackageInstallerSession extends IPackageInstallerSession.Stub 

    2、write session
    创建出PackageInstallerSession后,我们看看Pm.java中的doWriteSession函数:

     private int doWriteSession(int sessionId, String inPath, long sizeBytes, String splitName,
            boolean logSuccess) throws RemoteException {
        if ("-".equals(inPath)) {
            inPath = null;
        } else if (inPath != null) {
            //此时file指向了待安装的APK文件(adb执行拷贝后的目的地址)
            final File file = new File(inPath);
            if (file.isFile()) {
                sizeBytes = file.length();
            }
        }
        ......
        //取出PackageInstallerSession中的SessionInfo
        final SessionInfo info = mInstaller.getSessionInfo(sessionId);
    
        PackageInstaller.Session session = null;
        InputStream in = null;
        OutputStream out = null;
        try {
            //1 获取PackageInstallerSession的调用接口
            session = new PackageInstaller.Session(
                    mInstaller.openSession(sessionId));
    
            if (inPath != null) {
                //定义输入端,待安装APK对应文件的源地址
                in = new FileInputStream(inPath);
            } else {
                in = new SizedInputStream(System.in, sizeBytes);
            }
    
            //2 定义输出端,对应拷贝后的目的地址
            out = session.openWrite(splitName, 0, sizeBytes);
    
            int total = 0;
            byte[] buffer = new byte[65536];
            int c;
            //进行文件的拷贝
            while ((c = in.read(buffer)) != -1) {
                total += c;
                out.write(buffer, 0, c);
    
                if (info.sizeBytes > 0) {
                    final float fraction = ((float) c / (float) info.sizeBytes);
                    //只是更新进度而已
                    session.addProgress(fraction);
                }
            }
            session.fsync(out);
            ......
            return PackageInstaller.STATUS_SUCCESS;
        } catch (IOException e) {
            ........
        } finally {
            IoUtils.closeQuietly(out);
            IoUtils.closeQuietly(in);
            IoUtils.closeQuietly(session);
        }
    }

    从doWriteSession的代码来看,此处进行的主要工作就是通过Session将源端的数据拷贝到目的端。
    其实从整个对象的命名和执行过程来看,这里整个是基于C/S架构的通信过程,Pm作为PackageInstallerService 的客户端,利用PackageInstallerSession来封装每一次完整的通信过程。

    2.1 得到PackageInstallerSession的代理对象
    我们看看上面代码调用的PackageInstaller.Session的构造函数:

    //参数传入的是PackageInstallerService.openSession函数的返回结果,即实际PackageInstallerSession的代理端
    public Session(IPackageInstallerSession session) {
        mSession = session;
    }

    我们看看PackageInstallerService.openSession函数:

    @Override
    public IPackageInstallerSession openSession(int sessionId) {
        try {
            return openSessionInternal(sessionId);
        } catch (IOException e) {
            throw ExceptionUtils.wrap(e);
        }
    }
    
    private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException {
        synchronized (mSessions) {
            //根据sessionId得到之前创建的PackageInstallerSession
            final PackageInstallerSession session = mSessions.get(sessionId);
            if (session == null || !isCallingUidOwner(session)) {
                throw new SecurityException("Caller has no access to session " + sessionId);
            }
            //调用其open函数
            session.open();
    
            //PacakgeInstallerSession转化为IPackageInstallerSession返回
            return session;
        }
    }
    
    //open函数就是准备好待拷贝的目录
    public void open() throws IOException {
        .......
            //PackageInstallerService创建出PackageInstallerSession时,传入的prepared参数为false
            if (!mPrepared) {
                if (stageDir != null) {
                    prepareStageDir(stageDir);
                } else if (stageCid != null) {
                    prepareExternalStageCid(stageCid, params.sizeBytes);
                    .....
                } else {
                    //throw exception
                    ......
                }
                ........
            }
        }
    }

    2.2 得到客户端
    PacakgeInstaller.Session的openWrite函数:

    public @NonNull OutputStream openWrite(@NonNull String name, long offsetBytes,
            long lengthBytes) throws IOException {
        try {
            //mSession是PacakgeInstallerSession,这里发生了Binder通信
            final ParcelFileDescriptor clientSocket = mSession.openWrite(name,
                offsetBytes, lengthBytes);
            //引入了FileBridge对象,后文分析
            return new FileBridge.FileBridgeOutputStream(clientSocket);
        } catch (RuntimeException e) {
            ExceptionUtils.maybeUnwrapIOException(e);
            throw e;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    我们看看PacakgeInstallerSession的openWrite函数:

    @Override
    public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) {
        try {
            return openWriteInternal(name, offsetBytes, lengthBytes);
        } catch (IOException e) {
            throw ExceptionUtils.wrap(e);
        }
    }
    
    private ParcelFileDescriptor openWriteInternal(String name, long offsetBytes, long lengthBytes)
        throws IOException {
        // Quick sanity check of state, and allocate a pipe for ourselves. We
        // then do heavy disk allocation outside the lock, but this open pipe
        // will block any attempted install transitions.
        //FileBrige建立了客户端和服务端的管道
        final FileBridge bridge;
        synchronized (mLock) {
            ......
            bridge = new FileBridge();
            mBridges.add(bridge);
        }
    
        try {
            // Use installer provided name for now; we always rename later
            if (!FileUtils.isValidExtFilename(name)) {
                throw new IllegalArgumentException("Invalid name: " + name);
            }
    
            //打开文件,定义权限
            final File target = new File(resolveStageDir(), name);
            // holding open FDs into containers.
            final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(),
                    O_CREAT | O_WRONLY, 0644);
            Os.chmod(target.getAbsolutePath(), 0644);
    
            //定义文件内存格式及分配内存
            // If caller specified a total length, allocate it for them. Free up
            // cache space to grow, if needed.
            if (lengthBytes > 0) {
                final StructStat stat = Libcore.os.fstat(targetFd);
                final long deltaBytes = lengthBytes - stat.st_size;
                // Only need to free up space when writing to internal stage
                if (stageDir != null && deltaBytes > 0) {
                    mPm.freeStorage(params.volumeUuid, deltaBytes);
                }
                Libcore.os.posix_fallocate(targetFd, 0, lengthBytes);
            }
    
            //定义起始偏移量
            if (offsetBytes > 0) {
                Libcore.os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET);
            }
    
            bridge.setTargetFile(targetFd);
            bridge.start();
    
            //返回了bridge的client socket
            return new ParcelFileDescriptor(bridge.getClientSocket());
        } catch (ErrnoException e) {
            throw e.rethrowAsIOException();
        }
    }

    2.2.1 FileBridge
    为了更好的理解上述过程,我们需要看看FileBridge:

    public class FileBridge extends Thread {
        .......
        private final FileDescriptor mServer = new FileDescriptor();
        private final FileDescriptor mClient = new FileDescriptor();
        .......
        public FileBridge() {
            try {
                //构造函数建立的mServer和mClient之间的管道
                Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mServer, mClient);
            } catch (ErrnoException e) {
                throw new RuntimeException("Failed to create bridge");
            }
        }
        .......
        public void setTargetFile(FileDescriptor target) {
            mTarget = target;
        }
    
        public FileDescriptor getClientSocket() {
            return mClient;
        }
    
        @Override
        public void run() {
            final byte[] temp = new byte[8192];
            try {
                //取出mServer中的数据,并进行处理
                //注意mSever和mClient通道绑定,于是读出的数据是mClient写入的,向mServer写数据也会递交给mClient
                while (IoBridge.read(mServer, temp, 0, MSG_LENGTH) == MSG_LENGTH) {
                    final int cmd = Memory.peekInt(temp, 0, ByteOrder.BIG_ENDIAN);
                    if (cmd == CMD_WRITE) {
                        // Shuttle data into local file
                        int len = Memory.peekInt(temp, 4, ByteOrder.BIG_ENDIAN);
                        while (len > 0) {
                            int n = IoBridge.read(mServer, temp, 0, Math.min(temp.length, len));
                            .......
                            IoBridge.write(mTarget, temp, 0, n);
                            len -= n;
                        }
                    } else if (cmd == CMD_FSYNC) {
                        // Sync and echo back to confirm
                        Os.fsync(mTarget);
                        IoBridge.write(mServer, temp, 0, MSG_LENGTH);
                    } else if (cmd == CMD_CLOSE) {
                        // Close and echo back to confirm
                        Os.fsync(mTarget);
                        Os.close(mTarget);
                        mClosed = true;
                        IoBridge.write(mServer, temp, 0, MSG_LENGTH);
                        break;
                    }
                }
            } catch (ErrnoException | IOException e) {
                ........
            } finally {
                forceClose();
            }
    }

    通过调用PackageInstallerSession的openWrite函数,Pm将得到与PackageInstallerSession通信的client端,同时PackageInstallerSession启动FileBridge准备接收数据。

    在上文中进行文件拷贝时,最终就是利用FileBridge的管道来完成实际的工作。

    3、 commit session
    根据上面的代码,我们知道doWriteSession结束后,如果没有出现任何错误,那么APK源文件已经copy到目的地址了。
    接下来我们看看doCommitSession进行的工作。

    private int doCommitSession(int sessionId, boolean logSuccess) throws RemoteException {
        PackageInstaller.Session session = null;
        try {
            session = new PackageInstaller.Session(
                    mInstaller.openSession(sessionId));
    
            //receiver用于接收结果
            final LocalIntentReceiver receiver = new LocalIntentReceiver();
            //提交session
            session.commit(receiver.getIntentSender());
            .....
        } finally {
            IoUtils.closeQuietly(session);
        }
    }

    PackageInstaller.Session中的commit函数,将通过Binder通信调用PackageInstallerSession的commit函数:

    public void commit(@NonNull IntentSender statusReceiver) {
        try {
            mSession.commit(statusReceiver);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    我们跟进PackageInstallerSession的commit函数:

    @Override
    public void commit(IntentSender statusReceiver) {
        .......
        final boolean wasSealed;
        synchronized (mLock) {
            //初始时mSealed为false
            wasSealed = mSealed;
            if (!mSealed) {
                // Verify that all writers are hands-off
                //前面的doWriteSession传输数据的结尾,会关闭bridge
                for (FileBridge bridge : mBridges) {
                    if (!bridge.isClosed()) {
                        throw new SecurityException("Files still open");
                    }
                }
                mSealed = true;
            }
        }
        ............
        final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,
                statusReceiver, sessionId, mIsInstallerDeviceOwner, userId);
        mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
    }

    PackageInstallerSession被创建时,指定了mHandler对应callback:

    private final Handler.Callback mHandlerCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            synchronized (mLock) {
                if (msg.obj != null) {
                    //其实就是存储Pm.java中的结果接收器
                    mRemoteObserver = (IPackageInstallObserver2) msg.obj;
                }
    
                try {
                    //因此,commit发送消息后,最终将触发commitLocked
                    commitLocked();
                } catch (PackageManagerException e) {
                    .......
                }
    
                return true;
            }
        }
    };
    
    private void commitLocked() throws PackageManagerException {
        .......
        try {
            //解析安装地址,即前文APK文件copy后的目的地址
            resolveStageDir();
        } catch (IOException e) {
            ........
        }
    
        // Verify that stage looks sane with respect to existing application.
        // This currently only ensures packageName, versionCode, and certificate
        // consistency.
        //将利用PKMS检查APK文件是否满足要求,主要是保证各个文件是否具有一致性
        validateInstallLocked();
    
        //检查权限等
        ........
    
        if (stageCid != null) {
            // Figure out the final installed size and resize the container once
            // and for all. Internally the parser handles straddling between two
            // locations when inheriting.
            final long finalSize = calculateInstalledSize();
            resizeContainer(stageCid, finalSize);
        }
    
        // Inherit any packages and native libraries from existing install that
        // haven't been overridden.
        if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
            //如果新的APK文件继承某些已安装的Pacakge,此处将copy需要的native库文件等
            .........
        }
    
        ......
        // Unpack native libraries
        //解压缩native库文件
        extractNativeLibraries(mResolvedStageDir, params.abiOverride);
    
        // Container is ready to go, let's seal it up!
        if (stageCid != null) {
            //针对安装在sdcard的操作,根据uid、gid调用fixSdPermissions
            finalizeAndFixContainer(stageCid);
        }
    
        .........
        //调用PKMS的installStage,进入安装的下一步操作
        mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
                installerPackageName, installerUid, user, mCertificates);
    }

    代码看到这里,我们终于明白了APK安装过程中,Pm.java进行的操作其实就是将adb拷贝的文件,拷贝到系统内或sdcard的目录中,然后进行初步的权限检查等工作,最后通知PKMS进入Install Stage。
    整个代码引入了PackageInstallerSession,个人认为这里涉及了Binder通信、类似于Java网络通信的架构及命令模式,写的非常巧妙,有值得学习和模仿的地方。

    我们同样用一张图来为这一部分做个总结:

    大图链接

    四、installStage
    几经波折,APK的安装流程终于进入到了PKMS,我们看看installStage函数:

    void installStage(String packageName, File stagedDir, String stagedCid,
            IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
            String installerPackageName, int installerUid, UserHandle user,
            Certificate[][] certificates) {
        ............
        //verificationInfo主要用于存储权限验证需要的信息
        final VerificationInfo verificationInfo = new VerificationInfo(
                sessionParams.originatingUri, sessionParams.referrerUri,
                sessionParams.originatingUid, installerUid);
    
        //origin中主要存储的APK文件的路径信息
        final OriginInfo origin;
        if (stagedDir != null) {
            origin = OriginInfo.fromStagedFile(stagedDir);
        } else {
            origin = OriginInfo.fromStagedContainer(stagedCid);
        }
    
        final Message msg = mHandler.obtainMessage(INIT_COPY);
        //准备安装所需的参数
        final InstallParams params = new InstallParams(origin, null, observer,
                sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
                verificationInfo, user, sessionParams.abiOverride,
                sessionParams.grantedRuntimePermissions, certificates);
        .........
        msg.obj = params;
        .........
        //发送INIT_COPY消息,驱动处理流程
        mHandler.sendMessage(msg);
    }

    PKMS中实际的消息处理函数为doHandleMessage:

    void doHandleMessage(Message msg) {
        switch (msg.what) {
            case INIT_COPY: {
                //这里取出的其实就是InstallParams,其继承HandlerParams
                HandlerParams params = (HandlerParams) msg.obj;
                //idx为当前等待处理处理的安装请求的个数
                int idx = mPendingInstalls.size();
                ............
                // If a bind was already initiated we dont really
                // need to do anything. The pending install
                // will be processed later on.
                //初始时,mBound的值为false
                if (!mBound) {
                    ............
                    // If this is the only one pending we might
                    // have to bind to the service again.
                    //连接实际的安装服务,后文介绍
                    if (!connectToService()) {
                        ..................
                    } else {
                        // Once we bind to the service, the first
                        // pending request will be processed.
                        //绑定服务成功后,将新的请求加入到mPendingIntalls中,等待处理
                        mPendingInstalls.add(idx, params);
                    }
                } else {
                    //如果之前已经绑定过服务,同样将新的请求加入到mPendingIntalls中,等待处理
                    mPendingInstalls.add(idx, params);
                    // Already bound to the service. Just make
                    // sure we trigger off processing the first request.
                    if (idx == 0) {
                        //如果是第一个请求,则直接发送事件MCS_BOUND,触发处理流程
                        mHandler.sendEmptyMessage(MCS_BOUND);
                    }
                }
                break;
            }
        }
    }

    上面代码的处理逻辑实际上是比较简单的,我们就看看connectToService的操作,来寻找一下实际进行安装工作的服务:

    private boolean connectToService() {
        ........
        //Component的包名为"com.android.defcontainer";类名为"com.android.defcontainer.DefaultContainerService"
        Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
        Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
        if (mContext.bindServiceAsUser(service, mDefContainerConn,
                Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            mBound = true;
            return true;
        }
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        return false;
    }

    从代码可以看出实际进行安装工作的服务是DefaultContainerService,当绑定服务成功后:

    class DefaultContainerConnection implements ServiceConnection {
        public void onServiceConnected(ComponentName name, IBinder service) {
            ........
            //获得与服务端通信的代理对象
            IMediaContainerService imcs =
                    IMediaContainerService.Stub.asInterface(service);
            //发送MCS_BOUND消息触发流程
            mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));
        }
        .......
    }

    现在我们知道了当服务绑定成功后,也会发送MCS_BOUND消息触发接下来的流程。

    MCS_BOUND对应的处理流程同样定义于doHandleMessage中:

    void doHandleMessage(Message msg) {
        .......
        case MCS_BOUND: {
            ........
            if (msg.obj != null) {
                mContainerService = (IMediaContainerService) msg.obj;
                .......
            }
            if (mContainerService == null) {
                if (!mBound) {
                    // Something seriously wrong since we are not bound and we are not
                    // waiting for connection. Bail out.
                    ............            
                } else {
                    Slog.w(TAG, "Waiting to connect to media container service");
                }
            } else if (mPendingInstalls.size() > 0) {
                HandlerParams params = mPendingInstalls.get(0);
                if (params != null) {
                    ........
                    //调用参数的startCopy函数
                    if (params.startCopy()) {
                        ........
                        // Delete pending install
                        if (mPendingInstalls.size() > 0) {
                            mPendingInstalls.remove(0);
                        }
                        if (mPendingInstalls.size() == 0) {
                            if (mBound) {
                                ..........
                                removeMessages(MCS_UNBIND);
                                Message ubmsg = obtainMessage(MCS_UNBIND);
                                // Unbind after a little delay, to avoid
                                // continual thrashing.
                                sendMessageDelayed(ubmsg, 10000);
                            }
                        } else {
                            // There are more pending requests in queue.
                            // Just post MCS_BOUND message to trigger processing
                            // of next pending install.
                            ......
                            mHandler.sendEmptyMessage(MCS_BOUND);
                        }
                    }
                    .........
                }
            } else {
                // Should never happen ideally.
                Slog.w(TAG, "Empty queue");
            }
            break;
        }
    .......
    }

    这一段代码写的非常清晰,就是处理完一个安装请求后,接着处理下一个;如果队列为空,则等待一段时间后,发送MCS_UNBIND消息断开与安装服务的绑定。

    顺着流程,我们现在看看HandlerParams的startCopy函数:

    final boolean startCopy() {
        boolean res;
        try {
            ........
            //处理安装失败,MAX_RETRIES = 4
            if (++mRetries > MAX_RETRIES) {
                .........
                mHandler.sendEmptyMessage(MCS_GIVE_UP);
                handleServiceError();
                return false;
            } else {
                //先调用handleStartCopy进行实际的copy工作
                handleStartCopy();
                res = true;
            }
        } catch (RemoteException e) {
            if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
            mHandler.sendEmptyMessage(MCS_RECONNECT);
            res = false;
        }
        //然后根据结果做相应处理
        handleReturnCode();
        return res;
    }


    如上图所示,从这段代码来看,PKMS将先后调用handleStartCopy和handleReturnCode来完成主要的工作。接下来,我们分别介绍一下这两个函数的工作流程。

    五、handleStartCopy
    HandlerParams为PKMS的内部抽象类,上面代码中的实际处理函数由其子类InstallParams来实现,我们看看与实际安装相关的handleStartCopy函数:

    public void handleStartCopy() throws RemoteException {
        int ret = PackageManager.INSTALL_SUCCEEDED;
    
        // If we're already staged, we've firmly committed to an install location
        //根据参数决定是安装在手机内还是sdcard中,设置对应标志位
        if (origin.staged) {
            if (origin.file != null) {
                installFlags |= PackageManager.INSTALL_INTERNAL;
                installFlags &= ~PackageManager.INSTALL_EXTERNAL;
            } else if (origin.cid != null) {
                installFlags |= PackageManager.INSTALL_EXTERNAL;
                installFlags &= ~PackageManager.INSTALL_INTERNAL;
            } else {
                throw new IllegalStateException("Invalid stage location");
            }
        }
    
        final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
        final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
        final boolean ephemeral = (installFlags & PackageManager.INSTALL_EPHEMERAL) != 0;
        PackageInfoLite pkgLite = null;
    
        //检查APK的安装位置是否正确
        if (onInt && onSd) {
            // Check if both bits are set.
            ...........
            //APK不能同时安装在内部存储空间和SD card上
            ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
        } else if (onSd && ephemeral) {
            .......
            //APK不能短暂地安装在SD card上
            ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
        }  else {
            //1、利用ContainerService获取PackageInfoLite,应该判断了能否进行安装
            pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,
                    packageAbiOverride);
            .........
    
            /*
            * If we have too little free space, try to free cache
            * before giving up.
            */
            //对于安装在SD card上的APK,当存储空间过小导致安装失败时
            if (!origin.staged && pkgLite.recommendedInstallLocation
                    == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
                final StorageManager storage = StorageManager.from(mContext);
                //利用StroageManager得到设备内部存储空间允许的最小余量
                final long lowThreshold = storage.getStorageLowBytes(
                        Environment.getDataDirectory());
    
                //利用ContainerService得到安装APK的大小
                final long sizeBytes = mContainerService.calculateInstalledSize(
                        origin.resolvedPath, isForwardLocked(), packageAbiOverride);
                try {
                    //利用Installer释放缓存,试图将缓存释放到大于等于(最小余量与APK大小之和)
                    mInstaller.freeCache(null, sizeBytes + lowThreshold);
                    //再次试图得到PackageInfoLite,判断是否满足安装条件
                    pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,
                            installFlags, packageAbiOverride);
                } catch (InstallerException e) {
                    Slog.w(TAG, "Failed to free cache", e);
                }
    
                /*
                * The cache free must have deleted the file we
                * downloaded to install.
                *
                * TODO: fix the "freeCache" call to not delete
                *       the file we care about.
                */
                if (pkgLite.recommendedInstallLocation
                        == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
                    //试图释放cache还是无法安装,只能设置标志位为失败
                    pkgLite.recommendedInstallLocation
                            = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
                }
            }
        }
    
        if (ret == PackageManager.INSTALL_SUCCEEDED) {
            //recommendedInstallLocation中记录了安装的路径信息,即APK保存在终端内部还是Sd card中,此外也可以记录安装失败的信息
            int loc = pkgLite.recommendedInstallLocation;
            if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
                ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
            } else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) {
                ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
            } else if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
                ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
            } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) {
                ret = PackageManager.INSTALL_FAILED_INVALID_APK;
            } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
                ret = PackageManager.INSTALL_FAILED_INVALID_URI;
            } else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) {
                ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
            } else {
                // Override with defaults if needed.
                //2、installLocationPolicy主要判断终端上是否有已经安装过该APK,同一个APK一般只能用新版的替换旧版
                loc = installLocationPolicy(pkgLite);
                //根据loc调整installFlag
                ......
            }
        }
    
        //3、createInstallArgs用于创建一个安装参数对象
        final InstallArgs args = createInstallArgs(this);
        mArgs = args;
        if (ret == PackageManager.INSTALL_SUCCEEDED) {
            // Apps installed for "all" users use the device owner to verify the app
            UserHandle verifierUser = getUser();
            if (verifierUser == UserHandle.ALL) {
                verifierUser = UserHandle.SYSTEM;
            }
            /*
            * Determine if we have any installed package verifiers. If we
            * do, then we'll defer to them to verify the packages.
            */
            final int requiredUid = mRequiredVerifierPackage == null ? -1
                    : getPackageUid(mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
                            verifierUser.getIdentifier());
            if (!origin.existing && requiredUid != -1
                    && isVerificationEnabled(verifierUser.getIdentifier(), installFlags)) {
                //如果存在Package检查者,同时满足启动检查的条件,那么将利用Pacakge检查者来检查安装包
                //其实就是构造一个intent,action为"android.intent.action.PACKAGE_NEEDS_VERIFICATION"
                //在intent中添加需要多信息后,发送给接收者处理
                .........
            } else {
                //4、调用安装参数对象的copyApk函数
                ret = args.copyApk(mContainerService, true);
            }
        }
        mRet = ret;
    }

    handleStartCopy函数整体来看还是比较复杂的,内容比较多,我们需要分4步介绍其中主要的内容。

    1、getMinimalPackageInfo
    getMinimalPackageInfo实际定义于DefaultContainerService中,其代码如下:

    @Override
    public PackageInfoLite getMinimalPackageInfo(String packagePath, int flags,
            String abiOverride) {
        final Context context = DefaultContainerService.this;
        final boolean isForwardLocked = (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
    
        PackageInfoLite ret = new PackageInfoLite();
        ........
        final File packageFile = new File(packagePath);
        final PackageParser.PackageLite pkg;
        final long sizeBytes;
        try {
            //如同PKMS的构造函数,利用PackageParser来解析APK文件,得到PackageInfoLite
            pkg = PackageParser.parsePackageLite(packageFile, 0);
            sizeBytes = PackageHelper.calculateInstalledSize(pkg, isForwardLocked, abiOverride);
        } catch (PackageParserException | IOException e) {
            .................
        }
    
        ret.packageName = pkg.packageName;
        ret.splitNames = pkg.splitNames;
        ret.versionCode = pkg.versionCode;
        ret.baseRevisionCode = pkg.baseRevisionCode;
        ret.splitRevisionCodes = pkg.splitRevisionCodes;
        ret.installLocation = pkg.installLocation;
        ret.verifiers = pkg.verifiers;
        //利用resolveInstallLocation来得到一个合理的安装位置
        ret.recommendedInstallLocation = PackageHelper.resolveInstallLocation(context,
                pkg.packageName, pkg.installLocation, sizeBytes, flags);
        ret.multiArch = pkg.multiArch;
    
        return ret;
    }

    从代码可以看出,getMinimalPackageInfo的代码比较简单,其实就是利用PackageParser解析出APK对应Pacakge的基本信息,然后利用resolveInstallLocation得到适合APK安装的路径。

    1.1 resolveInstallLocation
    我们看看resolveInstallLocation函数:

    public static int resolveInstallLocation(Context context, String packageName,
            int installLocation, long sizeBytes, int installFlags) {
        ApplicationInfo existingInfo = null;
        try {
            //如果之前该APK之前安装过,那么将获取到之前记录的ApplicationInfo信息
            existingInfo = context.getPackageManager().getApplicationInfo(packageName,
                    PackageManager.GET_UNINSTALLED_PACKAGES);
        } catch (NameNotFoundException ignored) {
            .........
        }
    
        final int prefer;
        final boolean checkBoth;
        boolean ephemeral = false;
        //以下其实就是根据installFlags决定安装倾向的路径prefer
        if ((installFlags & PackageManager.INSTALL_EPHEMERAL) != 0) {
            prefer = RECOMMEND_INSTALL_INTERNAL;
            ephemeral = true;
            checkBoth = false;
        } else if ((installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
            prefer = RECOMMEND_INSTALL_INTERNAL;
            checkBoth = false;
        } else if ((installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
            prefer = RECOMMEND_INSTALL_EXTERNAL;
            checkBoth = false;
        } else if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
            prefer = RECOMMEND_INSTALL_INTERNAL;
            checkBoth = false;
        } else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
            prefer = RECOMMEND_INSTALL_EXTERNAL;
            checkBoth = true;
        } else if (installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
            //一般APK安装的路径就是auto
            // When app is already installed, prefer same medium
            if (existingInfo != null) {
                // TODO: distinguish if this is external ASEC
                if ((existingInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
                    prefer = RECOMMEND_INSTALL_EXTERNAL;
                } else {
                    prefer = RECOMMEND_INSTALL_INTERNAL;
                }
            } else {
                //可以看到一般默认条件下是安装在手机内部的
                prefer = RECOMMEND_INSTALL_INTERNAL;
            }
            //auto时,checkBoth为true
            checkBoth = true;
        } else {
            prefer = RECOMMEND_INSTALL_INTERNAL;
            checkBoth = false;
        }
    
        boolean fitsOnInternal = false;
        if (checkBoth || prefer == RECOMMEND_INSTALL_INTERNAL) {
            //fitsOnInternal和下面的fitsOnExternal应该就是用于检查对应路径是否有足够的空间来安装APK的
            fitsOnInternal = fitsOnInternal(context, sizeBytes);
        }
    
        boolean fitsOnExternal = false;
        if (checkBoth || prefer == RECOMMEND_INSTALL_EXTERNAL) {
            fitsOnExternal = fitsOnExternal(context, sizeBytes);
        }
    
        if (prefer == RECOMMEND_INSTALL_INTERNAL) {
            // The ephemeral case will either fit and return EPHEMERAL, or will not fit
            // and will fall through to return INSUFFICIENT_STORAGE
            if (fitsOnInternal) {
                return (ephemeral)
                        ? PackageHelper.RECOMMEND_INSTALL_EPHEMERAL
                        : PackageHelper.RECOMMEND_INSTALL_INTERNAL;
            }
        } else if (prefer == RECOMMEND_INSTALL_EXTERNAL) {
            if (fitsOnExternal) {
                return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
            }
        }
    
        //个人感觉这里就是容错用的吧,在前面的代码中正常情况下,prefer已经被取值为RECOMMEND_INSTALL_INTERNAL或RECOMMEND_INSTALL_EXTERNAL了
        if (checkBoth) {
            if (fitsOnInternal) {
                return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
            } else if (fitsOnExternal) {
                return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
            }
        }
    
        //没有足够空间,返回对应消息
        return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
    }

    上述代码相对比较简单,我们主要以fitsOnInternal为例,看看如何判断存储空间是否足够:

    public static boolean fitsOnInternal(Context context, long sizeBytes) {
        final StorageManager storage = context.getSystemService(StorageManager.class);
        final File target = Environment.getDataDirectory();
        //APK安装所需的空间,小于等于可用空间时,就返回true                
        return (sizeBytes <= storage.getStorageBytesUntilLow(target));
    }
    
    //StorgeManager中的函数,其实就是用总的可用空间,减去已经使用的空间,得到剩余空间
    public long getStorageBytesUntilLow(File path) {
        return path.getUsableSpace() - getStorageFullBytes(path);
    }

    2、installLocationPolicy
    当成功得到APK对应的PacakgeInfoLite,并判断安装路径有足够的剩余空间时,将调用installLocationPolicy函数:

    private int installLocationPolicy(PackageInfoLite pkgLite) {
        String packageName = pkgLite.packageName;
        int installLocation = pkgLite.installLocation;
        boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
    
        synchronized (mPackages) {
            // Currently installed package which the new package is attempting to replace or
            // null if no such package is installed.
            //判断终端上之前是否安装过同样的APK
            PackageParser.Package installedPkg = mPackages.get(packageName);
    
            // Package which currently owns the data which the new package will own if installed.
            // If an app is unstalled while keeping data (e.g., adb uninstall -k), installedPkg
            // will be null whereas dataOwnerPkg will contain information about the package
            // which was uninstalled while keeping its data.
            //当一个APK卸载时,那么installedPkg为null
            PackageParser.Package dataOwnerPkg = installedPkg;
            if (dataOwnerPkg  == null) {
                //但是如果APK卸载时,保留了数据,那么PKMS将取出对应的PacakgeSettings
                PackageSetting ps = mSettings.mPackages.get(packageName);
                if (ps != null) {
                    //从PacakgeSettings中取出Pacakge
                    dataOwnerPkg = ps.pkg;
                }
            }
    
            //存在旧APK对应的信息时
            if (dataOwnerPkg != null) {
                final boolean downgradeRequested =
                        (installFlags & PackageManager.INSTALL_ALLOW_DOWNGRADE) != 0;
                final boolean packageDebuggable =
                        (dataOwnerPkg.applicationInfo.flags
                                & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
                //当安装一个重复的APK时,新装的APK版本一般要比旧APK的版本高
                //除非满足以下要求,例如显示要求装入旧版本、在debug模式下等
                final boolean downgradePermitted =
                    (downgradeRequested) && ((Build.IS_DEBUGGABLE) || (packageDebuggable));
    
                //默认模式下,即仅能安装高版本时
                if (!downgradePermitted) {
                    try {
                        //比较两个Package信息中的VersionCode
                        checkDowngrade(dataOwnerPkg, pkgLite);
                    } catch (PackageManagerException e) {
                        Slog.w(TAG, "Downgrade detected: " + e.getMessage());
                        return PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE;
                    }
                }
            }
    
            //旧有的APK还存在终端上时
            if (installedPkg != null) {
                //installFlags中必须携带REPLACE_EXISTING,否则将报错
                if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
                    // Check for updated system application.
                    if ((installedPkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
                        //系统APK不应该存在SD card上
                        if (onSd) {
                            Slog.w(TAG, "Cannot install update to system app on sdcard");
                            return PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION;
                        }
                        return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
                    } else {
                        // If current upgrade specifies particular preference
                        if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
                            // Application explicitly specified internal.
                            return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
                        } else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
                            // App explictly prefers external. Let policy decide
                        } else {
                            // Prefer previous location
                            //未指定安装路径时,与之前的安装路径保持一致
                            if (isExternal(installedPkg)) {
                                return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
                            }
                            return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
                        }
                    }
                } else {
                    // Invalid install. Return error code
                    return PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS;
                }
            }
        }
        // All the special cases have been taken care of.
        // Return result based on recommended install location.
        if (onSd) {
            return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
        }
        return pkgLite.recommendedInstallLocation;
    }

    3、createInstallArgs
    处理完潜在的重复安装APK的风险后,PKMS调用createInstallArgs生成安装参数对象:

    private InstallArgs createInstallArgs(InstallParams params) {
        if (params.move != null) {
            return new MoveInstallArgs(params);
        } else if (installOnExternalAsec(params.installFlags) || params.isForwardLocked()) {
            return new AsecInstallArgs(params);
        } else {
            return new FileInstallArgs(params);
        }
    }

    这部分的代码较为简单,就是利用参数决定创建哪个InstallArgs的子类,我们主要关注在终端安装APK时,将要使用的FileInstallArgs,后文介绍其功能。

    4、copyApk
    如果不需要进行安装包检查,对于安装在终端内部的APK而言,将调用FileInstallArgs的copyAPK函数:

    int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyApk");
        try {
            //doCopyApk负责进行实际的工作
            return doCopyApk(imcs, temp);
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    }
    
    private int doCopyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
        //与之前版本不同的时,Android7.0中,已经通过Session进行了文件拷贝
        //当进入到前文所述的PKMS的installStage时,OriginInfo.fromStagedFile或OriginInfo.fromStagedContainer均会将staged变量置为true
        if (origin.staged) {
            if (DEBUG_INSTALL) Slog.d(TAG, origin.file + " already staged; skipping copy");
                codeFile = origin.file;
                resourceFile = origin.file;
                return PackageManager.INSTALL_SUCCEEDED;
            }
        }
    
        //当使用其它方式安装APK时,将进入到以下流程
        try {
            //当需要临时安装时,创建一个临时安装目录
            final boolean isEphemeral = (installFlags & PackageManager.INSTALL_EPHEMERAL) != 0;
            final File tempDir =
                    mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);
            codeFile = tempDir;
            resourceFile = tempDir;
        } catch (IOException e) {
            Slog.w(TAG, "Failed to create copy file: " + e);
            return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
        }
    
        //定义回调接口
        final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {
            @Override
            public ParcelFileDescriptor open(String name, int mode) throws RemoteException {
                if (!FileUtils.isValidExtFilename(name)) {
                    throw new IllegalArgumentException("Invalid filename: " + name);
                }
                try {
                    //当接口被回调时,需要创建并打开文件,同事赋予相应的权限
                    final File file = new File(codeFile, name);
                    final FileDescriptor fd = Os.open(file.getAbsolutePath(),
                            O_RDWR | O_CREAT, 0644);
                    Os.chmod(file.getAbsolutePath(), 0644);
                    return new ParcelFileDescriptor(fd);
                } catch (ErrnoException e) {
                    throw new RemoteException("Failed to open: " + e.getMessage());
                }
            }
        };
    
        //调用DefaultContainerService进行copyPackage的操作,传入了回调的接口
        int ret = PackageManager.INSTALL_SUCCEEDED;
        ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
        if (ret != PackageManager.INSTALL_SUCCEEDED) {
            Slog.e(TAG, "Failed to copy package");
            return ret;
        }
    
        //拷贝APK对应的Native库文件
        final File libraryRoot = new File(codeFile, LIB_DIR_NAME);
        NativeLibraryHelper.Handle handle = null;
        try {
            handle = NativeLibraryHelper.Handle.create(codeFile);
            ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
                    abiOverride);
        } catch (IOException e) {
            Slog.e(TAG, "Copying native libraries failed", e);
            ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
        } finally {
            IoUtils.closeQuietly(handle);
        }
    
        return ret;
    }

    对于非adb安装的APK,我们看看DefaultContainerService对应的copyPackage是如何进行处理的:

    @Override
    public int copyPackage(String packagePath, IParcelFileDescriptorFactory target) {
        if (packagePath == null || target == null) {
            return PackageManager.INSTALL_FAILED_INVALID_URI;
        }
    
        PackageLite pkg = null;
        try {
            final File packageFile = new File(packagePath);
            //解析出PackageFile
            pkg = PackageParser.parsePackageLite(packageFile, 0);
            //利用copyPackageInner进行实际的处理
            return copyPackageInner(pkg, target);
        } catch (PackageParserException | IOException | RemoteException e) {
            Slog.w(TAG, "Failed to copy package at " + packagePath + ": " + e);
            return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
        }
    }
    
    private int copyPackageInner(PackageLite pkg, IParcelFileDescriptorFactory target)
            throws IOException, RemoteException {
        //copyFile负责实际的拷贝
        copyFile(pkg.baseCodePath, target, "base.apk");
        if (!ArrayUtils.isEmpty(pkg.splitNames)) {
            for (int i = 0; i < pkg.splitNames.length; i++) {
                //对于多APK文件的情况,需依次拷贝所有的子文件
                copyFile(pkg.splitCodePaths[i], target, "split_" + pkg.splitNames[i] + ".apk");
            }
        }
    
        return PackageManager.INSTALL_SUCCEEDED;
    }
    
    private void copyFile(String sourcePath, IParcelFileDescriptorFactory target, String targetName)
            throws IOException, RemoteException {
        Slog.d(TAG, "Copying " + sourcePath + " to " + targetName);
        InputStream in = null;
        OutputStream out = null;
        try {
            //源文件作为输入
            in = new FileInputStream(sourcePath);
            //目的文件作为输出,这里进行了多层封装
            //上文提到过,回调接口target调用open函数后,将创建并打开目的端文件,然后赋予相应的写权限
            //ParcelFileDescriptor.AutoCloseOutputStream利用文件描述符构造出一个可自动关闭的输出流
            out = new ParcelFileDescriptor.AutoCloseOutputStream(
                    target.open(targetName, ParcelFileDescriptor.MODE_READ_WRITE));
            //进行实际的数据拷贝
            Streams.copy(in, out);
        } finally {
            IoUtils.closeQuietly(out);
            IoUtils.closeQuietly(in);
        }
    }

    至此整个handleStartCopy流程介绍完毕,可以看出当利用adb安装时,handleStartCopy实际上并没有完成什么实际的操作;对于其它方式安装APK时,handleStartCopy才会进行真正的数据拷贝工作。

    整个过程的大致流程如下:

    六、handleReturnCode
    copy过程结束后,将调用InstallParams的handleReturnCode:

    void handleReturnCode() {
        if (mArgs != null) {
            processPendingInstall(mArgs, mRet);
        }
    }
    
    private void processPendingInstall(final InstallArgs args, final int currentStatus) {
        mHandler.post(new Runnable() {
            public void run() {
                mHandler.removeCallbacks(this);
    
                // Result object to be returned
                PackageInstalledInfo res = new PackageInstalledInfo();
                res.setReturnCode(currentStatus);
                res.uid = -1;
                res.pkg = null;
                res.removedInfo = null;
                if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                    //1、安装在终端上的APK,将调用FileInstallArgs的doPreInstall进行处理
                    args.doPreInstall(res.returnCode);
    
                    synchronized (mInstallLock) {
                        //2、调用installPackageTracedLI进行安装
                        installPackageTracedLI(args, res);
                    }
    
                    //3、调用FileInstallArgs的doPostInstall
                    args.doPostInstall(res.returnCode, res.uid);
                }
    
                // A restore should be performed at this point if (a) the install
                // succeeded, (b) the operation is not an update, and (c) the new
                // package has not opted out of backup participation.
                //判断是否需要备份恢复
                final boolean update = res.removedInfo != null
                        && res.removedInfo.removedPackage != null;
                final int flags = (res.pkg == null) ? 0 : res.pkg.applicationInfo.flags;
                boolean doRestore = !update
                        && ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0);
    
                // Set up the post-install work request bookkeeping.  This will be used
                // and cleaned up by the post-install event handling regardless of whether
                // there's a restore pass performed.  Token values are >= 1.
                int token;
                if (mNextInstallToken < 0) mNextInstallToken = 1;
                token = mNextInstallToken++;
    
                PostInstallData data = new PostInstallData(args, res);
                mRunningInstalls.put(token, data);
    
                if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {
                    //调用BackupManager的接口进行恢复工作
                    .......
                }
    
                if (!doRestore) {
                    .......
                    //4、生成一个POST_INSTALL消息,触发后续操作
                    Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
                    mHandler.sendMessage(msg);
                }
            }
        });
    }

    从上面的代码可以看出,handleReturnCode主要做了4件事:
    *调用InstallArgs的doPreInstall函数,对于安装在终端内部的APK而言,将调用FileInstallArgs的doPreInstall函数;
    *调用PKMS的installPackageTracedLI函数进行APK安装;
    *调用InstallArgs的doPostInstall函数;
    *利用结果构造PostInstallData,然后发送POST_INSTALL消息触发后续处理流程

    现在我们分别介绍这几部分工作:
    1、doPreInstall

    int doPreInstall(int status) {
        if (status != PackageManager.INSTALL_SUCCEEDED) {
            cleanUp();
        }
        return status;
    }
    
    private boolean cleanUp() {
        if (codeFile == null || !codeFile.exists()) {
            return false;
        }
    
        removeCodePathLI(codeFile);
    
        if (resourceFile != null && !FileUtils.contains(codeFile, resourceFile)) {
            resourceFile.delete();
        }
    
        return true;
    }

    从代码来看,正常流程下doPreInstall并不会进行实际的工作,只是当handleStartCopy出现问题时,doPreInstall将清理拷贝的文件。

    2、installPackageTracedLI

    private void installPackageTracedLI(InstallArgs args, PackageInstalledInfo res) {
        try {
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackage");
            installPackageLI(args, res);
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    }
    
    private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
        //定义一些变量
        .........
        // Result object to be returned
        res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
    
        //定义parseFlags
        .......
        PackageParser pp = new PackageParser();
        .......
        final PackageParser.Package pkg;
        try {
            //解析APK文件,形成Package对象
            pkg = pp.parsePackage(tmpPackageFile, parseFlags);
        } catch (PackageParserException e) {
            res.setError("Failed parse during installPackageLI", e);
            return;
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    
        / If we are installing a clustered package add results for the children
        if (pkg.childPackages != null) {
            //在需要的情况下,解析Child Package信息
            ........
        }
    
        try {
            // either use what we've been given or parse directly from the APK
            if (args.certificates != null) {
                try {
                    //如果参数中定义了权限信息,就用参数中的权限信息配置Package对象
                    PackageParser.populateCertificates(pkg, args.certificates);
                } catch (PackageParserException e) {
                    // there was something wrong with the certificates we were given;
                    // try to pull them from the APK
                    PackageParser.collectCertificates(pkg, parseFlags);
                }
            } else {
                //否则,就从AndroidManifest.xml文件中解析出权限信息
                PackageParser.collectCertificates(pkg, parseFlags);
            }
        } catch (PackageParserException e) {
            res.setError("Failed collect during installPackageLI", e);
            return;
        }
    
        //当安装重复的APK时,根据权限、签名信息、版本等条件,判断能否进一步操作
        .............
    
        ........
        //根据Package中的信息,修改拷贝文件时,临时赋予的名称
        //此处将利用FileInstallArgs的doRename
        if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
            res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
            return;
        }
        .........
        try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
                "installPackageLI")) {
            if (replace) {
                //用新的package信息替换旧的
                replacePackageLIF(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
                        installerPackageName, res);
            } else {
                //将新的pacakge信息加入到PKMS中
                installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
                        args.user, installerPackageName, volumeUuid, res);
            }
        }
        ..........
    }

    从代码来看,installPackageTracedLI的主要工作就是解析APK文件,形成对应的Package对象;生成对应的权限信息后,根据Package中的信息,更改存储路径对应目录的名称。

    3、doPostInstall

    int doPostInstall(int status, int uid) {
        if (status != PackageManager.INSTALL_SUCCEEDED) {
            cleanUp();
        }
        return status;
    }

    可以看出,FileInstallArgs中定义的doPostInstall函数和doPreInstall函数完全一样,正常流程下不需要进行任何操作;当之前的处理流程出现问题时,利用cleanUp清楚创建的文件和资源。

    4、处理POST_INSTALL消息
    在PackageHandler的doHandleMessage中处理POST_INSTALL消息:

    .....
    case POST_INSTALL: {
        ............
        PostInstallData data = mRunningInstalls.get(msg.arg1);
        final boolean didRestore = (msg.arg2 != 0);
        mRunningInstalls.delete(msg.arg1);
    
        if (data != null) {
            ............
            // Handle the parent package
            handlePackagePostInstall(parentRes, grantPermissions, killApp,
                    grantedPermissions, didRestore, args.installerPackageName,
                    args.observer);
    
            // Handle the child packages
            final int childCount = (parentRes.addedChildPackages != null)
                    ? parentRes.addedChildPackages.size() : 0;
            for (int i = 0; i < childCount; i++) {
                //同样利用handlePackagePostInstall处理child Package
                ........
            }
            ........
        } else {
            .........
        }
    }
    break;
    ......

    我们跟进一下handlePackagePostInstall函数:

    private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions,
            boolean killApp, String[] grantedPermissions,
            boolean launchedForRestore, String installerPackage,
            IPackageInstallObserver2 installObserver) {
        if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
            // Send the removed broadcasts
            if (res.removedInfo != null) {
                res.removedInfo.sendPackageRemovedBroadcasts(killApp);
            }
    
            //调用grantRequestedRuntimePermissions等,赋予Package权限
            ...........
    
            //发送ACTION_PACKAGE_ADDED等广播消息
            ...........
    
            ...........
        }
    
        // If someone is watching installs - notify them
        //将安装的结果通知给Pm.java中观察者
        if (installObserver != null) {
            try {
                Bundle extras = extrasForInstallResult(res);
                installObserver.onPackageInstalled(res.name, res.returnCode,
                        res.returnMsg, extras);
            } catch (RemoteException e) {
                Slog.i(TAG, "Observer no longer exists.");
            }
        }
    }

    从上面的代码来看,处理POST_INSTALL的主要工作其实还是通过广播、回调接口通知系统中的其它组件,有新的Pacakge安装或发生了改变。

    最后整理一下handleReturnCode的流程,如下所示:

    七、总结
    从上面的代码来看,整个APK的安装过程极其琐碎复杂,但核心思想还是比较简单的:就是将待安装的APK文件拷贝到手机的指定位置,然后利用PackageParser来解析出对应的Package对象,最终将Package对象加入到PKMS中。
    整体流程的主干大体如下图所示:

    大图链接

    展开全文
  • apk安装失败笔记

    2017-09-13 19:05:44
    昨天公司刚发了测试机,我拿到一台魅蓝max.刚拆开手机。 今天插上数据线,run....结果模拟器跑了下,安装成功。 拿手机在其他人那跑了下,安装失败。好吧,手机的问题。 那到底是哪出了问题呢

    昨天公司刚发了测试机,我拿到一台魅蓝max.刚拆开手机。
    今天插上数据线,run.结果居然安装失败。提示信息为:INSTALL_FAILED_USER_RESTRICTED。
    顿时懵逼了,刚买的新机,就出现这个问题。无语啊。
    刚开始还以为是Android studio的问题。结果模拟器跑了下,安装成功。
    拿手机在其他人那跑了下,安装失败。好吧,手机的问题。
    那到底是哪出了问题呢?我未知来源的开关也打开了。USB调试也打开了。就是失败。结果,自己碰巧看到魅族手机中有个应用叫手机管家,里面有个USB权限管理,就是它了,是它阻止了程序的安装。

    下面是从网上找到的几种常见的错误及解决方法:
    1、INSTALL_FAILED_INVALID_APK:无效的安装包,安装包已损坏
    请检查安装包是否完整。如果是xpk包,可以通过手动安装xpk来检测一下。如果是apk包,请重新下载。
    2、INSTALL_FAILED_OLDER_SDK:系统版本过低
    当前程序不支持您的手机。
    3、INSTALL_FAILED_INSUFFICIENT_STORAGE:没有足够的存储空间。
    4、INSTALL_FAILED_INVALID_INSTALL_LOCATION:无效的安装位置。
    5、INSTALL_CANCELED_BY_USER:系统禁止安装未知来源的应用。
    这个要在Android系统设置里修改,勾选安全选项里的未知来源,允许安装。
    6、INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES:安装包签名不一致
    这样的问题主要是签名冲突造成的,比如你使用了ADB的debug权限签名,但后来使用标准sign签名后再安装同一个文件会出现这样的错误提示,解决的方法除了只有先老老实实从手机上卸载原有版本再进行安装,而adb install -r参数也无法解决这个问题。
    7、INSTALL_FAILED_INVALID_URI:
    应用为中文名, adb install 中文.apk时出现此问题。修改为英文名就OK了。
    8、INSTALL_FAILED_USER_RESTRICTED:
    手机系统中有应用阻止了安装,比如像魅族手机中的手机管家有监控usb安装apk的权限。关闭此权限,即可。

    附录:adb install 安装错误常见列表
    INSTALL_FAILED_ALREADY_EXISTS 程序已经存在
    INSTALL_FAILED_INVALID_APK 无效的APK
    INSTALL_FAILED_INVALID_URI 无效的链接
    INSTALL_FAILED_INSUFFICIENT_STORAGE 没有足够的存储空间
    INSTALL_FAILED_DUPLICATE_PACKAGE 已存在同名程序
    INSTALL_FAILED_NO_SHARED_USER 要求的共享用户不存在
    INSTALL_FAILED_UPDATE_INCOMPATIBLE 版本不能共存 (解决:Teminal中输入adb uninstall package-name )
    INSTALL_FAILED_SHARED_USER_INCOMPATIBLE 需求的共享用户签名错误
    INSTALL_FAILED_MISSING_SHARED_LIBRARY 需求的共享库已丢失
    INSTALL_FAILED_REPLACE_COULDNT_DELETE 需求的共享库无效
    INSTALL_FAILED_DEXOPT dex优化验证失败
    INSTALL_FAILED_OLDER_SDK 系统版本过旧
    INSTALL_FAILED_CONFLICTING_PROVIDER 存在同名的内容提供者
    INSTALL_FAILED_NEWER_SDK 系统版本过新
    INSTALL_FAILED_TEST_ONLY 调用者不被允许测试的测试程序
    INSTALL_FAILED_CPU_ABI_INCOMPATIBLE 包含的本机代码不兼容
    CPU_ABIINSTALL_FAILED_MISSING_FEATURE 使用了一个无效的特性
    INSTALL_FAILED_CONTAINER_ERROR SD卡访问失败
    INSTALL_FAILED_INVALID_INSTALL_LOCATION 无效的安装路径
    INSTALL_FAILED_MEDIA_UNAVAILABLE SD卡不存在
    INSTALL_FAILED_INTERNAL_ERROR 系统问题导致安装失败
    DEFAULT 未知错误

    展开全文
  • Android7.0 PackageManagerService (3) APK安装

    千次阅读 热门讨论 2016-10-09 10:53:25
    在本篇博客中,我们分析一下Android中的APK是如何安装的,以及PKMS在这个过程中进行了哪些工作。APK安装方式有很多,我们先来看看如何用adb命令进行安装

    在本篇博客中,我们分析一下Android中的APK是如何安装的,以及PKMS在这个过程中进行了哪些工作。

    APK的安装方式有很多,我们先来看看如何用adb命令进行安装。
    我们从adb install开始分析,该命令有多个参数,这里仅考虑最基本的adb install xxxx.apk。

    一、adb命令
    看看system/core/adb/commandline.cpp中的adb_commandline函数:

    int adb_commandline(int argc, const char **argv) {
        ...........
        else if (!strcmp(argv[0], "install")) {
            if (argc < 2) return usage();
            FeatureSet features;
            std::string error;
            if (!adb_get_feature_set(&features, &error)) {
                fprintf(stderr, "error: %s\n", error.c_str());
                return 1;
            }
    
            if (CanUseFeature(features, kFeatureCmd)) {
                //支持FeatureCmd时调用install_app
                return install_app(transport_type, serial, argc, argv);
            }
            //否则,利用install_app_legacy
            return install_app_legacy(transport_type, serial, argc, argv);
        }
        ...........
    }

    1、install_app_legacy
    我看先看看传统的install_app_legacy:

    static int install_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) {
        //待安装的APK目前还在源机器上,现在需要把APK的文件复制到手机里
        //如果安装在手机内部存储,那么目的地址为DATA_DEST
        //如果安装在SD卡上,则目的地址为SD_DEST
        static const char *const DATA_DEST = "/data/local/tmp/%s";
        static const char *const SD_DEST = "/sdcard/tmp/%s";
        .........
        //默认安装到手机内部
        const char* where = DATA_DEST;
        for (i = 1; i < argc; i++) {
            //携带参数-s时,才安装到SD卡
            if (!strcmp(argv[i], "-s")) {
                where = SD_DEST;
            }
        }
    
        //解析参数,判断adb命令中是否携带了有效的apk文件名
        ...........
    
        //取出apk名
        std::vector<const char*> apk_file = {argv[last_apk]};
        //构造apk目的地址
        std::string apk_dest = android::base::StringPrintf(
                where, adb_basename(argv[last_apk]).c_str());
    
        //do_sync_push将此APK文件传输到手机的目标路径,失败的话将跳转到clenaup_apk
        if (!do_sync_push(apk_file, apk_dest.c_str())) goto cleanup_apk;
    
        //执行pm_command
        result = pm_command(transport, serial, argc, argv);
    
    cleanup_apk:
        //删除刚才传输的文件
        //PKMS在安装过程中会将该APK复制一份到/data/app目录下,所有data/local/tmp目录下对应的文件可以删除
        delete_file(transport, serial, apk_dest);
        return result;
    }

    从代码来看,传统的安装方式就是将源机器中的APK文件拷贝到目的手机的tmp目录下,然后调用pm_command进行处理。

    2、install_app
    我们再看看支持FeatureCmd的机器,如何安装APK:

    static int install_app(TransportType transport, const char* serial, int argc, const char** argv) {
        //利用参数创建出本地文件的名称
        const char* file = argv[argc - 1];
    
        //解析参数,判断adb命令中是否携带了有效的apk文件名
        .........
    
        //adb_open中将创建出这个file对应的文件
        int localFd = adb_open(file, O_RDONLY);
        ............
        std::string cmd = "exec:cmd package";
        //添加cmd参数
        ............
        //连接源端,获取源APK文件的描述符
        int remoteFd = adb_connect(cmd, &error);
        ............
        //将remoteFd中的数据写入到localFd
        copy_to_file(localFd, remoteFd);
        //得到结果
        read_status_line(remoteFd, buf, sizeof(buf));
    
        adb_close(localFd);
        adb_close(remoteFd);
        ..........
        return 0;
    }

    从代码来看install_app就是将源机器的文件复制到了目的机器中,并没有进行额外的操作。猜想可能是支持特殊FeatureCmd的机器,PKMS能够监听到这个拷贝,然后触发后续的扫描工作。这个过程没有研究过对应代码,暂时不做深入分析。

    对于传统的安装方式,我们需要继续往下看看pm_command。

    二、pm_command
    我们先看看pm_command函数:

    static int pm_command(TransportType transport, const char* serial, int argc, const char** argv) {
        std::string cmd = "pm";
    
        //构造pm cmd
        while (argc-- > 0) {
            cmd += " " + escape_arg(*argv++);
        }
    
        //发送shell命令给adbd
        return send_shell_command(transport, serial, cmd, false);
    }

    我们跟进下send_shell_command:

    // Connects to the device "shell" service with |command| and prints the
    // resulting output.
    static int send_shell_command(TransportType transport_type, const char* serial,
                                  const std::string& command,
                                  bool disable_shell_protocol,
                                  std::string* output=nullptr,
                                  std::string* err=nullptr) {
        ...........
        while (true) {
            bool attempt_connection = true;
    
            // Use shell protocol if it's supported and the caller doesn't explicitly disable it.
            if (!disable_shell_protocol) {
                .......
                if (adb_get_feature_set(&features, &error)) {
                    //如果定义了feature,则替换shell protocol
                    use_shell_protocol = CanUseFeature(features, kFeatureShell2);
                } else {
                    // Device was unreachable.
                    attempt_connection = false;
                }
            }
    
            if (attempt_connection) {
                std::string error;
                //此时command中携带的就是以pm开头的命令
                std::string service_string = ShellServiceString(use_shell_protocol, "", command);
    
                //向shell服务发送命令
                fd = adb_connect(service_string, &error);
                if (fd >= 0) {
                    break;
                }
            }
            ............
        }
    
        //读取返回结果
        int exit_code = read_and_dump(fd, use_shell_protocol, output, err);
        if (adb_close(fd) < 0) {
            ..........
        }
    
        return int exit_code;
    }

    从上面的代码来看,pm_command就是向shell服务发送pm命令。

    pm是一个可执行脚本,我们在终端上调用adb shell,然后执行pm,可以得到以下结果:

    root:/ # pm
    usage: pm list packages [-f] [-d] [-e] [-s] [-3] [-i] [-u] [--user USER_ID] [FILTER]
           pm list permission-groups
           pm list permissions [-g] [-f] [-d] [-u] [GROUP]
           pm list instrumentation [-f] [TARGET-PACKAGE]
    ..........

    pm脚本定义在frameworks/base/cmds/pm中:

    base=/system
    export CLASSPATH=$base/framework/pm.jar
    exec app_process $base/bin com.android.commands.pm.Pm "$@"

    在编译system.img时,会根据Android.mk将该脚本复制到system/bin目录下。
    从脚本的内容来看,当调用pm时,将向app_process目录的main函数传入Pm对应的参数:

    我们看看对应的定义于app_main.cpp的main函数(前面的博客分析过,这个其实也是zygote启动的函数):

    //app_process的main函数
    int main(int argc, char* const argv[]) {
        ........
        //解析参数
        while (i < argc) {
            const char* arg = argv[i++];
            if (strcmp(arg, "--zygote") == 0) {
                zygote = true;
                niceName = ZYGOTE_NICE_NAME;
            } else if (strcmp(arg, "--start-system-server") == 0) {
                startSystemServer = true;
            } else if (strcmp(arg, "--application") == 0) {
                application = true;
            } else if (strncmp(arg, "--nice-name=", 12) == 0) {
                niceName.setTo(arg + 12);
            } else if (strncmp(arg, "--", 2) != 0) {
                //此时我们有参数,进入该分支设置className
                className.setTo(arg);
                break;
            } else {
                --i;
                break;
            }
        }
        ...........
        if (zygote) {
            runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
        } else if (className) {
            //此时不再是启动zygote,而是启动className对应的类
            runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
        } else {
            .........
        }
        ........
    }

    我们跟进AndroidRuntime.cpp的start函数:

    void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
    {
        ..........
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
                "([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
        } else {
            //反射调用main函数,从native层进入java世界
            env->CallStaticVoidMethod(startClass, startMeth, strArray);
        }
        .........
    }

    于是流程会进入到RuntimeInit的main函数:

    public static final void main(String[] argv) {
        ........
        //进行一些常规的初始化工作
        commonInit();
        /*
        * Now that we're running in interpreted code, call back into native code
        * to run the system.
        */
        nativeFinishInit();
        .........
    }

    native函数定义在framework/base/core/jni/AndroidRuntime.cpp中,对应的函数为:

    static void com_android_internal_os_RuntimeInit_nativeFinishInit(JNIEnv* env, jobject clazz)
    {
        //gCurRuntime保存AndroidRuntime,实际上是AndroidRuntime的子类
        gCurRuntime->onStarted();
    }

    App_main.cpp中定义的AppRuntime继承AndroidRuntime,实现了onStarted函数:

    virtual void onStarted()
    {
        //binder通信相关的
        sp<ProcessState> proc = ProcessState::self();
        ALOGV("App process: starting thread pool.\n");
        proc->startThreadPool();
    
        AndroidRuntime* ar = AndroidRuntime::getRuntime();
        //调用AndroidRuntime.cpp的callMain函数,参数与Pm.java相关
        ar->callMain(mClassName, mClass, mArgs);
    
        IPCThreadState::self()->stopProcess();
    }
    status_t AndroidRuntime::callMain(const String8& className, jclass clazz,
            const Vector<String8>& args) {
        ..........
        env = getJNIEnv();
        ..........
        methodId = env->GetStaticMethodID(clazz, "main", "([Ljava/lang/String;)V");
        ..........
        const size_t numArgs = args.size();
        stringClass = env->FindClass("java/lang/String");
        strArray = env->NewObjectArray(numArgs, stringClass, NULL);
    
        for (size_t i = 0; i < numArgs; i++) {
            jstring argStr = env->NewStringUTF(args[i].string());
            env->SetObjectArrayElement(strArray, i, argStr);
        }
        ...........
        //最终调用了Pm.java的main函数
        env->CallStaticVoidMethod(clazz, methodId, strArray);
        return NO_ERROR;
    }

    这里自己初次看时,认为这里没有fork新的进程,那么APK安装运行在zygote进程中。
    实际上这是一个错误的理解,说明自己的理解还不到位。
    init创建zygote进程时,是fork出一个子进程,然后才调用app_main中的函数,此时整个zygote严格来讲只是一个native进程;当app_main函数最终通过AndroidRuntime等反射调用zygoteInit.java的main函数后,才演变成了Java层的zygote进程。
    这里的情况是类似的,adb进程发送消息给Shell服务,Shell服务执行Pm脚本,由于exec函数并未创建出新的进程,因此调用app_main后整个代码仍然是运行在Shell服务对应的native进程中,同样通过反射后演变为Java层中的进程。

    这里自己花了很多的笔墨来分析如何从执行脚本文件,到启动Java进程。
    主要是弄懂这个机制后,我们实际上完全可以学习pm的写法,依葫芦画瓢写一个脚本文件,然后定义对应的Java文件。
    通过脚本命令,来让Java层的进程提供服务。

    最后,我们通过一个图来总结一下这个过程:

    三、Pm中的流程
    现在我们进入了Pm.java的main函数:

    public static void main(String[] args) {
        int exitCode = 1;
        try {
            //别被写法欺骗了,Pm并没有继承Runnable
            exitCode = new Pm().run(args);
        } catch (Exception e) {
            .......
        }
        System.exit(exitCode);
    }
    
    //根据参数进行对应的操作,现在我们仅关注APK安装
    public int run(String[] args) throws RemoteException {
        ...........
        //利用Binder通信,得到PKMS服务端代理
        mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
    
        //保存参数
        mArgs = args;
        String op = args[0];
        mNextArg = 1;
    
        ............
        //返回PKMS中保存的PackageInstallerService
        mInstaller = mPm.getPackageInstaller();
        ........
        if ("install".equals(op)) {
            //安装APK将调用runInstall
            return runInstall();
        }
        .......
    }

    我们跟进runInstall函数:

    private int runInstall() throws RemoteException {
        //根据参数创建InstallParams,其中包含了SessionParams,标志为MODE_FULL_INSTALL
        final InstallParams params = makeInstallParams();
        //1 创建Session
        final int sessionId = doCreateSession(params.sessionParams,
                params.installerPackageName, params.userId);
        try {
            //inPath对应于安装的APK文件
            final String inPath = nextArg();
            .......
            //2 wirite session
            if (doWriteSession(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk",
                    false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
                return 1;
            }
            //3 commit session
            if (doCommitSession(sessionId, false /*logSuccess*/)
                    != PackageInstaller.STATUS_SUCCESS) {
                return 1;
            }
            System.out.println("Success");
            return 0;
        } finally {
            ........
        }
    }

    从上面的代码来看,runInstall主要进行了三件事,即创建session、对session进行写操作,最后提交session。
    接下来,我们来看看每一步究竟在干些什么:

    1、 create session

    private int doCreateSession(SessionParams params, String installerPackageName, int userId)
            throws RemoteException {
        //通过ActivityManagerService得到"runInstallCreate"(作为Context对应的字符串)对应的uid
        userId = translateUserId(userId, "runInstallCreate");
        if (userId == UserHandle.USER_ALL) {
            userId = UserHandle.USER_SYSTEM;
            params.installFlags |= PackageManager.INSTALL_ALL_USERS;
        }
    
        //通过PackageInstallerService创建session
        final int sessionId = mInstaller.createSession(params, installerPackageName, userId);
        return sessionId;
    }

    跟进一下PackageInstallerService的createSession函数:

    @Override
    public int createSession(SessionParams params, String installerPackageName, int userId) {
        try {
            return createSessionInternal(params, installerPackageName, userId);
        } catch (IOException e) {
            throw ExceptionUtils.wrap(e);
        }
    }
    
    private int createSessionInternal(SessionParams params, String installerPackageName, int userId)
            throws IOException {
        //安装权限检查
        .......
        //修改SessionParams的installFlags
        if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
            params.installFlags |= PackageManager.INSTALL_FROM_ADB;
        } else {
            .........
        }
        ..........
        // Defensively resize giant app icons
        //调整app图标大小,这里应该是不同安装方式共用的代码
        //通过adb安装apk时,应该还没有解析到app图标
        if (params.appIcon != null) {
            ........
        }
    
        //根据SessionParams的installFlags进行一些操作
        ..........
        } else {
            // For now, installs to adopted media are treated as internal from
            // an install flag point-of-view.
            //adb安装应该进入这个分支(不添加参数指定安装在sd card时),为SessionParams设置InstallInternal Flag,后文会用到
            params.setInstallFlagsInternal();
            ...........
        }
    
        final int sessionId;
        final PackageInstallerSession session;
        synchronized (mSessions) {
            // Sanity check that installer isn't going crazy
            //确保同一个uid没有提交过多的Session,MAX_ACTIVE_SESSIONS为1024
            final int activeCount = getSessionCount(mSessions, callingUid);
            if (activeCount >= MAX_ACTIVE_SESSIONS) {
                throw new IllegalStateException(
                        "Too many active sessions for UID " + callingUid);
            }
    
            //同样确保同一个uid没有提交过多的Session,MAX_HISTORICAL_SESSIONS为1048576
            final int historicalCount = getSessionCount(mHistoricalSessions, callingUid);
            if (historicalCount >= MAX_HISTORICAL_SESSIONS) {
                throw new IllegalStateException(
                        "Too many historical sessions for UID " + callingUid);
            }
            ........
            //sessionId是个随机值
            sessionId = allocateSessionIdLocked();
    
            // We're staging to exactly one location
            File stageDir = null;
            String stageCid = null;
            //根据installFlags,决定安装目录,前文已经提到,过默认将安装到internal目录下
            if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
                final boolean isEphemeral =
                        (params.installFlags & PackageManager.INSTALL_EPHEMERAL) != 0;
                //此处将会在临时性的data目录下创建出file,应该是作为copy的目的地址
                stageDir = buildStageDir(params.volumeUuid, sessionId, isEphemeral);
            } else {
                stageCid = buildExternalStageCid(sessionId);
            }
    
            session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
                    mInstallThread.getLooper(), sessionId, userId, installerPackageName, callingUid,
                    params, createdMillis, stageDir, stageCid, false, false);
                    mSessions.put(sessionId, session);
            mSessions.put(sessionId, session);
        }
        //进行回调
        mCallbacks.notifySessionCreated(session.sessionId, session.userId);
        //在mSessionsFile中进行记录
        writeSessionsAsync();
    
        return sessionId;
    }

    从代码来看,上述代码的目的就是为APK安装做好准备工作,例如权限检查、目的临时文件的创建等, 最终创建出PackageInstallerSession对象。PackageInstallerSession可以看做是”安装APK”这个请求的封装,其中包含了处理这个请求需要的一些信息。
    这种设计方式,大致可以按照命令模式来理解。

    实际上PackageInstallerSession不仅是分装请求的对象,其自身还是个服务端:

    public class PackageInstallerSession extends IPackageInstallerSession.Stub 

    2、write session
    创建出PackageInstallerSession后,我们看看Pm.java中的doWriteSession函数:

     private int doWriteSession(int sessionId, String inPath, long sizeBytes, String splitName,
            boolean logSuccess) throws RemoteException {
        if ("-".equals(inPath)) {
            inPath = null;
        } else if (inPath != null) {
            //此时file指向了待安装的APK文件(adb执行拷贝后的目的地址)
            final File file = new File(inPath);
            if (file.isFile()) {
                sizeBytes = file.length();
            }
        }
        ......
        //取出PackageInstallerSession中的SessionInfo
        final SessionInfo info = mInstaller.getSessionInfo(sessionId);
    
        PackageInstaller.Session session = null;
        InputStream in = null;
        OutputStream out = null;
        try {
            //1 获取PackageInstallerSession的调用接口
            session = new PackageInstaller.Session(
                    mInstaller.openSession(sessionId));
    
            if (inPath != null) {
                //定义输入端,待安装APK对应文件的源地址
                in = new FileInputStream(inPath);
            } else {
                in = new SizedInputStream(System.in, sizeBytes);
            }
    
            //2 定义输出端,对应拷贝后的目的地址
            out = session.openWrite(splitName, 0, sizeBytes);
    
            int total = 0;
            byte[] buffer = new byte[65536];
            int c;
            //进行文件的拷贝
            while ((c = in.read(buffer)) != -1) {
                total += c;
                out.write(buffer, 0, c);
    
                if (info.sizeBytes > 0) {
                    final float fraction = ((float) c / (float) info.sizeBytes);
                    //只是更新进度而已
                    session.addProgress(fraction);
                }
            }
            session.fsync(out);
            ......
            return PackageInstaller.STATUS_SUCCESS;
        } catch (IOException e) {
            ........
        } finally {
            IoUtils.closeQuietly(out);
            IoUtils.closeQuietly(in);
            IoUtils.closeQuietly(session);
        }
    }

    从doWriteSession的代码来看,此处进行的主要工作就是通过Session将源端的数据拷贝到目的端。
    其实从整个对象的命名和执行过程来看,这里整个是基于C/S架构的通信过程,Pm作为PackageInstallerService 的客户端,利用PackageInstallerSession来封装每一次完整的通信过程。

    2.1 得到PackageInstallerSession的代理对象
    我们看看上面代码调用的PackageInstaller.Session的构造函数:

    //参数传入的是PackageInstallerService.openSession函数的返回结果,即实际PackageInstallerSession的代理端
    public Session(IPackageInstallerSession session) {
        mSession = session;
    }

    我们看看PackageInstallerService.openSession函数:

    @Override
    public IPackageInstallerSession openSession(int sessionId) {
        try {
            return openSessionInternal(sessionId);
        } catch (IOException e) {
            throw ExceptionUtils.wrap(e);
        }
    }
    
    private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException {
        synchronized (mSessions) {
            //根据sessionId得到之前创建的PackageInstallerSession
            final PackageInstallerSession session = mSessions.get(sessionId);
            if (session == null || !isCallingUidOwner(session)) {
                throw new SecurityException("Caller has no access to session " + sessionId);
            }
            //调用其open函数
            session.open();
    
            //PacakgeInstallerSession转化为IPackageInstallerSession返回
            return session;
        }
    }
    
    //open函数就是准备好待拷贝的目录
    public void open() throws IOException {
        .......
            //PackageInstallerService创建出PackageInstallerSession时,传入的prepared参数为false
            if (!mPrepared) {
                if (stageDir != null) {
                    prepareStageDir(stageDir);
                } else if (stageCid != null) {
                    prepareExternalStageCid(stageCid, params.sizeBytes);
                    .....
                } else {
                    //throw exception
                    ......
                }
                ........
            }
        }
    }

    2.2 得到客户端
    PacakgeInstaller.Session的openWrite函数:

    public @NonNull OutputStream openWrite(@NonNull String name, long offsetBytes,
            long lengthBytes) throws IOException {
        try {
            //mSession是PacakgeInstallerSession,这里发生了Binder通信
            final ParcelFileDescriptor clientSocket = mSession.openWrite(name,
                offsetBytes, lengthBytes);
            //引入了FileBridge对象,后文分析
            return new FileBridge.FileBridgeOutputStream(clientSocket);
        } catch (RuntimeException e) {
            ExceptionUtils.maybeUnwrapIOException(e);
            throw e;
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    我们看看PacakgeInstallerSession的openWrite函数:

    @Override
    public ParcelFileDescriptor openWrite(String name, long offsetBytes, long lengthBytes) {
        try {
            return openWriteInternal(name, offsetBytes, lengthBytes);
        } catch (IOException e) {
            throw ExceptionUtils.wrap(e);
        }
    }
    
    private ParcelFileDescriptor openWriteInternal(String name, long offsetBytes, long lengthBytes)
        throws IOException {
        // Quick sanity check of state, and allocate a pipe for ourselves. We
        // then do heavy disk allocation outside the lock, but this open pipe
        // will block any attempted install transitions.
        //FileBrige建立了客户端和服务端的管道
        final FileBridge bridge;
        synchronized (mLock) {
            ......
            bridge = new FileBridge();
            mBridges.add(bridge);
        }
    
        try {
            // Use installer provided name for now; we always rename later
            if (!FileUtils.isValidExtFilename(name)) {
                throw new IllegalArgumentException("Invalid name: " + name);
            }
    
            //打开文件,定义权限
            final File target = new File(resolveStageDir(), name);
            // holding open FDs into containers.
            final FileDescriptor targetFd = Libcore.os.open(target.getAbsolutePath(),
                    O_CREAT | O_WRONLY, 0644);
            Os.chmod(target.getAbsolutePath(), 0644);
    
            //定义文件内存格式及分配内存
            // If caller specified a total length, allocate it for them. Free up
            // cache space to grow, if needed.
            if (lengthBytes > 0) {
                final StructStat stat = Libcore.os.fstat(targetFd);
                final long deltaBytes = lengthBytes - stat.st_size;
                // Only need to free up space when writing to internal stage
                if (stageDir != null && deltaBytes > 0) {
                    mPm.freeStorage(params.volumeUuid, deltaBytes);
                }
                Libcore.os.posix_fallocate(targetFd, 0, lengthBytes);
            }
    
            //定义起始偏移量
            if (offsetBytes > 0) {
                Libcore.os.lseek(targetFd, offsetBytes, OsConstants.SEEK_SET);
            }
    
            bridge.setTargetFile(targetFd);
            bridge.start();
    
            //返回了bridge的client socket
            return new ParcelFileDescriptor(bridge.getClientSocket());
        } catch (ErrnoException e) {
            throw e.rethrowAsIOException();
        }
    }

    2.2.1 FileBridge
    为了更好的理解上述过程,我们需要看看FileBridge:

    public class FileBridge extends Thread {
        .......
        private final FileDescriptor mServer = new FileDescriptor();
        private final FileDescriptor mClient = new FileDescriptor();
        .......
        public FileBridge() {
            try {
                //构造函数建立的mServer和mClient之间的管道
                Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mServer, mClient);
            } catch (ErrnoException e) {
                throw new RuntimeException("Failed to create bridge");
            }
        }
        .......
        public void setTargetFile(FileDescriptor target) {
            mTarget = target;
        }
    
        public FileDescriptor getClientSocket() {
            return mClient;
        }
    
        @Override
        public void run() {
            final byte[] temp = new byte[8192];
            try {
                //取出mServer中的数据,并进行处理
                //注意mSever和mClient通道绑定,于是读出的数据是mClient写入的,向mServer写数据也会递交给mClient
                while (IoBridge.read(mServer, temp, 0, MSG_LENGTH) == MSG_LENGTH) {
                    final int cmd = Memory.peekInt(temp, 0, ByteOrder.BIG_ENDIAN);
                    if (cmd == CMD_WRITE) {
                        // Shuttle data into local file
                        int len = Memory.peekInt(temp, 4, ByteOrder.BIG_ENDIAN);
                        while (len > 0) {
                            int n = IoBridge.read(mServer, temp, 0, Math.min(temp.length, len));
                            .......
                            IoBridge.write(mTarget, temp, 0, n);
                            len -= n;
                        }
                    } else if (cmd == CMD_FSYNC) {
                        // Sync and echo back to confirm
                        Os.fsync(mTarget);
                        IoBridge.write(mServer, temp, 0, MSG_LENGTH);
                    } else if (cmd == CMD_CLOSE) {
                        // Close and echo back to confirm
                        Os.fsync(mTarget);
                        Os.close(mTarget);
                        mClosed = true;
                        IoBridge.write(mServer, temp, 0, MSG_LENGTH);
                        break;
                    }
                }
            } catch (ErrnoException | IOException e) {
                ........
            } finally {
                forceClose();
            }
    }

    通过调用PackageInstallerSession的openWrite函数,Pm将得到与PackageInstallerSession通信的client端,同时PackageInstallerSession启动FileBridge准备接收数据。

    在上文中进行文件拷贝时,最终就是利用FileBridge的管道来完成实际的工作。

    3、 commit session
    根据上面的代码,我们知道doWriteSession结束后,如果没有出现任何错误,那么APK源文件已经copy到目的地址了。
    接下来我们看看doCommitSession进行的工作。

    private int doCommitSession(int sessionId, boolean logSuccess) throws RemoteException {
        PackageInstaller.Session session = null;
        try {
            session = new PackageInstaller.Session(
                    mInstaller.openSession(sessionId));
    
            //receiver用于接收结果
            final LocalIntentReceiver receiver = new LocalIntentReceiver();
            //提交session
            session.commit(receiver.getIntentSender());
            .....
        } finally {
            IoUtils.closeQuietly(session);
        }
    }

    PackageInstaller.Session中的commit函数,将通过Binder通信调用PackageInstallerSession的commit函数:

    public void commit(@NonNull IntentSender statusReceiver) {
        try {
            mSession.commit(statusReceiver);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

    我们跟进PackageInstallerSession的commit函数:

    @Override
    public void commit(IntentSender statusReceiver) {
        .......
        final boolean wasSealed;
        synchronized (mLock) {
            //初始时mSealed为false
            wasSealed = mSealed;
            if (!mSealed) {
                // Verify that all writers are hands-off
                //前面的doWriteSession传输数据的结尾,会关闭bridge
                for (FileBridge bridge : mBridges) {
                    if (!bridge.isClosed()) {
                        throw new SecurityException("Files still open");
                    }
                }
                mSealed = true;
            }
        }
        ............
        final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,
                statusReceiver, sessionId, mIsInstallerDeviceOwner, userId);
        mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
    }

    PackageInstallerSession被创建时,指定了mHandler对应callback:

    private final Handler.Callback mHandlerCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message msg) {
            synchronized (mLock) {
                if (msg.obj != null) {
                    //其实就是存储Pm.java中的结果接收器
                    mRemoteObserver = (IPackageInstallObserver2) msg.obj;
                }
    
                try {
                    //因此,commit发送消息后,最终将触发commitLocked
                    commitLocked();
                } catch (PackageManagerException e) {
                    .......
                }
    
                return true;
            }
        }
    };
    
    private void commitLocked() throws PackageManagerException {
        .......
        try {
            //解析安装地址,即前文APK文件copy后的目的地址
            resolveStageDir();
        } catch (IOException e) {
            ........
        }
    
        // Verify that stage looks sane with respect to existing application.
        // This currently only ensures packageName, versionCode, and certificate
        // consistency.
        //将利用PKMS检查APK文件是否满足要求,主要是保证各个文件是否具有一致性
        validateInstallLocked();
    
        //检查权限等
        ........
    
        if (stageCid != null) {
            // Figure out the final installed size and resize the container once
            // and for all. Internally the parser handles straddling between two
            // locations when inheriting.
            final long finalSize = calculateInstalledSize();
            resizeContainer(stageCid, finalSize);
        }
    
        // Inherit any packages and native libraries from existing install that
        // haven't been overridden.
        if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
            //如果新的APK文件继承某些已安装的Pacakge,此处将copy需要的native库文件等
            .........
        }
    
        ......
        // Unpack native libraries
        //解压缩native库文件
        extractNativeLibraries(mResolvedStageDir, params.abiOverride);
    
        // Container is ready to go, let's seal it up!
        if (stageCid != null) {
            //针对安装在sdcard的操作,根据uid、gid调用fixSdPermissions
            finalizeAndFixContainer(stageCid);
        }
    
        .........
        //调用PKMS的installStage,进入安装的下一步操作
        mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
                installerPackageName, installerUid, user, mCertificates);
    }

    代码看到这里,我们终于明白了APK安装过程中,Pm.java进行的操作其实就是将adb拷贝的文件,拷贝到系统内或sdcard的目录中,然后进行初步的权限检查等工作,最后通知PKMS进入Install Stage。
    整个代码引入了PackageInstallerSession,个人认为这里涉及了Binder通信、类似于Java网络通信的架构及命令模式,写的非常巧妙,有值得学习和模仿的地方。

    我们同样用一张图来为这一部分做个总结:

    大图链接

    四、installStage
    几经波折,APK的安装流程终于进入到了PKMS,我们看看installStage函数:

    void installStage(String packageName, File stagedDir, String stagedCid,
            IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
            String installerPackageName, int installerUid, UserHandle user,
            Certificate[][] certificates) {
        ............
        //verificationInfo主要用于存储权限验证需要的信息
        final VerificationInfo verificationInfo = new VerificationInfo(
                sessionParams.originatingUri, sessionParams.referrerUri,
                sessionParams.originatingUid, installerUid);
    
        //origin中主要存储的APK文件的路径信息
        final OriginInfo origin;
        if (stagedDir != null) {
            origin = OriginInfo.fromStagedFile(stagedDir);
        } else {
            origin = OriginInfo.fromStagedContainer(stagedCid);
        }
    
        final Message msg = mHandler.obtainMessage(INIT_COPY);
        //准备安装所需的参数
        final InstallParams params = new InstallParams(origin, null, observer,
                sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
                verificationInfo, user, sessionParams.abiOverride,
                sessionParams.grantedRuntimePermissions, certificates);
        .........
        msg.obj = params;
        .........
        //发送INIT_COPY消息,驱动处理流程
        mHandler.sendMessage(msg);
    }

    PKMS中实际的消息处理函数为doHandleMessage:

    void doHandleMessage(Message msg) {
        switch (msg.what) {
            case INIT_COPY: {
                //这里取出的其实就是InstallParams,其继承HandlerParams
                HandlerParams params = (HandlerParams) msg.obj;
                //idx为当前等待处理处理的安装请求的个数
                int idx = mPendingInstalls.size();
                ............
                // If a bind was already initiated we dont really
                // need to do anything. The pending install
                // will be processed later on.
                //初始时,mBound的值为false
                if (!mBound) {
                    ............
                    // If this is the only one pending we might
                    // have to bind to the service again.
                    //连接实际的安装服务,后文介绍
                    if (!connectToService()) {
                        ..................
                    } else {
                        // Once we bind to the service, the first
                        // pending request will be processed.
                        //绑定服务成功后,将新的请求加入到mPendingIntalls中,等待处理
                        mPendingInstalls.add(idx, params);
                    }
                } else {
                    //如果之前已经绑定过服务,同样将新的请求加入到mPendingIntalls中,等待处理
                    mPendingInstalls.add(idx, params);
                    // Already bound to the service. Just make
                    // sure we trigger off processing the first request.
                    if (idx == 0) {
                        //如果是第一个请求,则直接发送事件MCS_BOUND,触发处理流程
                        mHandler.sendEmptyMessage(MCS_BOUND);
                    }
                }
                break;
            }
        }
    }

    上面代码的处理逻辑实际上是比较简单的,我们就看看connectToService的操作,来寻找一下实际进行安装工作的服务:

    private boolean connectToService() {
        ........
        //Component的包名为"com.android.defcontainer";类名为"com.android.defcontainer.DefaultContainerService"
        Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
        Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
        if (mContext.bindServiceAsUser(service, mDefContainerConn,
                Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
            Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
            mBound = true;
            return true;
        }
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
        return false;
    }

    从代码可以看出实际进行安装工作的服务是DefaultContainerService,当绑定服务成功后:

    class DefaultContainerConnection implements ServiceConnection {
        public void onServiceConnected(ComponentName name, IBinder service) {
            ........
            //获得与服务端通信的代理对象
            IMediaContainerService imcs =
                    IMediaContainerService.Stub.asInterface(service);
            //发送MCS_BOUND消息触发流程
            mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));
        }
        .......
    }

    现在我们知道了当服务绑定成功后,也会发送MCS_BOUND消息触发接下来的流程。

    MCS_BOUND对应的处理流程同样定义于doHandleMessage中:

    void doHandleMessage(Message msg) {
        .......
        case MCS_BOUND: {
            ........
            if (msg.obj != null) {
                mContainerService = (IMediaContainerService) msg.obj;
                .......
            }
            if (mContainerService == null) {
                if (!mBound) {
                    // Something seriously wrong since we are not bound and we are not
                    // waiting for connection. Bail out.
                    ............            
                } else {
                    Slog.w(TAG, "Waiting to connect to media container service");
                }
            } else if (mPendingInstalls.size() > 0) {
                HandlerParams params = mPendingInstalls.get(0);
                if (params != null) {
                    ........
                    //调用参数的startCopy函数
                    if (params.startCopy()) {
                        ........
                        // Delete pending install
                        if (mPendingInstalls.size() > 0) {
                            mPendingInstalls.remove(0);
                        }
                        if (mPendingInstalls.size() == 0) {
                            if (mBound) {
                                ..........
                                removeMessages(MCS_UNBIND);
                                Message ubmsg = obtainMessage(MCS_UNBIND);
                                // Unbind after a little delay, to avoid
                                // continual thrashing.
                                sendMessageDelayed(ubmsg, 10000);
                            }
                        } else {
                            // There are more pending requests in queue.
                            // Just post MCS_BOUND message to trigger processing
                            // of next pending install.
                            ......
                            mHandler.sendEmptyMessage(MCS_BOUND);
                        }
                    }
                    .........
                }
            } else {
                // Should never happen ideally.
                Slog.w(TAG, "Empty queue");
            }
            break;
        }
    .......
    }

    这一段代码写的非常清晰,就是处理完一个安装请求后,接着处理下一个;如果队列为空,则等待一段时间后,发送MCS_UNBIND消息断开与安装服务的绑定。

    顺着流程,我们现在看看HandlerParams的startCopy函数:

    final boolean startCopy() {
        boolean res;
        try {
            ........
            //处理安装失败,MAX_RETRIES = 4
            if (++mRetries > MAX_RETRIES) {
                .........
                mHandler.sendEmptyMessage(MCS_GIVE_UP);
                handleServiceError();
                return false;
            } else {
                //先调用handleStartCopy进行实际的copy工作
                handleStartCopy();
                res = true;
            }
        } catch (RemoteException e) {
            if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
            mHandler.sendEmptyMessage(MCS_RECONNECT);
            res = false;
        }
        //然后根据结果做相应处理
        handleReturnCode();
        return res;
    }


    如上图所示,从这段代码来看,PKMS将先后调用handleStartCopy和handleReturnCode来完成主要的工作。接下来,我们分别介绍一下这两个函数的工作流程。

    五、handleStartCopy
    HandlerParams为PKMS的内部抽象类,上面代码中的实际处理函数由其子类InstallParams来实现,我们看看与实际安装相关的handleStartCopy函数:

    public void handleStartCopy() throws RemoteException {
        int ret = PackageManager.INSTALL_SUCCEEDED;
    
        // If we're already staged, we've firmly committed to an install location
        //根据参数决定是安装在手机内还是sdcard中,设置对应标志位
        if (origin.staged) {
            if (origin.file != null) {
                installFlags |= PackageManager.INSTALL_INTERNAL;
                installFlags &= ~PackageManager.INSTALL_EXTERNAL;
            } else if (origin.cid != null) {
                installFlags |= PackageManager.INSTALL_EXTERNAL;
                installFlags &= ~PackageManager.INSTALL_INTERNAL;
            } else {
                throw new IllegalStateException("Invalid stage location");
            }
        }
    
        final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
        final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
        final boolean ephemeral = (installFlags & PackageManager.INSTALL_EPHEMERAL) != 0;
        PackageInfoLite pkgLite = null;
    
        //检查APK的安装位置是否正确
        if (onInt && onSd) {
            // Check if both bits are set.
            ...........
            //APK不能同时安装在内部存储空间和SD card上
            ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
        } else if (onSd && ephemeral) {
            .......
            //APK不能短暂地安装在SD card上
            ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
        }  else {
            //1、利用ContainerService获取PackageInfoLite,应该判断了能否进行安装
            pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,
                    packageAbiOverride);
            .........
    
            /*
            * If we have too little free space, try to free cache
            * before giving up.
            */
            //对于安装在SD card上的APK,当存储空间过小导致安装失败时
            if (!origin.staged && pkgLite.recommendedInstallLocation
                    == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
                final StorageManager storage = StorageManager.from(mContext);
                //利用StroageManager得到设备内部存储空间允许的最小余量
                final long lowThreshold = storage.getStorageLowBytes(
                        Environment.getDataDirectory());
    
                //利用ContainerService得到安装APK的大小
                final long sizeBytes = mContainerService.calculateInstalledSize(
                        origin.resolvedPath, isForwardLocked(), packageAbiOverride);
                try {
                    //利用Installer释放缓存,试图将缓存释放到大于等于(最小余量与APK大小之和)
                    mInstaller.freeCache(null, sizeBytes + lowThreshold);
                    //再次试图得到PackageInfoLite,判断是否满足安装条件
                    pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,
                            installFlags, packageAbiOverride);
                } catch (InstallerException e) {
                    Slog.w(TAG, "Failed to free cache", e);
                }
    
                /*
                * The cache free must have deleted the file we
                * downloaded to install.
                *
                * TODO: fix the "freeCache" call to not delete
                *       the file we care about.
                */
                if (pkgLite.recommendedInstallLocation
                        == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
                    //试图释放cache还是无法安装,只能设置标志位为失败
                    pkgLite.recommendedInstallLocation
                            = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
                }
            }
        }
    
        if (ret == PackageManager.INSTALL_SUCCEEDED) {
            //recommendedInstallLocation中记录了安装的路径信息,即APK保存在终端内部还是Sd card中,此外也可以记录安装失败的信息
            int loc = pkgLite.recommendedInstallLocation;
            if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
                ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
            } else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) {
                ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
            } else if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
                ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
            } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) {
                ret = PackageManager.INSTALL_FAILED_INVALID_APK;
            } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
                ret = PackageManager.INSTALL_FAILED_INVALID_URI;
            } else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) {
                ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
            } else {
                // Override with defaults if needed.
                //2、installLocationPolicy主要判断终端上是否有已经安装过该APK,同一个APK一般只能用新版的替换旧版
                loc = installLocationPolicy(pkgLite);
                //根据loc调整installFlag
                ......
            }
        }
    
        //3、createInstallArgs用于创建一个安装参数对象
        final InstallArgs args = createInstallArgs(this);
        mArgs = args;
        if (ret == PackageManager.INSTALL_SUCCEEDED) {
            // Apps installed for "all" users use the device owner to verify the app
            UserHandle verifierUser = getUser();
            if (verifierUser == UserHandle.ALL) {
                verifierUser = UserHandle.SYSTEM;
            }
            /*
            * Determine if we have any installed package verifiers. If we
            * do, then we'll defer to them to verify the packages.
            */
            final int requiredUid = mRequiredVerifierPackage == null ? -1
                    : getPackageUid(mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
                            verifierUser.getIdentifier());
            if (!origin.existing && requiredUid != -1
                    && isVerificationEnabled(verifierUser.getIdentifier(), installFlags)) {
                //如果存在Package检查者,同时满足启动检查的条件,那么将利用Pacakge检查者来检查安装包
                //其实就是构造一个intent,action为"android.intent.action.PACKAGE_NEEDS_VERIFICATION"
                //在intent中添加需要多信息后,发送给接收者处理
                .........
            } else {
                //4、调用安装参数对象的copyApk函数
                ret = args.copyApk(mContainerService, true);
            }
        }
        mRet = ret;
    }

    handleStartCopy函数整体来看还是比较复杂的,内容比较多,我们需要分4步介绍其中主要的内容。

    1、getMinimalPackageInfo
    getMinimalPackageInfo实际定义于DefaultContainerService中,其代码如下:

    @Override
    public PackageInfoLite getMinimalPackageInfo(String packagePath, int flags,
            String abiOverride) {
        final Context context = DefaultContainerService.this;
        final boolean isForwardLocked = (flags & PackageManager.INSTALL_FORWARD_LOCK) != 0;
    
        PackageInfoLite ret = new PackageInfoLite();
        ........
        final File packageFile = new File(packagePath);
        final PackageParser.PackageLite pkg;
        final long sizeBytes;
        try {
            //如同PKMS的构造函数,利用PackageParser来解析APK文件,得到PackageInfoLite
            pkg = PackageParser.parsePackageLite(packageFile, 0);
            sizeBytes = PackageHelper.calculateInstalledSize(pkg, isForwardLocked, abiOverride);
        } catch (PackageParserException | IOException e) {
            .................
        }
    
        ret.packageName = pkg.packageName;
        ret.splitNames = pkg.splitNames;
        ret.versionCode = pkg.versionCode;
        ret.baseRevisionCode = pkg.baseRevisionCode;
        ret.splitRevisionCodes = pkg.splitRevisionCodes;
        ret.installLocation = pkg.installLocation;
        ret.verifiers = pkg.verifiers;
        //利用resolveInstallLocation来得到一个合理的安装位置
        ret.recommendedInstallLocation = PackageHelper.resolveInstallLocation(context,
                pkg.packageName, pkg.installLocation, sizeBytes, flags);
        ret.multiArch = pkg.multiArch;
    
        return ret;
    }

    从代码可以看出,getMinimalPackageInfo的代码比较简单,其实就是利用PackageParser解析出APK对应Pacakge的基本信息,然后利用resolveInstallLocation得到适合APK安装的路径。

    1.1 resolveInstallLocation
    我们看看resolveInstallLocation函数:

    public static int resolveInstallLocation(Context context, String packageName,
            int installLocation, long sizeBytes, int installFlags) {
        ApplicationInfo existingInfo = null;
        try {
            //如果之前该APK之前安装过,那么将获取到之前记录的ApplicationInfo信息
            existingInfo = context.getPackageManager().getApplicationInfo(packageName,
                    PackageManager.GET_UNINSTALLED_PACKAGES);
        } catch (NameNotFoundException ignored) {
            .........
        }
    
        final int prefer;
        final boolean checkBoth;
        boolean ephemeral = false;
        //以下其实就是根据installFlags决定安装倾向的路径prefer
        if ((installFlags & PackageManager.INSTALL_EPHEMERAL) != 0) {
            prefer = RECOMMEND_INSTALL_INTERNAL;
            ephemeral = true;
            checkBoth = false;
        } else if ((installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
            prefer = RECOMMEND_INSTALL_INTERNAL;
            checkBoth = false;
        } else if ((installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
            prefer = RECOMMEND_INSTALL_EXTERNAL;
            checkBoth = false;
        } else if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
            prefer = RECOMMEND_INSTALL_INTERNAL;
            checkBoth = false;
        } else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
            prefer = RECOMMEND_INSTALL_EXTERNAL;
            checkBoth = true;
        } else if (installLocation == PackageInfo.INSTALL_LOCATION_AUTO) {
            //一般APK安装的路径就是auto
            // When app is already installed, prefer same medium
            if (existingInfo != null) {
                // TODO: distinguish if this is external ASEC
                if ((existingInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
                    prefer = RECOMMEND_INSTALL_EXTERNAL;
                } else {
                    prefer = RECOMMEND_INSTALL_INTERNAL;
                }
            } else {
                //可以看到一般默认条件下是安装在手机内部的
                prefer = RECOMMEND_INSTALL_INTERNAL;
            }
            //auto时,checkBoth为true
            checkBoth = true;
        } else {
            prefer = RECOMMEND_INSTALL_INTERNAL;
            checkBoth = false;
        }
    
        boolean fitsOnInternal = false;
        if (checkBoth || prefer == RECOMMEND_INSTALL_INTERNAL) {
            //fitsOnInternal和下面的fitsOnExternal应该就是用于检查对应路径是否有足够的空间来安装APK的
            fitsOnInternal = fitsOnInternal(context, sizeBytes);
        }
    
        boolean fitsOnExternal = false;
        if (checkBoth || prefer == RECOMMEND_INSTALL_EXTERNAL) {
            fitsOnExternal = fitsOnExternal(context, sizeBytes);
        }
    
        if (prefer == RECOMMEND_INSTALL_INTERNAL) {
            // The ephemeral case will either fit and return EPHEMERAL, or will not fit
            // and will fall through to return INSUFFICIENT_STORAGE
            if (fitsOnInternal) {
                return (ephemeral)
                        ? PackageHelper.RECOMMEND_INSTALL_EPHEMERAL
                        : PackageHelper.RECOMMEND_INSTALL_INTERNAL;
            }
        } else if (prefer == RECOMMEND_INSTALL_EXTERNAL) {
            if (fitsOnExternal) {
                return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
            }
        }
    
        //个人感觉这里就是容错用的吧,在前面的代码中正常情况下,prefer已经被取值为RECOMMEND_INSTALL_INTERNAL或RECOMMEND_INSTALL_EXTERNAL了
        if (checkBoth) {
            if (fitsOnInternal) {
                return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
            } else if (fitsOnExternal) {
                return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
            }
        }
    
        //没有足够空间,返回对应消息
        return PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
    }

    上述代码相对比较简单,我们主要以fitsOnInternal为例,看看如何判断存储空间是否足够:

    public static boolean fitsOnInternal(Context context, long sizeBytes) {
        final StorageManager storage = context.getSystemService(StorageManager.class);
        final File target = Environment.getDataDirectory();
        //APK安装所需的空间,小于等于可用空间时,就返回true                
        return (sizeBytes <= storage.getStorageBytesUntilLow(target));
    }
    
    //StorgeManager中的函数,其实就是用总的可用空间,减去已经使用的空间,得到剩余空间
    public long getStorageBytesUntilLow(File path) {
        return path.getUsableSpace() - getStorageFullBytes(path);
    }

    2、installLocationPolicy
    当成功得到APK对应的PacakgeInfoLite,并判断安装路径有足够的剩余空间时,将调用installLocationPolicy函数:

    private int installLocationPolicy(PackageInfoLite pkgLite) {
        String packageName = pkgLite.packageName;
        int installLocation = pkgLite.installLocation;
        boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
    
        synchronized (mPackages) {
            // Currently installed package which the new package is attempting to replace or
            // null if no such package is installed.
            //判断终端上之前是否安装过同样的APK
            PackageParser.Package installedPkg = mPackages.get(packageName);
    
            // Package which currently owns the data which the new package will own if installed.
            // If an app is unstalled while keeping data (e.g., adb uninstall -k), installedPkg
            // will be null whereas dataOwnerPkg will contain information about the package
            // which was uninstalled while keeping its data.
            //当一个APK卸载时,那么installedPkg为null
            PackageParser.Package dataOwnerPkg = installedPkg;
            if (dataOwnerPkg  == null) {
                //但是如果APK卸载时,保留了数据,那么PKMS将取出对应的PacakgeSettings
                PackageSetting ps = mSettings.mPackages.get(packageName);
                if (ps != null) {
                    //从PacakgeSettings中取出Pacakge
                    dataOwnerPkg = ps.pkg;
                }
            }
    
            //存在旧APK对应的信息时
            if (dataOwnerPkg != null) {
                final boolean downgradeRequested =
                        (installFlags & PackageManager.INSTALL_ALLOW_DOWNGRADE) != 0;
                final boolean packageDebuggable =
                        (dataOwnerPkg.applicationInfo.flags
                                & ApplicationInfo.FLAG_DEBUGGABLE) != 0;
                //当安装一个重复的APK时,新装的APK版本一般要比旧APK的版本高
                //除非满足以下要求,例如显示要求装入旧版本、在debug模式下等
                final boolean downgradePermitted =
                    (downgradeRequested) && ((Build.IS_DEBUGGABLE) || (packageDebuggable));
    
                //默认模式下,即仅能安装高版本时
                if (!downgradePermitted) {
                    try {
                        //比较两个Package信息中的VersionCode
                        checkDowngrade(dataOwnerPkg, pkgLite);
                    } catch (PackageManagerException e) {
                        Slog.w(TAG, "Downgrade detected: " + e.getMessage());
                        return PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE;
                    }
                }
            }
    
            //旧有的APK还存在终端上时
            if (installedPkg != null) {
                //installFlags中必须携带REPLACE_EXISTING,否则将报错
                if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
                    // Check for updated system application.
                    if ((installedPkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
                        //系统APK不应该存在SD card上
                        if (onSd) {
                            Slog.w(TAG, "Cannot install update to system app on sdcard");
                            return PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION;
                        }
                        return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
                    } else {
                        // If current upgrade specifies particular preference
                        if (installLocation == PackageInfo.INSTALL_LOCATION_INTERNAL_ONLY) {
                            // Application explicitly specified internal.
                            return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
                        } else if (installLocation == PackageInfo.INSTALL_LOCATION_PREFER_EXTERNAL) {
                            // App explictly prefers external. Let policy decide
                        } else {
                            // Prefer previous location
                            //未指定安装路径时,与之前的安装路径保持一致
                            if (isExternal(installedPkg)) {
                                return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
                            }
                            return PackageHelper.RECOMMEND_INSTALL_INTERNAL;
                        }
                    }
                } else {
                    // Invalid install. Return error code
                    return PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS;
                }
            }
        }
        // All the special cases have been taken care of.
        // Return result based on recommended install location.
        if (onSd) {
            return PackageHelper.RECOMMEND_INSTALL_EXTERNAL;
        }
        return pkgLite.recommendedInstallLocation;
    }

    3、createInstallArgs
    处理完潜在的重复安装APK的风险后,PKMS调用createInstallArgs生成安装参数对象:

    private InstallArgs createInstallArgs(InstallParams params) {
        if (params.move != null) {
            return new MoveInstallArgs(params);
        } else if (installOnExternalAsec(params.installFlags) || params.isForwardLocked()) {
            return new AsecInstallArgs(params);
        } else {
            return new FileInstallArgs(params);
        }
    }

    这部分的代码较为简单,就是利用参数决定创建哪个InstallArgs的子类,我们主要关注在终端安装APK时,将要使用的FileInstallArgs,后文介绍其功能。

    4、copyApk
    如果不需要进行安装包检查,对于安装在终端内部的APK而言,将调用FileInstallArgs的copyAPK函数:

    int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
        Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyApk");
        try {
            //doCopyApk负责进行实际的工作
            return doCopyApk(imcs, temp);
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    }
    
    private int doCopyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
        //与之前版本不同的时,Android7.0中,已经通过Session进行了文件拷贝
        //当进入到前文所述的PKMS的installStage时,OriginInfo.fromStagedFile或OriginInfo.fromStagedContainer均会将staged变量置为true
        if (origin.staged) {
            if (DEBUG_INSTALL) Slog.d(TAG, origin.file + " already staged; skipping copy");
                codeFile = origin.file;
                resourceFile = origin.file;
                return PackageManager.INSTALL_SUCCEEDED;
            }
        }
    
        //当使用其它方式安装APK时,将进入到以下流程
        try {
            //当需要临时安装时,创建一个临时安装目录
            final boolean isEphemeral = (installFlags & PackageManager.INSTALL_EPHEMERAL) != 0;
            final File tempDir =
                    mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);
            codeFile = tempDir;
            resourceFile = tempDir;
        } catch (IOException e) {
            Slog.w(TAG, "Failed to create copy file: " + e);
            return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
        }
    
        //定义回调接口
        final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {
            @Override
            public ParcelFileDescriptor open(String name, int mode) throws RemoteException {
                if (!FileUtils.isValidExtFilename(name)) {
                    throw new IllegalArgumentException("Invalid filename: " + name);
                }
                try {
                    //当接口被回调时,需要创建并打开文件,同事赋予相应的权限
                    final File file = new File(codeFile, name);
                    final FileDescriptor fd = Os.open(file.getAbsolutePath(),
                            O_RDWR | O_CREAT, 0644);
                    Os.chmod(file.getAbsolutePath(), 0644);
                    return new ParcelFileDescriptor(fd);
                } catch (ErrnoException e) {
                    throw new RemoteException("Failed to open: " + e.getMessage());
                }
            }
        };
    
        //调用DefaultContainerService进行copyPackage的操作,传入了回调的接口
        int ret = PackageManager.INSTALL_SUCCEEDED;
        ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
        if (ret != PackageManager.INSTALL_SUCCEEDED) {
            Slog.e(TAG, "Failed to copy package");
            return ret;
        }
    
        //拷贝APK对应的Native库文件
        final File libraryRoot = new File(codeFile, LIB_DIR_NAME);
        NativeLibraryHelper.Handle handle = null;
        try {
            handle = NativeLibraryHelper.Handle.create(codeFile);
            ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
                    abiOverride);
        } catch (IOException e) {
            Slog.e(TAG, "Copying native libraries failed", e);
            ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
        } finally {
            IoUtils.closeQuietly(handle);
        }
    
        return ret;
    }

    对于非adb安装的APK,我们看看DefaultContainerService对应的copyPackage是如何进行处理的:

    @Override
    public int copyPackage(String packagePath, IParcelFileDescriptorFactory target) {
        if (packagePath == null || target == null) {
            return PackageManager.INSTALL_FAILED_INVALID_URI;
        }
    
        PackageLite pkg = null;
        try {
            final File packageFile = new File(packagePath);
            //解析出PackageFile
            pkg = PackageParser.parsePackageLite(packageFile, 0);
            //利用copyPackageInner进行实际的处理
            return copyPackageInner(pkg, target);
        } catch (PackageParserException | IOException | RemoteException e) {
            Slog.w(TAG, "Failed to copy package at " + packagePath + ": " + e);
            return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
        }
    }
    
    private int copyPackageInner(PackageLite pkg, IParcelFileDescriptorFactory target)
            throws IOException, RemoteException {
        //copyFile负责实际的拷贝
        copyFile(pkg.baseCodePath, target, "base.apk");
        if (!ArrayUtils.isEmpty(pkg.splitNames)) {
            for (int i = 0; i < pkg.splitNames.length; i++) {
                //对于多APK文件的情况,需依次拷贝所有的子文件
                copyFile(pkg.splitCodePaths[i], target, "split_" + pkg.splitNames[i] + ".apk");
            }
        }
    
        return PackageManager.INSTALL_SUCCEEDED;
    }
    
    private void copyFile(String sourcePath, IParcelFileDescriptorFactory target, String targetName)
            throws IOException, RemoteException {
        Slog.d(TAG, "Copying " + sourcePath + " to " + targetName);
        InputStream in = null;
        OutputStream out = null;
        try {
            //源文件作为输入
            in = new FileInputStream(sourcePath);
            //目的文件作为输出,这里进行了多层封装
            //上文提到过,回调接口target调用open函数后,将创建并打开目的端文件,然后赋予相应的写权限
            //ParcelFileDescriptor.AutoCloseOutputStream利用文件描述符构造出一个可自动关闭的输出流
            out = new ParcelFileDescriptor.AutoCloseOutputStream(
                    target.open(targetName, ParcelFileDescriptor.MODE_READ_WRITE));
            //进行实际的数据拷贝
            Streams.copy(in, out);
        } finally {
            IoUtils.closeQuietly(out);
            IoUtils.closeQuietly(in);
        }
    }

    至此整个handleStartCopy流程介绍完毕,可以看出当利用adb安装时,handleStartCopy实际上并没有完成什么实际的操作;对于其它方式安装APK时,handleStartCopy才会进行真正的数据拷贝工作。

    整个过程的大致流程如下:

    六、handleReturnCode
    copy过程结束后,将调用InstallParams的handleReturnCode:

    void handleReturnCode() {
        if (mArgs != null) {
            processPendingInstall(mArgs, mRet);
        }
    }
    
    private void processPendingInstall(final InstallArgs args, final int currentStatus) {
        mHandler.post(new Runnable() {
            public void run() {
                mHandler.removeCallbacks(this);
    
                // Result object to be returned
                PackageInstalledInfo res = new PackageInstalledInfo();
                res.setReturnCode(currentStatus);
                res.uid = -1;
                res.pkg = null;
                res.removedInfo = null;
                if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
                    //1、安装在终端上的APK,将调用FileInstallArgs的doPreInstall进行处理
                    args.doPreInstall(res.returnCode);
    
                    synchronized (mInstallLock) {
                        //2、调用installPackageTracedLI进行安装
                        installPackageTracedLI(args, res);
                    }
    
                    //3、调用FileInstallArgs的doPostInstall
                    args.doPostInstall(res.returnCode, res.uid);
                }
    
                // A restore should be performed at this point if (a) the install
                // succeeded, (b) the operation is not an update, and (c) the new
                // package has not opted out of backup participation.
                //判断是否需要备份恢复
                final boolean update = res.removedInfo != null
                        && res.removedInfo.removedPackage != null;
                final int flags = (res.pkg == null) ? 0 : res.pkg.applicationInfo.flags;
                boolean doRestore = !update
                        && ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0);
    
                // Set up the post-install work request bookkeeping.  This will be used
                // and cleaned up by the post-install event handling regardless of whether
                // there's a restore pass performed.  Token values are >= 1.
                int token;
                if (mNextInstallToken < 0) mNextInstallToken = 1;
                token = mNextInstallToken++;
    
                PostInstallData data = new PostInstallData(args, res);
                mRunningInstalls.put(token, data);
    
                if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {
                    //调用BackupManager的接口进行恢复工作
                    .......
                }
    
                if (!doRestore) {
                    .......
                    //4、生成一个POST_INSTALL消息,触发后续操作
                    Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
                    mHandler.sendMessage(msg);
                }
            }
        });
    }

    从上面的代码可以看出,handleReturnCode主要做了4件事:
    *调用InstallArgs的doPreInstall函数,对于安装在终端内部的APK而言,将调用FileInstallArgs的doPreInstall函数;
    *调用PKMS的installPackageTracedLI函数进行APK安装;
    *调用InstallArgs的doPostInstall函数;
    *利用结果构造PostInstallData,然后发送POST_INSTALL消息触发后续处理流程

    现在我们分别介绍这几部分工作:
    1、doPreInstall

    int doPreInstall(int status) {
        if (status != PackageManager.INSTALL_SUCCEEDED) {
            cleanUp();
        }
        return status;
    }
    
    private boolean cleanUp() {
        if (codeFile == null || !codeFile.exists()) {
            return false;
        }
    
        removeCodePathLI(codeFile);
    
        if (resourceFile != null && !FileUtils.contains(codeFile, resourceFile)) {
            resourceFile.delete();
        }
    
        return true;
    }

    从代码来看,正常流程下doPreInstall并不会进行实际的工作,只是当handleStartCopy出现问题时,doPreInstall将清理拷贝的文件。

    2、installPackageTracedLI

    private void installPackageTracedLI(InstallArgs args, PackageInstalledInfo res) {
        try {
            Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackage");
            installPackageLI(args, res);
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    }
    
    private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
        //定义一些变量
        .........
        // Result object to be returned
        res.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
    
        //定义parseFlags
        .......
        PackageParser pp = new PackageParser();
        .......
        final PackageParser.Package pkg;
        try {
            //解析APK文件,形成Package对象
            pkg = pp.parsePackage(tmpPackageFile, parseFlags);
        } catch (PackageParserException e) {
            res.setError("Failed parse during installPackageLI", e);
            return;
        } finally {
            Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
        }
    
        / If we are installing a clustered package add results for the children
        if (pkg.childPackages != null) {
            //在需要的情况下,解析Child Package信息
            ........
        }
    
        try {
            // either use what we've been given or parse directly from the APK
            if (args.certificates != null) {
                try {
                    //如果参数中定义了权限信息,就用参数中的权限信息配置Package对象
                    PackageParser.populateCertificates(pkg, args.certificates);
                } catch (PackageParserException e) {
                    // there was something wrong with the certificates we were given;
                    // try to pull them from the APK
                    PackageParser.collectCertificates(pkg, parseFlags);
                }
            } else {
                //否则,就从AndroidManifest.xml文件中解析出权限信息
                PackageParser.collectCertificates(pkg, parseFlags);
            }
        } catch (PackageParserException e) {
            res.setError("Failed collect during installPackageLI", e);
            return;
        }
    
        //当安装重复的APK时,根据权限、签名信息、版本等条件,判断能否进一步操作
        .............
    
        ........
        //根据Package中的信息,修改拷贝文件时,临时赋予的名称
        //此处将利用FileInstallArgs的doRename
        if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
            res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
            return;
        }
        .........
        try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
                "installPackageLI")) {
            if (replace) {
                //用新的package信息替换旧的
                replacePackageLIF(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,
                        installerPackageName, res);
            } else {
                //将新的pacakge信息加入到PKMS中
                installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
                        args.user, installerPackageName, volumeUuid, res);
            }
        }
        ..........
    }

    从代码来看,installPackageTracedLI的主要工作就是解析APK文件,形成对应的Package对象;生成对应的权限信息后,根据Package中的信息,更改存储路径对应目录的名称。

    3、doPostInstall

    int doPostInstall(int status, int uid) {
        if (status != PackageManager.INSTALL_SUCCEEDED) {
            cleanUp();
        }
        return status;
    }

    可以看出,FileInstallArgs中定义的doPostInstall函数和doPreInstall函数完全一样,正常流程下不需要进行任何操作;当之前的处理流程出现问题时,利用cleanUp清楚创建的文件和资源。

    4、处理POST_INSTALL消息
    在PackageHandler的doHandleMessage中处理POST_INSTALL消息:

    .....
    case POST_INSTALL: {
        ............
        PostInstallData data = mRunningInstalls.get(msg.arg1);
        final boolean didRestore = (msg.arg2 != 0);
        mRunningInstalls.delete(msg.arg1);
    
        if (data != null) {
            ............
            // Handle the parent package
            handlePackagePostInstall(parentRes, grantPermissions, killApp,
                    grantedPermissions, didRestore, args.installerPackageName,
                    args.observer);
    
            // Handle the child packages
            final int childCount = (parentRes.addedChildPackages != null)
                    ? parentRes.addedChildPackages.size() : 0;
            for (int i = 0; i < childCount; i++) {
                //同样利用handlePackagePostInstall处理child Package
                ........
            }
            ........
        } else {
            .........
        }
    }
    break;
    ......

    我们跟进一下handlePackagePostInstall函数:

    private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions,
            boolean killApp, String[] grantedPermissions,
            boolean launchedForRestore, String installerPackage,
            IPackageInstallObserver2 installObserver) {
        if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
            // Send the removed broadcasts
            if (res.removedInfo != null) {
                res.removedInfo.sendPackageRemovedBroadcasts(killApp);
            }
    
            //调用grantRequestedRuntimePermissions等,赋予Package权限
            ...........
    
            //发送ACTION_PACKAGE_ADDED等广播消息
            ...........
    
            ...........
        }
    
        // If someone is watching installs - notify them
        //将安装的结果通知给Pm.java中观察者
        if (installObserver != null) {
            try {
                Bundle extras = extrasForInstallResult(res);
                installObserver.onPackageInstalled(res.name, res.returnCode,
                        res.returnMsg, extras);
            } catch (RemoteException e) {
                Slog.i(TAG, "Observer no longer exists.");
            }
        }
    }

    从上面的代码来看,处理POST_INSTALL的主要工作其实还是通过广播、回调接口通知系统中的其它组件,有新的Pacakge安装或发生了改变。

    最后整理一下handleReturnCode的流程,如下所示:

    七、总结
    从上面的代码来看,整个APK的安装过程极其琐碎复杂,但核心思想还是比较简单的:就是将待安装的APK文件拷贝到手机的指定位置,然后利用PackageParser来解析出对应的Package对象,最终将Package对象加入到PKMS中。
    整体流程的主干大体如下图所示:

    大图链接

    展开全文
  • 摘要:上一节讲解了APK的扫描,本节讲解APK安装流程 阅读本文大约需要花费40分钟。 文章首发微信公众号:大猫玩程序 专注于Android系统级源码分析,Android的平台设计,欢迎关注我,谢谢! [Android取经之路...
  • 如果是xpk包,可以通过手动安装xpk来检测一下。如果是apk包,请重新下载。 2、INSTALL_FAILED_OLDER_SDK:系统版本过低 当前程序不支持您的手机。 3、INSTALL_FAILED_INSUFFICIENT_STORAGE:没有足够的存储空间。 4、...
  • 考虑到应用的安全性,建议版本发布前测试人员需要做如下几项验证: 1、签名验证:  使用开发工具对APK进行debug时就回自动生成APK(比如eclipse...将待验证apk重命名为rar格式,然后打开压缩包,取出其中的META-INF
  • Hello人海相遇,有您真好梧桐旨在给大家分享搜集整理的精品软件/资源/教程等,每天搜集整理是一件非常消耗时间的事情,视频和公众号分享的资源都是经过梧桐测试了一段时间,验证了可用性之后才分享给大家的,甚至...
  • Cordova插件集合、平台搭建及apk安装

    千次阅读 2019-04-07 11:29:34
    Cordova插件(打开命令窗口下载) ...apk安装 启动虚拟机 将apk拷贝到 C盘 android-sdk 中的 platform-tools 中 在此处打开命令窗口输入命令:adb install app-debug.apk,其中app-debug-apk为app安装包
  • Android APK安装

    千次阅读 2018-02-12 14:43:22
    简述下APK安装过程,在文章的正式开始之前,我们需要做一些知识的补充 1:如何调用起安装界面,以从sd卡安装为例子 Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE); intent.setData(Uri.parse(&...
  • 主要现象是,Android Studio编译apk,加上系统的签名之后无法安装,系统安装升级、adb instal apk、adb install -r apk等都返回错误,错误表现为返回ErrorCode:-15 , INSTALL_FAILED_TEST_ONLY错误。
  • APK打包安装过程

    2016-04-11 20:45:56
    无论是替换还是新安装,都会调用scanPackageLI(),然后跑去scanPackageDirtyLI,它会判断是否为系统程序,解析apk程序包,检查依赖库,验证签名,检查sharedUser签名、权限冲突、ContentProvider冲突,更新native库...
  • 关于android3.0版本的apk打包安装失败的问题 最近遇到做项目遇到的一个坑,现在来总结一下,方便记录。 我用的as版本为3.0.1,直接连接电脑安装app是没有问题的,但是在签名打包apk以后, 手机显示安装失败,原因为...
  • 原理基于上篇的多渠道打包工具,我们使用apktool解压需要验证apk文件后,去读AndroidManifest.xml,当读到渠道号哪一行的时候输出即可。 源码如下: Main.java [java] view plaincopyprint? package...
  • Android – Mac终端adb安装APK文件 先检查mac是否配置adb环境,直接在终端输入 adb 即可 如出现 -bash: adb: command not found; 未配置 然后继续一下操作 首先设置相关配置 第一步,启动Mac自带的终端Terminal ...
  • 安装apk到虚拟机

    2014-01-03 16:15:37
    三、输入adb install apk_path(网上有人说是adb install xxx.apk。我这么写不行,用apk地址成功了)   没有工具的朋友(完整步骤) 第一步:下载并解压android SDK到自己硬盘上。 第二步:在系统环境...
  • 屏蔽HTTPS证书验证下载apk安装包

    千次阅读 2016-03-30 16:04:19
    当我们从一个HTTPS的网址下载资源时,不需要经过它的证书验证实现下载想要的资源 private void DownApk(String url) throws MalformedURLException { ...// 目前的证书基本都是ca签名验证,SSL验证。 SSLConte
  • 操作系统变了,环境也变了,问题也随之而来哦~用AS Run了一个代码,生成了apk进行安装。但是不幸的事情是,我的小米手机提示,未安装(也就是安装失败了)。查看logcat 提示:Installation error code: -15 意思是:...
  • apk从打包到安装到启动

    千次阅读 2017-01-06 11:04:52
    把安卓从apk打包到开机到apk安装到显示好好看了下,只是大概过程,里面的很多类都不是很理解,但仅仅是想学习下整体流程,接触下各个阶段涉及的类,为后面看源码做准备。老罗的源码解析的书翻开了还几次最终还是关上...
  • 当产品有BUG需要被修复的时候,我们可以使用DownloadManager 系统提供的下载类来实现 下载新版本的APK,并通过静默安装的方式,将APK神不知鬼不觉的安装到手机中,静默安装分为两种方式,第一种为root过的手机,第二...
  • Android逆向分析APK的打包与安装

    千次阅读 2016-12-02 09:46:20
    从我们在Android Studio中,点击run,到app运行在手机上,之间究竟发生了什么,代码和资源是怎么变成APK的,而APK又是怎么安装上去,并能执行的呢。 我们或许都能说出来像 上图 这样一个简单的过程:...
  • 安装APK的时候,出现这个页面时,表示很惊讶,但是没关系,我还可以输入密码,然而重点来了,账号是IT同学设置的,我!并!不!知!道!密!码! Vivo的技术支持是这么解释的。对此我只能说,我真的不应该选择 ...
  • 安装了Android Studio 3.0 ,换了新的测试机 结果一个问题困扰我一天  新建的项目直接无法安装,并出现:Failed to finalize session : INSTALL_FAILED_INTERNAL_ERROR 首先罗列一下困扰我的原因 1...
  • 小米8 安装apk提示签名不一致

    千次阅读 2019-05-13 16:35:20
    今天遇到使用小米8 安装应用的时候提示安装失败,说签名不一致。把本地之前的应用卸载了还是有问题。之后找到了原因:小米8有分身功能,在分身的那个界面有之前安装的应用,导致安装失败。卸载了就可以了。 ...
  • python手册 apk_python apk

    2020-11-26 07:31:32
    广告关闭腾讯云11.11云上盛惠 ,精选热门产品助力上云,云服务器首年88元起,买的越多返的越多,最高返5000元!使用python生成多渠道包 往apk包中追加到一个空文件到meta-inf目录以标识渠道,android中获取此文件...
  • 我们在开发app的时候,往往在打包完之后还要进行分发。...问题的关键就是在这里,在Studio 2.2以后引入一项新的应用签名方案 APK Signature Scheme v2 ,它能提供更快的应用安装时间和更多针对未授权AP
  • 在本系列上一篇文章Android9.0 PM机制系列(一)PackageInstaller初始化解析中我们学习了PackageInstaller是如何初始化的,这一篇文章我们接着学习PackageInstaller是如何安装APK的。本系列文章的源码基于Android9.0...
  • APK可视化修改工具 APK改之理 APK IDE

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 8,809
精华内容 3,523
关键字:

关闭apk安装验证