Android7.0 PackageManagerService (3) APK安装

来源:互联网 发布:淘宝卖内衣赚钱吗 编辑:程序博客网 时间:2024/05/16 09:27

在本篇博客中,我们分析一下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:/ # pmusage: 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=/systemexport CLASSPATH=$base/framework/pm.jarexec 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函数:

@Overridepublic 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函数:

@Overridepublic 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函数:

@Overridepublic 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函数:

@Overridepublic 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中,其代码如下:

@Overridepublic 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是如何进行处理的:

@Overridepublic 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中。
整体流程的主干大体如下图所示:

大图链接

2 0