android 9 adb安装过程学习(一)

参考资料:PMS 第 7 篇 - 通过 pm 指令分析 Install 过程

一、前期知识准备

1.1 包管理机制

所谓包,其实是一种文件格式,比如apk包、jar包等。包管理者一个职能就是识别不同的包,维护这些包的信息。当有一个新的包进入或离开Android世界,都需要向包管理者申报一下,其他管理部分要获取包的具体信息,也都需要向包管理者申请。包管理机制的核心是PackageManagerService(下文称PMS),它负责对包进行管理。

1.2 关注点

Android P 上采用类似 socket 的方式与 server 端通信完成安装,其中Session是重点
其中有如下几个点是我们要重点关注的如下:

  • PackageInstaller:
    根据官方文档,PackageInstaller 提供了安装、更新以及卸载等功能,其中包括单 APK 和多 APK 安装。
    具体的安装行为是通过 PackageInstaller 内部的 Session 完成的。所有的应用都有权限创建这个 Session,但是可能会需要用户的确认才能完成安装(权限不足)。
  • Session:
    创建 Session 可以为其指定参数 SessionParams,其中一个作用就是要全部替换还是局部替换 MODE_FULL_INSTALL 和 MODE_INHERIT_EXISTING

1.3 如何安装

通过 IO 流的方式向 Session 内输送 apk 数据。具体代码可以看下文。需要注意的是,PackageInsatller 对于安装结果回调没有采用普通的函数回调,而是采用 Intent 的方式完成回调,比如 广播等(可以参考下面两个实例)。

apk的安装方法

  1. 开机过程中安装:系统在每次开机会安装系统应用
  2. adb工具安装:adb命令安装apk
  3. 手动安装:平时下载的apk,通过系统安装器PackageInstaller(它是系统内置的应用程序,用于安装和卸载应用程序)来安装apk,是有安装界面的
  4. 商店应用安装:商店上安装app,没有安装界面

二、代码分析

adb inatsll ***.apk 命令对应的代码位置:system/core/adb/commandline.cpp

int adb_commandline(int argc, const char** argv) {
	//...各种参数输入的处理

    /* adb_connect() commands */
    if (!strcmp(argv[0], "devices")) {
		//...
    }
    else if (!strcmp(argv[0], "connect")) {
		//..
    }
    else if (!strcmp(argv[0], "disconnect")) {
		//...
    }
    else if (!strcmp(argv[0], "emu")) {
        //...
    }
    else if (!strcmp(argv[0], "shell")) {
        //...
    }
    else if (!strcmp(argv[0], "exec-in") || !strcmp(argv[0], "exec-out")) {
		//...
    }
    else if (!strcmp(argv[0], "kill-server")) {
        return adb_kill_server() ? 0 : 1;
    }
    else if (!strcmp(argv[0], "sideload")) {
		//...
    } else if (!strcmp(argv[0], "tcpip")) {
		//...
    }
    else if (!strcmp(argv[0], "remount") ||
		//...
    } else if (!strcmp(argv[0], "root") || !strcmp(argv[0], "unroot")) {
        return adb_root(argv[0]) ? 0 : 1;
    } else if (!strcmp(argv[0], "bugreport")) {
		//...
    } else if (!strcmp(argv[0], "forward") || !strcmp(argv[0], "reverse")) {
		//...
    }
    /* do_sync_*() commands */
    else if (!strcmp(argv[0], "ls")) {
		//...
    }
    else if (!strcmp(argv[0], "push")) {
		//...
    }
    else if (!strcmp(argv[0], "pull")) {
		//...
    }
    else if (!strcmp(argv[0], "install")) {
        if (argc < 2) return syntax_error("install requires an argument");
        if (_use_legacy_install()) {		//android 9使用旧的pm install
            return install_app_legacy(argc, argv);
        }	//如果支持FeatureCmd,那就调用install_app
        return install_app(argc, argv);
    }
    else if (!strcmp(argv[0], "install-multiple")) {
		//...
	} 		//...
	else {
		//...
	}

    syntax_error("unknown command %s", argv[0]);
    return 1;
}

这里看到:

  • 如果支持 cmd 命令的情况下,会调用 install_app;
  • 如果不支持,执行 install_app_legacy 方法!

2.1 cmd install - 支持cmd命令

commandline::install_app
static int install_app(int argc, const char** argv) {
    // The last argument must be the APK file
    const char* file = argv[argc - 1];
    if (!android::base::EndsWithIgnoreCase(file, ".apk")) {
        return syntax_error("filename doesn't end .apk: %s", file);
    }

    struct stat sb;
    if (stat(file, &sb) == -1) {
        fprintf(stderr, "adb: failed to stat %s: %s\n", file, strerror(errno));
        return 1;
    }
	//打开apk文件,获取localFD的文件描述符
    int localFd = adb_open(file, O_RDONLY);
    if (localFd < 0) {
        fprintf(stderr, "adb: failed to open %s: %s\n", file, strerror(errno));
        return 1;
    }
	//【1】使用 cmd package 命令!
    std::string error;
    std::string cmd = "exec:cmd package";

    // don't copy the APK name, but, copy the rest of the arguments as-is
    while (argc-- > 1) {
        cmd += " " + escape_arg(std::string(*argv++));
    }

    // add size parameter [required for streaming installs]
    // do last to override any user specified value
    cmd += " " + android::base::StringPrintf("-S %" PRIu64, static_cast<uint64_t>(sb.st_size));
	//使用连接到设备上的adb服务,获取远程描述符
    int remoteFd = adb_connect(cmd, &error);
    if (remoteFd < 0) {
        fprintf(stderr, "adb: connect error for write: %s\n", error.c_str());
        adb_close(localFd);
        return 1;
    }
	//这里只是把文件复制过去,并没有进行安装
    char buf[BUFSIZ];
    copy_to_file(localFd, remoteFd);			
    read_status_line(remoteFd, buf, sizeof(buf));	//读取复制结果

    adb_close(localFd);
    adb_close(remoteFd);

    if (!strncmp("Success", buf, 7)) {
        fputs(buf, stdout);
        return 0;
    }
    fprintf(stderr, "adb: failed to install %s: %s", file, buf);
    return 1;
}

需要注意的是,这段代码中的实际应用程序的安装操作并没有完成,只是将文件内容从本地复制到了远程。实际的安装步骤可能在后续的代码中执行。

2.2 pm install - 不支持 cmd 指令

当使用adb install时,在android 9 的情况下会使用旧的安装方式,进行安装。回到用install_app_legacy(),最终调用pm_command

static int install_app_legacy(int argc, const char** argv) {
	// 此时我们需要先将 apk 将拷贝到手机的指定目录下!
    //【1】如果是要安装到内部存储,目标路径是 DATA_DEST,如果是存储则是 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;
	//【2】如果 adb 指令设置了 -s 参数,那就安装到外置存储,默认是内置存储!
    for (int i = 1; i < argc; i++) {
        if (!strcmp(argv[i], "-s")) {
            where = SD_DEST;
        }
    }

	//【3】判断是否有 apk 文件参数!
    int result = -1;
    std::vector<const char*> apk_file = {argv[last_apk]};
    //【4】计算目标位置,将要安装的 apk 拷贝到目标缓存位置
    // 同时将 adb 命令的最后一个参数改为 apk 拷贝后的目标缓存位置!
    // 目标位置:/data/local/tmp/name.apk!
    std::string apk_dest = android::base::StringPrintf(
        where, android::base::Basename(argv[last_apk]).c_str());
    // do_sync_push 将此 apk 文件传输到目标路径,失败的话将跳转到 clenaup_apk
    if (!do_sync_push(apk_file, apk_dest.c_str(), false)) goto cleanup_apk;
    // 设置新位置!
    argv[last_apk] = apk_dest.c_str(); /* destination name, not source location */
    //执行安装
    result = pm_command(argc, argv);

cleanup_apk:
// 删除目标缓存 apk 文件,PMS 会把该 apk 拷贝到 /data/app 目录下,所以这个缓存中的 apk 没用了!
    delete_file(apk_dest);
    return result;
}

comandline::pm_command

static int pm_command(int argc, const char** argv) {
	// 我们看到,这里会将 adb install 命令和参数,转为 pm 指令!
    std::string cmd = "pm";

    while (argc-- > 0) {
        cmd += " " + escape_arg(*argv++);
    }
	// 继续调用 send_shell_command 方法!
    return send_shell_command(cmd, false);
}

commandline::send_shell_command

int send_shell_command(const std::string& command, bool disable_shell_protocol,
                       StandardStreamsCallbackInterface* callback) {
    int fd;
    bool use_shell_protocol = false;

    while (true) {
        bool attempt_connection = true;

        // 使用 shell protocol
        if (!disable_shell_protocol) {
            FeatureSet features;
            std::string error;
            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 protocol 发送命令
            fd = adb_connect(service_string, &error);
            if (fd >= 0) {
                break;
            }
        }

        fprintf(stderr, "- waiting for device -\n");
        if (!wait_for_device("wait-for-device")) {
            return 1;
        }
    }
	// 处理命令执行结果!
    int exit_code = read_and_dump(fd, use_shell_protocol, callback);

    if (adb_close(fd) < 0) {
        PLOG(ERROR) << "failure closing FD " << fd;
    }

    return exit_code;
}

到这里,我们知道,最后其实是向 shell 服务发送 pm 命令,触发 apk 的安装!

三、pm install

cmd 的调用上面已经分析了,其实和 pm install 没有区别!
这里我们来看下 pm 命令的主要用法,通过 pm install 继续分析安装:

usage: pm path [--user USER_ID] PACKAGE
       pm dump PACKAGE
       pm install [-lrtsfd] [-i PACKAGE] [--user USER_ID] [PATH]
       pm install-create [-lrtsfdp] [-i PACKAGE] [-S BYTES]
               [--install-location 0/1/2]
               [--force-uuid internal|UUID]
       pm install-write [-S BYTES] SESSION_ID SPLIT_NAME [PATH]
       pm install-commit SESSION_ID
       pm install-abandon SESSION_ID
       pm uninstall [-k] [--user USER_ID] [--versionCode VERSION_CODE] PACKAGE
       pm set-installer PACKAGE INSTALLER
       pm move-package PACKAGE [internal|UUID]
       pm move-primary-storage [internal|UUID]
       pm clear [--user USER_ID] PACKAGE
       pm enable [--user USER_ID] PACKAGE_OR_COMPONENT
       pm disable [--user USER_ID] PACKAGE_OR_COMPONENT
       pm disable-user [--user USER_ID] PACKAGE_OR_COMPONENT
       pm disable-until-used [--user USER_ID] PACKAGE_OR_COMPONENT
       pm default-state [--user USER_ID] PACKAGE_OR_COMPONENT
       pm set-user-restriction [--user USER_ID] RESTRICTION VALUE
       pm hide [--user USER_ID] PACKAGE_OR_COMPONENT
       pm unhide [--user USER_ID] PACKAGE_OR_COMPONENT
       pm grant [--user USER_ID] PACKAGE PERMISSION
       pm revoke [--user USER_ID] PACKAGE PERMISSION
       pm reset-permissions
       pm set-app-link [--user USER_ID] PACKAGE {always|ask|never|undefined}
       pm get-app-link [--user USER_ID] PACKAGE
       pm set-install-location [0/auto] [1/internal] [2/external]
       pm get-install-location
       pm set-permission-enforced PERMISSION [true|false]
       pm trim-caches DESIRED_FREE_SPACE [internal|UUID]
       pm create-user [--profileOf USER_ID] [--managed] [--restricted] [--ephemeral] [--guest] USER_NAME
       pm remove-user USER_ID
       pm get-max-users

pm 命令的源码位于 frameworks/base/cmds/pm 中,我们直接去看下目录结构:

chen@ubuntu:~/Mygit/Android$ ls frameworks/base/cmds/pm/
Android.mk              MODULE_LICENSE_APACHE2  NOTICE                  pm

看看Android.mk中的内容:

chen@ubuntu:~/Mygit/Android$ cat frameworks/base/cmds/pm/Android.mk
# Copyright 2007 The Android Open Source Project
#
LOCAL_PATH:= $(call my-dir)

include $(CLEAR_VARS)
LOCAL_MODULE := pm
LOCAL_SRC_FILES := pm 	#定义了该模块的源文件为 "pm",意味着要编译构建的文件名为 "pm"
LOCAL_MODULE_CLASS := EXECUTABLES	#表示该模块是一个可执行文件
LOCAL_MODULE_TAGS := optional	#表示该模块是一个可选项
include $(BUILD_PREBUILT)

接着,来看看 pm 脚本的内容:

chen@ubuntu:~/Mygit/Android$ cat frameworks/base/cmds/pm/pm
#!/system/bin/sh
cmd package "$@"

这看着像封装了一下调用/system/bin/cmd package,只是简化使用 “cmd package” 命令的流程

3.1 app_process::main

app_process 其实就是 Zygote 的源码,在开机篇我们已经分析过了 Zygote 的启动,这里略过和 Zygote 相关的代码:
位置:frameworks/base/cmds/app_process/app_main.cpp

int main(int argc, char* const argv[])
{
	//...

    ++i;  // Skip unused "parent dir" argument.
    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) {
        	//【1】获得要启动的 java 进程的 className
            className.setTo(arg);
            break;
        } else {
            --i;
            break;
        }
    }

    Vector<String8> args;
    if (!className.isEmpty()) {
        //【2】根据 application 的取值,判断我们启动的是应用类型进程,还是工具类型进程!
        args.add(application ? String8("application") : String8("tool"));
        runtime.setClassNameAndArgs(className, argc - i, argv + i);

        if (!LOG_NDEBUG) {
          String8 restOfArgs;
          char* const* argv_new = argv + i;
          int argc_new = argc - i;
          for (int k = 0; k < argc_new; ++k) {
            restOfArgs.append("\"");
            restOfArgs.append(argv_new[k]);
            restOfArgs.append("\" ");
          }
          ALOGV("Class name = %s, args = %s", className.string(), restOfArgs.string());
        }
    } else {	// zygote 启动才会进入!
		//...
    }
    
    if (zygote) {
        runtime.start("com.android.internal.os.ZygoteInit", args, zygote);
    } else if (className) {
    	//【2.2】非 Zygote 进程的启动方式如下
        runtime.start("com.android.internal.os.RuntimeInit", args, zygote);
    } else {
        fprintf(stderr, "Error: no class name or --zygote supplied.\n");
        app_usage();
        LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied.");
    }
}

接下来,会进入 AndroidRuntime 中去!!

3.2 AndroidRuntime::start

这里的 className 传入的值为 com.android.internal.os.RuntimeInit
位置:"frameworks/base/core/jni/AndroidRuntime.cpp"

void AndroidRuntime::start(const char* className, const Vector<String8>& options, bool zygote)
{
    ALOGD(">>>>>> START %s uid %d <<<<<<\n",
            className != NULL ? className : "(unknown)", getuid());

    static const String8 startSystemServer("start-system-server");

    /*
     * 'startSystemServer == true' means runtime is obsolete and not run from
     * init.rc anymore, so we print out the boot start event here.
     */
    for (size_t i = 0; i < options.size(); ++i) {
        if (options[i] == startSystemServer) {
           /* track our progress through the boot sequence */
           const int LOG_BOOT_PROGRESS_START = 3000;
           LOG_EVENT_LONG(LOG_BOOT_PROGRESS_START,  ns2ms(systemTime(SYSTEM_TIME_MONOTONIC)));
        }
    }

    const char* rootDir = getenv("ANDROID_ROOT");
    if (rootDir == NULL) {
        rootDir = "/system";
        if (!hasDir("/system")) {
            LOG_FATAL("No root directory specified, and /android does not exist.");
            return;
        }
        setenv("ANDROID_ROOT", rootDir, 1);
    }

    //const char* kernelHack = getenv("LD_ASSUME_KERNEL");
    //ALOGD("Found LD_ASSUME_KERNEL='%s'\n", kernelHack);

    /* start the virtual machine */  //【1】启动虚拟机!
    JniInvocation jni_invocation;
    jni_invocation.Init(NULL);
    JNIEnv* env;
    if (startVm(&mJavaVM, &env, zygote) != 0) {
        return;
    }
    onVmCreated(env);

    /*
     * Register android functions.
     */ //【2】注册 Android 函数!
    if (startReg(env) < 0) {
        ALOGE("Unable to register all android natives\n");
        return;
    }

    /*
     * We want to call main() with a String array with arguments in it.
     * At present we have two arguments, the class name and an option string.
     * Create an array to hold them.
     */ //【3】创建一个 String 数组来保存传递进来的参数!
    jclass stringClass;
    jobjectArray strArray;
    jstring classNameStr;

    stringClass = env->FindClass("java/lang/String");
    assert(stringClass != NULL);
    strArray = env->NewObjectArray(options.size() + 1, stringClass, NULL);
    assert(strArray != NULL);
    classNameStr = env->NewStringUTF(className);
    assert(classNameStr != NULL);
    env->SetObjectArrayElement(strArray, 0, classNameStr);

    for (size_t i = 0; i < options.size(); ++i) {
        jstring optionsStr = env->NewStringUTF(options.itemAt(i).string());
        assert(optionsStr != NULL);
        env->SetObjectArrayElement(strArray, i + 1, optionsStr);
    }

    /*
     * Start VM.  This thread becomes the main thread of the VM, and will
     * not return until the VM exits.
     */  //【4】启动虚拟机的主线程,该主线程不会退出,直到虚拟机退出!
    char* slashClassName = toSlashClassName(className != NULL ? className : "");
    jclass startClass = env->FindClass(slashClassName);
    if (startClass == NULL) {
        ALOGE("JavaVM unable to locate class '%s'\n", slashClassName);
        /* keep going */
    } else {	//【2.3】反射调用 RuntimeInit.main 函数,从 native 层进入 java 世界!
        jmethodID startMeth = env->GetStaticMethodID(startClass, "main",
            "([Ljava/lang/String;)V");
        if (startMeth == NULL) {
            ALOGE("JavaVM unable to find main() in '%s'\n", className);
            /* keep going */
        } else {
            env->CallStaticVoidMethod(startClass, startMeth, strArray);

#if 0
            if (env->ExceptionCheck())
                threadExitUncaughtException(env);
#endif
        }
    }
    free(slashClassName);

    ALOGD("Shutting down VM\n");
    if (mJavaVM->DetachCurrentThread() != JNI_OK)
        ALOGW("Warning: unable to detach main thread\n");
    if (mJavaVM->DestroyJavaVM() != 0)
        ALOGW("Warning: VM did not shut down cleanly\n");
}

通过反射,进入 RuntimeInit 中!

3.3 RuntimeInit.main

位置:.frameworks/base/core/java/com/android/internal/os/RuntimeInit.java

public static final void main(String[] argv) {
        enableDdms();
        if (argv.length == 2 && argv[1].equals("application")) {
            if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting application");
            redirectLogStreams();
        } else {
            if (DEBUG) Slog.d(TAG, "RuntimeInit: Starting tool");
        }
		//【1】进程一些初始化的操作,比如设置线程的默认异常处理 Handler,设置 Log 系统等等,在进程的启动时,我们分析过!!
        commonInit();

        /*
         * Now that we're running in interpreted code, call back into native code
         * to run the system.
         */		//【2.4】这里的关键点在这里!
        nativeFinishInit();

        if (DEBUG) Slog.d(TAG, "Leaving RuntimeInit!");
    }

nativeFinishInit 调用的是 native 方法,位于 AndroidRuntime.cpp 文件中!

3.4 com_android_internal_os_RuntimeInit_nativeFinishInit

位置:frameworks/base/core/jni/AndroidRuntime.cpp

static void com_android_internal_os_RuntimeInit_nativeFinishInit(JNIEnv* env, jobject clazz)
{
	//【2.5】gCurRuntime 是 AndroidRuntime 子类 AppRuntime 的实例!
    gCurRuntime->onStarted();
}

实际上 AndroidRuntime 并没有实现 onStarted 方法,真正是实现是在 App_main.cpp 中的 AppRuntime 类中,他是 AndroidRuntime 的子类!

3.5 AndroidRuntime

位置:./frameworks/base/cmds/app_process/app_main.cpp

    virtual void onStarted()
    {
        sp<ProcessState> proc = ProcessState::self();
        ALOGV("App process: starting thread pool.\n");
        proc->startThreadPool();
		//【2.6】执行 AndroidRuntime 的 callMain 方法!
        AndroidRuntime* ar = AndroidRuntime::getRuntime();
        ar->callMain(mClassName, mClass, mArgs);

        IPCThreadState::self()->stopProcess();
        hardware::IPCThreadState::self()->stopProcess();
    }

3.6 AndroidRuntime::callMain

位置:"frameworks/base/core/jni/AndroidRuntime.cpp"

status_t AndroidRuntime::callMain(const String8& className, jclass clazz,
    const Vector<String8>& args)
{
    JNIEnv* env;
    jmethodID methodId;

    ALOGD("Calling main entry %s", className.string());

    env = getJNIEnv();
    if (clazz == NULL || env == NULL) {
        return UNKNOWN_ERROR;
    }

    methodId = env->GetStaticMethodID(clazz, "main", "([Ljava/lang/String;)V");
    if (methodId == NULL) {
        ALOGE("ERROR: could not find method %s.main(String[])\n", className.string());
        return UNKNOWN_ERROR;
    }

    /*
     * We want to call main() with a String array with our arguments in it.
     * Create an array and populate it.
     */		//【1】创建一个 String 数组来封装参数!
    jclass stringClass;
    jobjectArray strArray;

    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);
    }
	//【*2.7】最终反射调用了 Pm.main 方法!
    env->CallStaticVoidMethod(clazz, methodId, strArray);
    return NO_ERROR;
}

四、命令安装

之前是用adb shell pm …命令
现在是用adb shell cmd package …命令,
也可用adb install 命令安装应用。
cmd package命令通过binder的shell cmd 调用

位置:frameworks/native/cmds/cmd/cmd.cpp

int main(int argc, char* const argv[])
{
	//...
    // TODO: block until a result is returned to MyResultReceiver.
    //参数service即传入的package服务,经过Binder的一些传递(CPP 通过binder调用JAVA Binder服务)最终调用onShellCommand()
    status_t err = IBinder::shellCommand(service, STDIN_FILENO, STDOUT_FILENO, STDERR_FILENO, args,
            cb, result);
	//...
    return res;
}
//位置:./frameworks/base/core/java/android/os/Binder.java
    public void shellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
            @Nullable FileDescriptor err,
            @NonNull String[] args, @Nullable ShellCallback callback,
            @NonNull ResultReceiver resultReceiver) throws RemoteException {
        onShellCommand(in, out, err, args, callback, resultReceiver);
    }

对用package来说,即调用到

//位置:frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
    @Override
    public void onShellCommand(FileDescriptor in, FileDescriptor out,
            FileDescriptor err, String[] args, ShellCallback callback,
            ResultReceiver resultReceiver) {
        (new PackageManagerShellCommand(this)).exec(
                this, in, out, err, args, callback, resultReceiver);
    }

在PackageManagerShellCommand中,调用exec会调用onCommand我觉得
位置:frameworks/base/services/core/java/com/android/server/pm/PackageManagerShellCommand.java

public int onCommand(String cmd) {
        if (cmd == null) {
            return handleDefaultCommands(cmd);
        }

        final PrintWriter pw = getOutPrintWriter();
        try {
            switch(cmd) {
                case "path":
                    return runPath();
                case "dump":
                    return runDump();
                case "list":
                    return runList();
				//...
                case "install":		//如果参数是install就调用runInstall
                    return runInstall();

                case "preinstall":

                     return preInstall();

				//...
                case "uninstall":
                    return runUninstall();
                case "clear":
                    return runClear();
                case "enable":
                    return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
                case "disable":
                    return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
				//...
                default: {
                    String nextArg = getNextArg();
                    if (nextArg == null) {
                        if (cmd.equalsIgnoreCase("-l")) {
                            return runListPackages(false);
                        } else if (cmd.equalsIgnoreCase("-lf")) {
                            return runListPackages(true);
                        }
                    } else if (getNextArg() == null) {
                        if (cmd.equalsIgnoreCase("-p")) {
                            return displayPackageFilePath(nextArg, UserHandle.USER_SYSTEM);
                        }
                    }
                    return handleDefaultCommands(cmd);		//如果啥参数都没有,就调用onHelp
                }
            }
        } catch (RemoteException e) {
            pw.println("Remote exception: " + e);
        }
        return -1;
    }

4.1 runInstall

大体的安装流程:

IBinder::shellCommand()/cmd.cpp --> .... --> onShellCommand()/PackageManagerService.java --> exec()/ShellCommand.java
--> onCommand()/PackageManagerShellCommand.java -->
runInstall() 
	+--> doCreateSession() 
	 |     +--> mInterface.getPackageInstaller().createSession(params, installerPackageName, userId);
    +--> doCommitSession()
          +--> new PackageInstaller.Session(mInterface.getPackageInstaller().openSession(sessionId));
          +--> session.commit(receiver.getIntentSender())
private int runInstall() throws RemoteException {
        final PrintWriter pw = getOutPrintWriter();
        final InstallParams params = makeInstallParams();
        final String inPath = getNextArg();

        setParamsSize(params, inPath);
        final int sessionId = doCreateSession(params.sessionParams,
                params.installerPackageName, params.userId);		//创建安装的session
        boolean abandonSession = true;
        try {
            if (inPath == null && params.sessionParams.sizeBytes == -1) {
                pw.println("Error: must either specify a package size or an APK file");
                return 1;
            }
            //该过程其实是将inPath指向apk写入到.tmp目录下,指定文件名是base.apk
            if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk",
                    false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
                return 1;
            }
            //提交Session触发安装
            if (doCommitSession(sessionId, false /*logSuccess*/)
                    != PackageInstaller.STATUS_SUCCESS) {
                return 1;
            }
            abandonSession = false;
            pw.println("Success");
            return 0;
        } finally {
            if (abandonSession) {
                try {
                	//如果安装异常,忽视本次事务
                    doAbandonSession(sessionId, false /*logSuccess*/);
                } catch (Exception ignore) {
                }
            }
        }
    }

4.2.1 makeInstallParams

Pm 中会根据传入的安装参数,创建 InstallParams,封装安装参数!
位置:./frameworks/base/services/core/java/com/android/server/pm/PackageManagerShellCommand.java

    private InstallParams makeInstallParams() {
    	//【*2.9.1.1】创建 SessionParams 实例,封装安装参数!
        final SessionParams sessionParams = new SessionParams(SessionParams.MODE_FULL_INSTALL);
        //【*2.9.1.2】创建 InstallParams,解耦合!
        final InstallParams params = new InstallParams();
        params.sessionParams = sessionParams;	// 设置引用关系!
        String opt;
        boolean replaceExisting = true;
        //【1】根据额外的参数,设置安装参数!
        while ((opt = getNextOption()) != null) {
            switch (opt) {
                case "-l":
                //【1.1】该应用以 forward locked 方式安装,在该模式下,只有应用自己能够访问自己的代码和非资源文件!
                    sessionParams.installFlags |= PackageManager.INSTALL_FORWARD_LOCK;
                    break;
                case "-r": // ignore	//【1.2】替换已存在的 apk
                    break;
                case "-R":
                    replaceExisting = false;
                    break;
                case "-i":	// 显式指定安装器
                    params.installerPackageName = getNextArg();
                    if (params.installerPackageName == null) {
                        throw new IllegalArgumentException("Missing installer package");
                    }
                    break;
                case "-t":	// 允许测试应用安装(设置了 android:testOnly)
                    sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_TEST;
                    break;
                case "-s":	// 安装到外置存储
                    sessionParams.installFlags |= PackageManager.INSTALL_EXTERNAL;
                    break;
                case "-f":	// 安装到内置存储
                    sessionParams.installFlags |= PackageManager.INSTALL_INTERNAL;
                    break;
                case "-d":	//【1.3】允许降级安装!
                    sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_DOWNGRADE;
                    break;
                case "-g":	//【1.4】授予运行时权限
                    sessionParams.installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;
                    break;
                case "--dont-kill":
                    sessionParams.installFlags |= PackageManager.INSTALL_DONT_KILL_APP;
                    break;
                case "--originating-uri":	// 安装不会杀 app 进程
                    sessionParams.originatingUri = Uri.parse(getNextArg());
                    break;
                case "--referrer":
                    sessionParams.referrerUri = Uri.parse(getNextArg());
                    break;
                case "-p":	//【1.5】继承已存在的 apk,appPackageName 用以保存要继承的包名,也就是主 apk 的包名!
                    sessionParams.mode = SessionParams.MODE_INHERIT_EXISTING;
                    sessionParams.appPackageName = getNextArg();
                    if (sessionParams.appPackageName == null) {
                        throw new IllegalArgumentException("Missing inherit package name");
                    }
                    break;
                case "--pkg":
                    sessionParams.appPackageName = getNextArg();
                    if (sessionParams.appPackageName == null) {
                        throw new IllegalArgumentException("Missing package name");
                    }
                    break;
                case "-S":	// 显式指定应用的大小
                    final long sizeBytes = Long.parseLong(getNextArg());
                    if (sizeBytes <= 0) {
                        throw new IllegalArgumentException("Size must be positive");
                    }
                    sessionParams.setSize(sizeBytes);
                    break;
                case "--abi":	// 显式指定 abi
                    sessionParams.abiOverride = checkAbiArgument(getNextArg());
                    break;
                case "--ephemeral":	// 作为一个 lightweight "ephemeral" 应用!
                case "--instant":
                case "--instantapp":
                    sessionParams.setInstallAsInstantApp(true /*isInstantApp*/);
                    break;
                case "--full":
                    sessionParams.setInstallAsInstantApp(false /*isInstantApp*/);
                    break;
                case "--preload":
                    sessionParams.setInstallAsVirtualPreload();
                    break;
                case "--user":	// 指定安装的 userId
                    params.userId = UserHandle.parseUserArg(getNextArgRequired());
                    break;
                case "--install-location":	// 指定安装位置
                    sessionParams.installLocation = Integer.parseInt(getNextArg());
                    break;
                case "--force-uuid":
                    sessionParams.installFlags |= PackageManager.INSTALL_FORCE_VOLUME_UUID;
                    sessionParams.volumeUuid = getNextArg();
                    if ("internal".equals(sessionParams.volumeUuid)) {
                        sessionParams.volumeUuid = null;
                    }
                    break;
                case "--force-sdk":
                    sessionParams.installFlags |= PackageManager.INSTALL_FORCE_SDK;
                    break;
                default:
                    throw new IllegalArgumentException("Unknown option " + opt);
            }
            if (replaceExisting) {
                sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
            }
        }
        return params;
    }

额外的参数很多,根据额外的参数,对 SessionParams 进行了属性设置!

sessionParams.installFlags 会根据不同的额外参数,设置不同的二进制位,具体的含义我上面也简单的注释了下。大家可以自己去看源码!
这里我们要注意下:
- 如果我们安装的是 split apk 的话,那么我们可以指定 -p 参数,后面指定主 apk 的包名,后面我们会分析其作用!

4.2.2 doCreateSession

位置:./frameworks/base/services/core/java/com/android/server/pm/PackageManagerShellCommand.java

    private int doCreateSession(SessionParams params, String installerPackageName, int userId)
            throws RemoteException {
        userId = translateUserId(userId, true /*allowAll*/, "runInstallCreate");
        if (userId == UserHandle.USER_ALL) {
            userId = UserHandle.USER_SYSTEM;
            params.installFlags |= PackageManager.INSTALL_ALL_USERS;
        }
		//【*3.1】进入 PackageInstallerService!
        final int sessionId = mInterface.getPackageInstaller()
                .createSession(params, installerPackageName, userId);
        return sessionId;
    }

进入了 PackageInstallerService 服务,创建 Session 对象

4.2.3 doWriteSplit - 写入事务

回顾下参数:

if (doWriteSplit(sessionId, inPath, params.sessionParams.sizeBytes, "base.apk",
		false /*logSuccess*/) != PackageInstaller.STATUS_SUCCESS) {
		return 1;
	}

这里的 splitName 值为 “base.apk”;
位置:./frameworks/base/services/core/java/com/android/server/pm/PackageManagerShellCommand.java

private int doWriteSplit(int sessionId, String inPath, long sizeBytes, String splitName,
            boolean logSuccess) throws RemoteException {
        //【1】这里的 inPath 就是前面创建的 /data/local/tmp/ 目录!
        final PrintWriter pw = getOutPrintWriter();
        final ParcelFileDescriptor fd;
        //【3】 定义输入流对象,指向待安装 APK 对应文件的源地址
        if (STDIN_PATH.equals(inPath)) {
            fd = new ParcelFileDescriptor(getInFileDescriptor());
        } else if (inPath != null) {
            fd = openFileForSystem(inPath, "r");
            if (fd == null) {
                return -1;
            }
            sizeBytes = fd.getStatSize();
            if (sizeBytes < 0) {
                getErrPrintWriter().println("Unable to get size of: " + inPath);
                return -1;
            }
        } else {
            fd = new ParcelFileDescriptor(getInFileDescriptor());
        }
        if (sizeBytes <= 0) {
            getErrPrintWriter().println("Error: must specify a APK size");
            return 1;
        }

        PackageInstaller.Session session = null;
        try {
        	//【*3.3.2】根据前面创建的 Session id,获得本次安装事务对象!
        	//【*3.2.1】通过 PackageInstallerService.openSession 获得对应的 PackageInstallerSession 对象!
        	//【*2.9.3.1】封装成 Session 实例!
            session = new PackageInstaller.Session(
                    mInterface.getPackageInstaller().openSession(sessionId));
            //【*2.9.3.1.1】 定义输出流对象,指向待安装 APK 对应文件的源地址!
            session.write(splitName, 0, sizeBytes, fd);

            if (logSuccess) {
                pw.println("Success: streamed " + sizeBytes + " bytes");
            }
            //【3】拷贝完成,返回 STATUS_SUCCESS!
            return 0;
        } catch (IOException e) {
            getErrPrintWriter().println("Error: failed to write; " + e.getMessage());
            return 1;
        } finally {
            IoUtils.closeQuietly(session);
        }
    }

doWriteSession 整个流程其实就是进行 apk 的拷贝操作!

4.2.3.1 new PackageInstaller.Session

位置:./frameworks/base/core/java/android/content/pm/PackageInstaller.java
Session 是对 PackageInstallerSession 的简单封装

	public static class Session implements Closeable {
        private IPackageInstallerSession mSession;

        /** {@hide} */
        public Session(IPackageInstallerSession session) {
            mSession = session;
        }

		public void setProgress(float progress) {
			//...
		}
		public void setStagingProgress(float progress) {
			//...
		}
		public void addProgress(float progress) {
			//...
		}
		public @NonNull OutputStream openWrite(@NonNull String name, long offsetBytes,
                long lengthBytes) throws IOException {
			//...
		}
		public void write(@NonNull String name, long offsetBytes, long lengthBytes,
                @NonNull ParcelFileDescriptor fd) throws IOException {
			//...
		}
		public void fsync(@NonNull OutputStream out) throws IOException {
            if (ENABLE_REVOCABLE_FD) {
			//...
		}
		public @NonNull String[] getNames() throws IOException {
			//...
		}
		public @NonNull InputStream openRead(@NonNull String name) throws IOException {
			//...
		}
		public void removeSplit(@NonNull String splitName) throws IOException {
			//...
		}
		public void commit(@NonNull IntentSender statusReceiver) {
			//...
		}
		@SystemApi
        @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES)
        public void commitTransferred(@NonNull IntentSender statusReceiver) {
        	//...
		}
        public void transfer(@NonNull String packageName)
                throws PackageManager.NameNotFoundException {
        	//...
		}
        @Override
        public void close() {
        	//...
		}
        public void abandon() {
        	//...
		}
    }

mSession 指向了对应的 PackageInstallerSession 实例

同时其还提供了很多借口,来操作 PackageInstallerSession,这里我们用一张图先简单的看下:

Session.setProgress -> PackageInstallerSession.setClientProgress
Session.setStagingProgress -> PackageInstallerSession.setClientProgress
Session.addProgress -> PackageInstallerSession.addClientProgress
Session.openWrite -> PackageInstallerSession.openWrite
Session.openRead -> PackageInstallerSession.openRead
Session.commit -> PackageInstallerSession.commit
Session.abandon -> PackageInstallerSession.abandon
… … …

2.4.3.1.1 Session.openWrite

这里的 name 传入的是 base.apk:

        public @NonNull OutputStream openWrite(@NonNull String name, long offsetBytes,
                long lengthBytes) throws IOException {
            try {
                if (ENABLE_REVOCABLE_FD) {
                    return new ParcelFileDescriptor.AutoCloseOutputStream(
                            mSession.openWrite(name, offsetBytes, lengthBytes));
                } else {
                	//【*4.1】这里会调用 PackageInstallerSession 的 openWrite 方法
                	// 将前面创建 Session 的文件目录 /data/app/vmdl[sessionId].tmp 封装成一个文件描述符对象!
                    final ParcelFileDescriptor clientSocket = mSession.openWrite(name,
                            offsetBytes, lengthBytes);
                    //【2】获得该文件描述符输出流
                    return new FileBridge.FileBridgeOutputStream(clientSocket);
                }
            } catch (RuntimeException e) {
                ExceptionUtils.maybeUnwrapIOException(e);
                throw e;
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }

mSession.openWrite 方法涉及到了文件相关处理,这里就不过多关注

2.4.3.1.2 Session.addProgress
        public void addProgress(float progress) {
            try {
            //这里会调用 PackageInstallerSession 的 addClientProgress 方法
	        // 更新文件读写进度!
                mSession.addClientProgress(progress);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }

位置:./frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java

@Override
    public void addClientProgress(float progress) {
        synchronized (mLock) {
            assertCallerIsOwnerOrRootLocked();
			//调用了 setClientProgress 方法
            setClientProgress(mClientProgress + progress);
        }
    }

	@Override
    public void setClientProgress(float progress) {
        synchronized (mLock) {
            assertCallerIsOwnerOrRootLocked();

            // Always publish first staging movement	//【1】用于第一次更新进度
            final boolean forcePublish = (mClientProgress == 0);
            mClientProgress = progress;
            //最终,调用 computeProgressLocked 方法
            computeProgressLocked(forcePublish);
        }
    }
    
    @GuardedBy("mLock")
    private void computeProgressLocked(boolean forcePublish) {
    	//【1】计算进度
        mProgress = MathUtils.constrain(mClientProgress * 0.8f, 0f, 0.8f)
                + MathUtils.constrain(mInternalProgress * 0.2f, 0f, 0.2f);
		//【2】更新进度
        // Only publish when meaningful change
        if (forcePublish || Math.abs(mProgress - mReportedProgress) >= 0.01) {
            mReportedProgress = mProgress;
            //同时通知事务观察者,进度的变化
            mCallback.onSessionProgressChanged(this, mProgress);
        }
    }

2.4.4 Pm.doCommitSession - 提交事务

位置:./frameworks/base/services/core/java/com/android/server/pm/PackageManagerShellCommand.java

private int doCommitSession(int sessionId, boolean logSuccess) throws RemoteException {
        final PrintWriter pw = getOutPrintWriter();
        PackageInstaller.Session session = null;
        try {
        	//获得前面创建的 Session 对象
            session = new PackageInstaller.Session(
                    mInterface.getPackageInstaller().openSession(sessionId));

            // Sanity check that all .dm files match an apk.
            // (The installer does not support standalone .dm files and will not process them.)
            try {
                DexMetadataHelper.validateDexPaths(session.getNames());
            } catch (IllegalStateException | IOException e) {
                pw.println("Warning [Could not validate the dex paths: " + e.getMessage() + "]");
            }
			//创建了一个 LocalIntentReceiver
            final LocalIntentReceiver receiver = new LocalIntentReceiver();
            //将 receiver.getIntentSender() 传递给 pms,用户客户端进程获得安装结果
            //调用 PackageInstallerSession 的 commit 方法,提交事务
            session.commit(receiver.getIntentSender());
			//处理返回结果
            final Intent result = receiver.getResult();
            final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
                    PackageInstaller.STATUS_FAILURE);
            if (status == PackageInstaller.STATUS_SUCCESS) {
                if (logSuccess) {
                    pw.println("Success");
                }
            } else {
                pw.println("Failure ["
                        + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
            }
            return status;
        } finally {
            IoUtils.closeQuietly(session);
        }
    }

到这里,提交过程就结束了,最终的逻辑是在 PackageInstallerService 中实现

2.4.4.1 LocalIntentReceiver - 跨进程返回结果

LocalIntentReceiver 用来接收安装结果

private static class LocalIntentReceiver {
		//以 Intent 的方式保存安装结果
        private final SynchronousQueue<Intent> mResult = new SynchronousQueue<>();

        private IIntentSender.Stub mLocalSender = new IIntentSender.Stub() {
            @Override
            public void send(int code, Intent intent, String resolvedType, IBinder whitelistToken,
                    IIntentReceiver finishedReceiver, String requiredPermission, Bundle options) {
                try {
                	//将结果保存到 mResult 中
                    mResult.offer(intent, 5, TimeUnit.SECONDS);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        };
		//getIntentSender 将 getIntentSender 封装到 IntentSender 中,由于 IntentSender 实现了 Parcelable,所以可以跨进程传递:
        public IntentSender getIntentSender() {
            return new IntentSender((IIntentSender) mLocalSender);
        }

        public Intent getResult() {
            try {
            	//返回安装结果
                return mResult.take();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

LocalIntentReceiver 有一个内部成员 mLocalSender,实现了 IIntentSender.Stub,用于跨进程通信。
系统进程会持有 mLocalSender 对应的代理对象,通过 send 方法,将安装的状态或者结果返回保存到 mResult 中。

2.4.4.2 LocalIntentReceiver.getIntentSender

上面写了

2.4.4.3 Session.commit

位置:./frameworks/base/core/java/android/content/pm/PackageInstaller.java

        public void commit(@NonNull IntentSender statusReceiver) {
            try {
            	//最终,调用了 PackageInstallerSession 的 commit 方法,进入系统进程
                mSession.commit(statusReceiver, false);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        }

2.4.4.4 LocalIntentReceiver.getResult

上面写了

你可能感兴趣的:(安卓,android,adb,pm,install)