PKMS 2

本文分析 APK 是如何安装的,以及 PKMS 在这个过程中进行了哪些工作。

本文仅供自己记录学习使用。

APK 的安装方式有很多,我们先看看如何用 adb 命令进行安装。

我们从adb install 开始分析,该命令有多个参数,我们仅考虑最基本的安装方式,如下:

adb install app_debug.apk

adb 命令

/system/core/adb/commandline.cpp 中 adb_commandline 函数,其中对很多参数进行了处理,例如常用的 adb pull / push / install / uninstall 等。

其中对install参数的处理如下:

1413int adb_commandline(int argc, const char** argv) {
        ...
1828    else if (!strcmp(argv[0], "install")) {
1829        if (argc < 2) return usage("install requires an argument");
1830        if (_use_legacy_install()) {
1831            return install_app_legacy(transport_type, serial, argc, argv);
1832        }
1833        return install_app(transport_type, serial, argc, argv);
1834    }
        ...
1980}   

---

1403static bool _use_legacy_install() {
1404    FeatureSet features;
1405    std::string error;
1406    if (!adb_get_feature_set(&features, &error)) {
1407        fprintf(stderr, "error: %s\n", error.c_str());
1408        return true;
1409    }
1410    return !CanUseFeature(features, kFeatureCmd);
1411}

内部又进行了 _use_legacy_install() 判断是否能用Feature特性,然后调用 install_app_legacy() 或 install_app() 。

2220static int install_app_legacy(TransportType transport, const char* serial, int argc, const char** argv) {
        // 待安装的APK目前还在源机器上,现在需要把APK的文件复制到手机里
        // 如果安装在手机内部存储,那么目的地址为DATA_DEST
        // 如果安装在SD卡上,则目的地址为SD_DEST
2221    static const char *const DATA_DEST = "/data/local/tmp/%s";
2222    static const char *const SD_DEST = "/sdcard/tmp/%s";
        
        // 默认安装在手机内部
2223    const char* where = DATA_DEST;
2224
2225    for (int i = 1; i < argc; i++) {
            // 携带参数 -s 时,才安装到SD卡
2226        if (!strcmp(argv[i], "-s")) {
2227            where = SD_DEST;
2228        }
2229    }
        
        // 解析参数,判断adb命令中是否携带了有效的apk文件名
        ...

        // 取出apk名
2247    std::vector apk_file = {argv[last_apk]};
        // 构造apk目的地址
2248    std::string apk_dest = android::base::StringPrintf(
2249        where, android::base::Basename(argv[last_apk]).c_str());
        // do_sync_push 将此APK文件传输到手机的目标路径,失败的话将跳转cleanup_apk
2250    if (!do_sync_push(apk_file, apk_dest.c_str())) goto cleanup_apk;
2251    argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
        // 执行 pm_command
2252    result = pm_command(transport, serial, argc, argv);
2253
2254cleanup_apk:
        // 删除刚在传输的文件
2255    delete_file(transport, serial, apk_dest);
2256    return result;
2257}   

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

接下来看一下 install_app()

2001static int install_app(TransportType transport, const char* serial, int argc, const char** argv) {
2002    // The last argument must be the APK file
        // 利用参数创建出本地文件的名称
2003    const char* file = argv[argc - 1];
        // 解析参数,判断adb命令中是否携带了有效的apk文件名
        ...
        // adb_open 中将创建出这个file对应的文件
2015    int localFd = adb_open(file, O_RDONLY);
        ...

2022    std::string cmd = "exec:cmd package";
        //添加cmd参数
        ...
        // 连接源端,获取源APK文件的描述符
2033    int remoteFd = adb_connect(cmd, &error);
        ...
        // 将remoteFd中数据写入到localFd
2041    copy_to_file(localFd, remoteFd);
        // 得到结果
2042    read_status_line(remoteFd, buf, sizeof(buf));
2043
2044    adb_close(localFd);
2045    adb_close(remoteFd);
2046
2047    if (strncmp("Success", buf, 7)) {
2048        fprintf(stderr, "Failed to install %s: %s", file, buf);
2049        return 1;
2050    }
2051    fputs(buf, stderr);
2052    return 0;
2053}

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

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

2186static int pm_command(TransportType transport, const char* serial, int argc, const char** argv) {
        // 构造 pm 命令
2187    std::string cmd = "pm";
2188
2189    while (argc-- > 0) {
2190        cmd += " " + escape_arg(*argv++);
2191    }
2192
        // 发送shell命令给adb
2193    return send_shell_command(transport, serial, cmd, false);
2194}

继续跟进 send_shell_command:

1096int send_shell_command(TransportType transport_type, const char* serial, const std::string& command,
1097                       bool disable_shell_protocol, StandardStreamsCallbackInterface* callback) {
1098    int fd;
1099    bool use_shell_protocol = false;
1100
1101    while (true) {
1102        bool attempt_connection = true;
1103
1104        // Use shell protocol if it's supported and the caller doesn't explicitly
1105        // disable it.
1106        if (!disable_shell_protocol) {
1107            FeatureSet features;
1108            std::string error;
1109            if (adb_get_feature_set(&features, &error)) {
                    // 如果定义了feature,则替换 shell protocol
1110                use_shell_protocol = CanUseFeature(features, kFeatureShell2);
1111            } else {
1112                // Device was unreachable.
1113                attempt_connection = false;
1114            }
1115        }
1116
1117        if (attempt_connection) {
1118            std::string error;
                // 此时 command 中携带的就是以pm开头的命令
1119            std::string service_string = ShellServiceString(use_shell_protocol, "", command);
1120
                // 向shell服务发送命令
1121            fd = adb_connect(service_string, &error);
1122            if (fd >= 0) {
1123                break;
1124            }
1125        }
            ...
1131    }
1132
        // 读取返回结果
1133    int exit_code = read_and_dump(fd, use_shell_protocol, callback);
1134
1135    if (adb_close(fd) < 0) {
1136        PLOG(ERROR) << "failure closing FD " << fd;
1137    }
1138
1139    return exit_code;
1140}

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

pm 是一个可执行脚本。

我们在终端上调用adb shell,然后执行pm,可以得到以下结果

 ~/Tmp  adb shell
shell@hwALE-H:/ $ 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 list features
       pm list libraries
       ...

pm 脚本定义在 /framework/base/cmds/pm/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启动函数
/frameworks/base/cmds/app_process/app_main.cpp

187int main(int argc, char* const argv[])
188{
        ...
        // 解析参数
272    while (i < argc) {
273        const char* arg = argv[i++];
274        if (strcmp(arg, "--zygote") == 0) {
275            zygote = true;
276            niceName = ZYGOTE_NICE_NAME;
277        } else if (strcmp(arg, "--start-system-server") == 0) {
278            startSystemServer = true;
279        } else if (strcmp(arg, "--application") == 0) {
280            application = true;
281        } else if (strncmp(arg, "--nice-name=", 12) == 0) {
282            niceName.setTo(arg + 12);
283        } else if (strncmp(arg, "--", 2) != 0) {
284            className.setTo(arg);
285            break;
286        } else {
287            --i;
288            break;
289        }
290    }
        
        ...
342
343    if (zygote) {
344        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
345    } else if (className) {
            // 此时不再是启动zygote,而是启动className对应的类
346        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
347    } else {
            ...
351    }
352}

跟进 AndroidRumtime.cpp 的 start 函数

994void AndroidRuntime::start(const char* className, const Vector& options, bool zygote)
995{
        ...
1076        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
1077            "([Ljava/lang/String;)V");
1078        if (startMeth == NULL) {
1079            ALOGE("JavaVM unable to find main() in '%s'\n", className);
1080            /* keep going */
1081        } else {
1082            env->CallStaticVoidMethod(startClass, startMeth, strArray);
                ...
1088        }
        ...
1097}

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

private static final native void nativeFinishInit();

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();
    ...
}

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

216static void com_android_internal_os_RuntimeInit_nativeFinishInit(JNIEnv* env, jobject clazz)
217{
        // getC
218    gCurRuntime->onStarted();
219}

/frameworks/base/cmds/app_process/app_main.cpp 中定义的 AppRuntime 继承 AndroidRuntime,实现了onStarted函数

/frameworks/base/core/jni/AndroidRuntime.cpp 中的callMain函数

305status_t AndroidRuntime::callMain(const String8& className, jclass clazz,
306    const Vector& args)
307{
308    JNIEnv* env;
309    jmethodID methodId;
        ...
313    env = getJNIEnv();
        ...
318    methodId = env->GetStaticMethodID(clazz, "main", "([Ljava/lang/String;)V");
        ...
328    jclass stringClass;
329    jobjectArray strArray;
330
331    const size_t numArgs = args.size();
332    stringClass = env->FindClass("java/lang/String");
333    strArray = env->NewObjectArray(numArgs, stringClass, NULL);
334
335    for (size_t i = 0; i < numArgs; i++) {
336        jstring argStr = env->NewStringUTF(args[i].string());
337        env->SetObjectArrayElement(strArray, i, argStr);
338    }
339
        // 最终调用Pm.java的main函数
340    env->CallStaticVoidMethod(clazz, methodId, strArray);
341    return NO_ERROR;
342}

以上主要分析如何 从-执行脚本文件-到-启动Java进程。


Pm执行脚本到启动Java进程

Pm中的流程

/frameworks/base/cmds/pm/src/com/android/commands/pm/Pm.java
进入到 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"));
    ...
    // 返回PKMS中保存的PackageInstallerService
    mInstaller = mPm.getPackageInstaller();
    // 保存参数
    mArgs = args;
    String op = args[0];
    mNextArg = 1;
    ...
    if ("install".equals(op)) {
        // 安装APK将调用runInstall
        return runInstall();
    }
    ...
}

跟进 runInstall函数

private int runInstall() throws RemoteException {
    long startedTime = SystemClock.elapsedRealtime();
    // 根据参数创建InstallParams,其中包含了SessionParams,标志为MODE_FULL_INSTALL
    final InstallParams params = makeInstallParams();
    // inPath 对应于安装的APK文件
    final String inPath = nextArg();
    ...
    // 1. 创建Session
    final int sessionId = doCreateSession(params.sessionParams,
            params.installerPackageName, params.userId);

    try {
        ...
        // 2. write session
        if (doWriteSession(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk",
                false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
            return 1;
        }
        // 3 commit session
        Pair status = doCommitSession(sessionId, false /*logSuccess*/);
        if (status.second != PackageInstaller.STATUS_SUCCESS) {
            return 1;
        }
        ...
        return 0;
    } finally {
        try {
            mInstaller.abandonSession(sessionId);
        } catch (Exception ignore) {
        }
    }
}

从上面的代码可以看出,runInstall主要进行了三件事情:

  1. 创建session
  2. 对session进行写操作
  3. 最后提交session。

1. create session

private int doCreateSession(SessionParams params, String installerPackageName, int userId)
        throws RemoteException {
    // 通过AMS得到字符串"runInstallCreate"对应的uid
    userId = translateUserId(userId, "runInstallCreate");
    if (userId == UserHandle.USER_ALL) {
        userId = UserHandle.USER_SYSTEM;
        params.installFlags |= PackageManager.INSTALL_ALL_USERS;
    }
    // 通过PackageInstallService创建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图标,所以adb安装时没有显示图标
    if (params.appIcon != null) {
        final ActivityManager am = (ActivityManager) mContext.getSystemService(
                Context.ACTIVITY_SERVICE);
        final int iconSize = am.getLauncherLargeIconSize();
        if ((params.appIcon.getWidth() > iconSize * 2)
                || (params.appIcon.getHeight() > iconSize * 2)) {
            params.appIcon = Bitmap.createScaledBitmap(params.appIcon, iconSize, iconSize,
                    true);
        }
    }

    switch (params.mode) {
        case SessionParams.MODE_FULL_INSTALL:
        case SessionParams.MODE_INHERIT_EXISTING:
            break;
        default:
            throw new IllegalArgumentException("Invalid install mode: " + params.mode);
    }

    // If caller requested explicit location, sanity check it, otherwise
    // resolve the best internal or adopted location.
    if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
        ...
    } else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
        ...
    } else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) {
        ...
    } else {
        // For now, installs to adopted media are treated as internal from
        // an install flag point-of-view.
        // adb 安装时应该进入这个分支(不添加参数指定安装在sd card时),为SessionParams设置InstallInternalFlag
        params.setInstallFlagsInternal();
        ...
    }

    final int sessionId;
    final PackageInstallerSession session;
    synchronized (mSessions) {
        ...
        // sessionId是个随机值
        sessionId = allocateSessionIdLocked();
    }

    final long createdMillis = System.currentTimeMillis();
    // We're staging to exactly one location
    File stageDir = null;
    String stageCid = null;
    // 根据installFlags,决定安装目录,默认安装到internal目录下
    if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
        final boolean isInstant =
                (params.installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
        // 此处将会在临时性的data目录下创建file,应该是作为copy的目的地址
        stageDir = buildStageDir(params.volumeUuid, sessionId, isInstant);
    } else {
        stageCid = buildExternalStageCid(sessionId);
    }

    session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
            mInstallThread.getLooper(), sessionId, userId, installerPackageName, callingUid,
            params, createdMillis, stageDir, stageCid, false, false);

    synchronized (mSessions) {
        mSessions.put(sessionId, session);
    }
    // 进行回调
    mCallbacks.notifySessionCreated(session.sessionId, session.userId);
    // 在mSessionFile中进行记录
    writeSessionsAsync();
    return sessionId;
}

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

public class PackageInstallerSession extends IPackageInstallerSession.Stub 

2. write session

创建出PackageInstallerSession后,我们看看Pm#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 得到PackageInstallSession的代理对象

我们看看上面代码调用的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 得到客户端

PackageInstaller.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();
    }
}

我们看看 PackageInstallerService的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网络通信的架构及命令模式,写的非常巧妙。

用一张图来为这一部分做个总结:


Pm中的流程

PKMS.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 2_第1张图片
installStage

从这段代码来看,PKMS将先后调用HandlerParams#handleStartCopy和HandlerParams#handleReturnCode来完成主要的工作。接下来介绍一下这两个函数的工作流程。

handleStartCopy

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

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,利用PackageParser解析出APK对应Package的基本信息,然后利用resolveInstallLocation得到适合APK安装路径。
  2. installLocationPolicy,当成功得到APK对应的PackageInfoLite,并判断安装路径有足够的剩余空间。
  3. createInstallArgs,处理完潜在的重复安装APK的风险后,PKMS调用createInstallArgs生成安装参数对象,这部分代码较为简单,就是利用参数决定创建哪个InstallArgs的子类,我们主要关注在终端安装APK时,将要使用的FileInstallArgs,后文介绍其功能。
  4. copyApk,如果不需要进行安装包检查,对于安装在终端内部的APK而言,将调用FileInstallArgs的copyAPK函数。对于非adb安装的APK,将调用DefaultContainerService对应的copyPackage进行处理。

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

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

PKMS 2_第2张图片
handleStartCopy

InstallParams#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);
            }
        }
    });
}

主要做了4件事情

  1. InstallArgs#doPreInstall
  2. PKMS#installPackageTracedLI 进行Apk安装
  3. InstallArgs#doPostInstall函数
  4. 利用构造PostInstallData,然后发送POST_INSTALL消息触发后续流程
PKMS 2_第3张图片
handleReturnCode

总结 adb install

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


adb install

installd

当SystemServer创建PKMS时,在其构造函数中传入了一个Installer对象。

Installer是一个系统服务,可以和installd通信,完成一些重要的工作,例如利用dexopt函数对APK文件进行dex优化;存储空间不足时,利用freeCache函数清理存储空间。

installd启动

在手机根目录下 /init.rc 中,接近末尾的地方代码片段

...
service installd /system/bin/installd
  class main
  socket installd stream 600 system system
...

installd 将作为service被init进程启动,同时会创建一个名为installd的socket。

在 frameworks/native/cmds/installd/installd.cpp 中:

int main(const int argc, char *argv[]) {
    return android::installd::installd_main(argc, argv);
}

static int installd_main(const int argc ATTRIBUTE_UNUSED, char *argv[]) {
    .........
    //以下初始化全局变量,包括创建data下的一些目录等
    if (!initialize_globals()) {
        ALOGE("Could not initialize globals; exiting.\n");
        exit(1);
    }

    if (initialize_directories() < 0) {
        ALOGE("Could not create directories; exiting.\n");
        exit(1);
    }
    ...........
    //得到"installd" socket
    lsocket = android_get_control_socket(SOCKET_PATH);
    ...........
    //"installd"变成服务端
    if (listen(lsocket, 5)) {
        ALOGE("Listen on socket failed: %s\n", strerror(errno));
        exit(1);
    }
    fcntl(lsocket, F_SETFD, FD_CLOEXEC);

    for (;;) {
        alen = sizeof(addr);
        //接受Java层的installer服务连接,形成与之连接的socket "s"
        s = accept(lsocket, &addr, &alen);
        ..........
        fcntl(s, F_SETFD, FD_CLOEXEC);
        ..........
        for (;;) {
            unsigned short count;
            //读取收到的消息的长度
            if (readx(s, &count, sizeof(count))) {
                ALOGE("failed to read size\n");
                break;
            }

            //判断有效性
            if ((count < 1) || (count >= BUFFER_MAX)) {
                ALOGE("invalid size %d\n", count);
                break;
            }

            //读取cmd
            if (readx(s, buf, count)) {
                ALOGE("failed to read command\n");
                break;
            }
            ..........
            buf[count] = 0;
            //执行cmd
            if (execute(s, buf)) break;
        }
        .........
        close(s);
    }

    return 0;
}

从上面代码可以看出,installd整体结构非常简单,其实就是启动后,获取作为服务端socket的“installd”;
然后监听“installd”,等待Java层Installer服务的连接及命令的到来。

命令处理方式
一旦installd收到命令后,将调用execute函数进行处理,其代码如下:

static int execute(int s, char cmd[BUFFER_MAX]) {
    char reply[REPLY_MAX];
    char *arg[TOKEN_MAX+1];
    .......
    arg[0] = cmd;
    //解析参数的个数
    while (*cmd) {
        //当发现空格时
        if (isspace(*cmd)) {
            *cmd++ = 0;
            //参数个数+1
            n++;
            //保存参数
            arg[n] = cmd;
            if (n == TOKEN_MAX) {
                ALOGE("too many arguments\n");
                goto done;
            }
        }
        if (*cmd) {
          cmd++;
        }
    }

    //cmds为一个数组,保存了不同命令及对应的处理函数
    for (i = 0; i < sizeof(cmds) / sizeof(cmds[0]); i++) {
        if (!strcmp(cmds[i].name,arg[0])) {
            if (n != cmds[i].numargs) {
                //log
                .......
            } else {
                //参数正确时,调用对应的执行函数进行处理
                ret = cmds[i].func(arg + 1, reply);
            }
        }
    }
    ...........
}

execute函数的思路很清晰,类似于收到命令后查找表找到对应的处理函数,然后传入参数并执行函数。

在这一部分的最后,我们看看cmds中的内容:

struct cmdinfo {
    const char *name;
    unsigned numargs;
    int (*func)(char **arg, char reply[REPLY_MAX]);
};

struct cmdinfo cmds[] = {
    { "ping",                 0, do_ping },

    { "create_app_data",      7, do_create_app_data },
    { "restorecon_app_data",  6, do_restorecon_app_data },
    { "migrate_app_data",     4, do_migrate_app_data },
    { "clear_app_data",       5, do_clear_app_data },
    { "destroy_app_data",     5, do_destroy_app_data },
    { "move_complete_app",    7, do_move_complete_app },
    { "get_app_size",         6, do_get_app_size },
    { "get_app_data_inode",   4, do_get_app_data_inode },

    { "create_user_data",     4, do_create_user_data },
    { "destroy_user_data",    3, do_destroy_user_data },

    { "dexopt",              10, do_dexopt },
    { "markbootcomplete",     1, do_mark_boot_complete },
    { "rmdex",                2, do_rm_dex },
    { "freecache",            2, do_free_cache },
    { "linklib",              4, do_linklib },
    { "idmap",                3, do_idmap },
    { "createoatdir",         2, do_create_oat_dir },
    { "rmpackagedir",         1, do_rm_package_dir },
    { "clear_app_profiles",   1, do_clear_app_profiles },
    { "destroy_app_profiles", 1, do_destroy_app_profiles },
    { "linkfile",             3, do_link_file },
    { "move_ab",              3, do_move_ab },
    { "merge_profiles",       2, do_merge_profiles },
    { "dump_profiles",        3, do_dump_profiles },
};

可以清晰地看出cmds数组中存储的就是cmdinfo结构体,结构体中定义了名称、参数个数及对应的处理函数。

installer服务的启动
在SystemServer.java#startBootstrapServices函数中:

private void startBootstrapServices() {
    // Wait for installd to finish starting up so that it has a chance to
    // create critical directories such as /data/user with the appropriate
    // permissions.  We need this to complete before we initialize other services.
    Installer installer = mSystemServiceManager.startService(Installer.class);
    ..............
}

public SystemService startService(String className) {
    final Class serviceClass;
    try {
        serviceClass = (Class)Class.forName(className);
    } catch (ClassNotFoundException ex) {
        //抛出异常
        ..........
    }
    return startService(serviceClass);
}

public  T startService(Class serviceClass) {
    try {
        final String name = serviceClass.getName();
        .............
        final T service;
        try {
            //反射调用构造函数
            Constructor constructor = serviceClass.getConstructor(Context.class);
        } catch(.....) {
            .............
        }.......

        //SystemServiceManager的mServices中存储所有由其启动的服务
        mServices.add(service);

        // Start it.
        try {
            service.onStart();
        } catch (RuntimeException ex) {
            //抛出异常
            ...........
        }
        return service;
    } finally {
        //log
        ...........
    }
}

从上面的代码容易看出,SystemServer利用SystemServerManager的startService启动服务时,其实就是通过反射来创建服务对象,然后调用服务对象的onStart函数。

因此,对于installer服务,我们此时只需要关注其构造函数和onStart函数:

public final class Installer extends SystemService {
    ........
    public Installer(Context context) {
        super(context);
        //与installd沟通的桥梁
        mInstaller = new InstallerConnection();
    }
    ......
    public void onStart() {
        Slog.i(TAG, "Waiting for installd to be ready.");
        //调用InstallerConnection的waitForConnection函数
        mInstaller.waitForConnection();
    }
    ........
}

从上面的代码可以看出,Installer继承自SystemService,其构造函数主要创建出InstallerConnection,然后onStart函数中调用InstallerConnection的waitForConnection函数。

连接installd
接下来,我们看看InstallerConnection#waitForConnection函数:

public void waitForConnection() {
    for (;;) {
        try {
            //"ping"应该只是确认installd是否存活
            execute("ping");
            return;
        } catch (InstallerException ignored) {
        }
        Slog.w(TAG, "installd not ready");
        SystemClock.sleep(1000);
    }
}

跟进InstallerConnection的execute函数:

public String[] execute(String cmd, Object... args) throws InstallerException {
    final StringBuilder builder = new StringBuilder(cmd);
    //处理参数
    for (Object arg : args) {
        String escaped;
        if (arg == null) {
            escaped = "";
        } else {
            escaped = String.valueOf(arg);
        }

        if (escaped.indexOf('\0') != -1 || escaped.indexOf(' ') != -1 || "!".equals(escaped)) {
            //错误格式抛出异常
            ........
        }

        if (TextUtils.isEmpty(escaped)) {
            escaped = "!";
        }
        builder.append(' ').append(escaped);
    }

    //transact发送命令及对应参数,并返回结果
    final String[] resRaw = transact(builder.toString()).split(" ");
    int res = -1;
    try {
        //解析结果
        res = Integer.parseInt(resRaw[0]);
    } catch (ArrayIndexOutOfBoundsException | NumberFormatException ignored) {
    }

    if (res != 0) {
        throw new InstallerException(
                "Failed to execute " + cmd + " " + Arrays.toString(args) + ": " + res);
    }
    return resRaw;
}

容易看出transact函数负责进行实际的传输工作:

public synchronized String transact(String cmd) {
    ............
    //连接"installd"
    if (!connect()) {
        Slog.e(TAG, "connection failed");
        return "-1";
    }

    //发送命令
    if (!writeCommand(cmd)) {
        Slog.e(TAG, "write command failed? reconnect!");
        if (!connect() || !writeCommand(cmd)) {
            return "-1";
        }
    }

    //读取返回结果
    final int replyLength = readReply();
    if (replyLength > 0) {
        String s = new String(buf, 0, replyLength);
        ........
        return s;
    } else {
        ........
        return "-1";
    }
}

从上面的代码来看,整个通信结构非常的清晰,典型的socket通信架构。

为了分析的完整性,我们再看看connect、writeCommand和readReply函数:

private boolean connect() {
    //第一次才需要进行实际的连接,之后就不需要了
    if (mSocket != null) {
        return true;
    }
    ......
    try {
        mSocket = new LocalSocket();

        //得到"installd"目的端地址
        LocalSocketAddress address = new LocalSocketAddress("installd",
                LocalSocketAddress.Namespace.RESERVED);

        //进行连接的过程
        mSocket.connect(address);

        //以下得到输入流和输出流
        mIn = mSocket.getInputStream();
        mOut = mSocket.getOutputStream();
    } catch (IOException ex) {
        disconnect();
        return false;
    }
    return true;
}

private boolean writeCommand(String cmdString) {
    final byte[] cmd = cmdString.getBytes();
    final int len = cmd.length;
    ........
    buf[0] = (byte) (len & 0xff);
    buf[1] = (byte) ((len >> 8) & 0xff);
    try {
        //写入长度
        mOut.write(buf, 0, 2);
        //写入命令和参数
        mOut.write(cmd, 0, len);
    } catch (IOException ex) {
        Slog.e(TAG, "write error");
        disconnect();
        return false;
    }
    return true;
}

private int readReply() {
    if (!readFully(buf, 2)) {
        return -1;
    }

    final int len = (((int) buf[0]) & 0xff) | ((((int) buf[1]) & 0xff) << 8);
    ........
    if (!readFully(buf, len)) {
        return -1;
    }

    return len;
}

private boolean readFully(byte[] buffer, int len) {
    try {
        //还是靠mIn来读取数据
        Streams.readFully(mIn, buffer, 0, len);
    } catch (IOException ioe) {
        Slog.e(TAG, "read exception");
        disconnect();
        return false;
    }
    ........
    return true;
}

至此Installer服务的启动主要的流程基本介绍完毕。
从上述代码可以看出,Installer服务完全可以看做是installd在Java层的代理。

该Java服务自身基本不承载任何主要的逻辑,就是通过Socket将请求发送给native层的installd进行处理。

参考资料

感谢以下文章作者
Android7.0 PackageManagerService (3) APK安装

你可能感兴趣的:(PKMS 2)