Quantcast
Channel: CSDN博客移动开发推荐文章
Viewing all articles
Browse latest Browse all 5930

Android7.0 PackageManagerService (3) APK安装

$
0
0

在本篇博客中,我们分析一下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中。
整体流程的主干大体如下图所示:

大图链接

作者:Gaugamela 发表于2016/10/9 10:53:25 原文链接
阅读:47 评论:0 查看评论

Viewing all articles
Browse latest Browse all 5930

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>