【Android12】Android Framework系列---Adb和PMS安装apk源码流程
Adb和PMS安装apk源码流程
adb install命令
通过adb install命令可以将apk安装到Android系统(注意:特定类型的apk,比如persist类型是无法通过adb安装的)
下述命令中adb解析install命令,并调用Android PackageManagerService进行apk安装。
# -r : replace existing application
# -t : allow test packages\n"
# -d : allow version code downgrade (debuggable packages only)
adb install -r -t -d linduo_test.apk
基于Android12,分析从adb install到 PakcageManagerService安装apk的流程。
源码流程分析
adb install命令的源码实现
Android中adb的源码被放在 “packages/modules/adb”这个仓库中,这个仓库编译后的主要成果物是adbd和**adb。**adbd是a服务端进程,adb是提供的客户端工具。adb和adbd两者间,通过socket完成相互间的通信。
# packages/modules/adb/Android.bp
# adbd,在Android系统中运行
cc_binary {name: "adbd",defaults: ["adbd_defaults", "host_adbd_supported", "libadbd_binary_dependencies"],recovery_available: true,apex_available: ["com.android.adbd"],srcs: ["daemon/main.cpp",],# 省略...
}
# adb,提供给Host端(就是主机PC)
cc_binary_host {name: "adb",stl: "libc++_static",defaults: ["adb_defaults"],srcs: ["client/adb_client.cpp","client/bugreport.cpp","client/commandline.cpp","client/file_sync_client.cpp","client/main.cpp","client/console.cpp","client/adb_install.cpp","client/line_printer.cpp","client/fastdeploy.cpp","client/fastdeploycallbacks.cpp","client/incremental.cpp","client/incremental_server.cpp","client/incremental_utils.cpp","shell_service_protocol.cpp",],# 省略...
}
这里关注adb install命令对应的源码实现,adb这个bin程序对应的main函数定义在“packages/modules/adb/client/main.cpp”,main函数中调用adb_trace_init进行Trace(记录运行Log)的初始化,然后调用adb_commandline解析adb命令参数。
int main(int argc, char* argv[], char* envp[]) {__adb_argv = const_cast<const char**>(argv);__adb_envp = const_cast<const char**>(envp);adb_trace_init(argv);// 这里忽略了第一个入参(程序名自己)return adb_commandline(argc - 1, const_cast<const char**>(argv + 1));
}
adb_commandline函数在"packages/modules/adb/client/commandline.cpp"中实现,在这个函数中解析adb命令的参数。如果是adb install命令,会调用install_app函数进行APK安装的相关流程。
int adb_commandline(int argc, const char** argv) {// 省略...// 解析各个参数while (argc > 0) {// 解析第一个参数。就是adb命令后第一个参数if (!strcmp(argv[0], "server")) {// 省略了很多代码...} else {// adb install 的情况下走这里,跳出循环/* out of recognized modifiers and flags */break;}argc--;argv++;}// 省略.../* adb_connect() commands */if (!strcmp(argv[0], "devices")) {// 省略了很多代码(对各个命令的判断)} else if (!strcmp(argv[0], "install")) {// 判断参数为install,执行app安装相关函数if (argc < 2) error_exit("install requires an argument");return install_app(argc, argv);} else if (!strcmp(argv[0], "install-multiple")) {if (argc < 2) error_exit("install-multiple requires an argument");return install_multiple_app(argc, argv);} else if (!strcmp(argv[0], "install-multi-package")) {if (argc < 2) error_exit("install-multi-package requires an argument");return install_multi_package(argc, argv);} else if (!strcmp(argv[0], "uninstall")) {if (argc < 2) error_exit("uninstall requires an argument");return uninstall_app(argc, argv);} else if (!strcmp(argv[0], "sync")) {// 省略...}// 省略...// 如果输入了错误的参数,会报下面的错误,并结束此次adb命令的执行error_exit("unknown command %s", argv[0]);__builtin_unreachable();
}
install_app函数在“packages/modules/adb/client/adb_install.cpp”中定义。该函数先判断APK的安装模式是
INSTALL_PUSH/INSTALL_STREAM/INSTALL_INCREMENTAL中哪一种。默认情况下是INSTALL_PUSH模式,然后调用install_app_legacy函数进行APK安装。
- INSTALL_PUSH:通过推送的方式安装APK,将APK整个推送到设置上进行安装。
- INSTALL_STREAM:流式传输安装APK,APK被分割成多份并在安装过程中逐个传输到系统设置上进行组装和安装,这种方式可以减少安装的时间(如果系统支持,默认会使用这种方式安装)
- INSTALL_INCREMENTAL:增量方式安装APK,仅安装差异的部分不需要重新安装整个Apk,加速apk安装时间。大型的APK(如GB级别)应用较多(需要系统支持)
int install_app(int argc, const char** argv) {InstallMode install_mode = INSTALL_DEFAULT;auto incremental_request = CmdlineOption::None;bool incremental_wait = false;bool use_fastdeploy = false;FastDeploy_AgentUpdateStrategy agent_update_strategy = FastDeploy_AgentUpdateDifferentVersion;// 进行安装默认的判断,通过parse_install_mode/parse_fast_deploy_mode/calculate_install_mode实现// 具体实现方式请自行查看源码,这里就不关注了auto unused_argv = parse_install_mode({argv, argv + argc}, &install_mode, &incremental_request,&incremental_wait);auto passthrough_argv =parse_fast_deploy_mode(std::move(unused_argv), &use_fastdeploy, &agent_update_strategy);auto [primary_mode, fallback_mode] =calculate_install_mode(install_mode, use_fastdeploy, incremental_request);// 省略...// adb install默认使用INSTALL_PUSH方式安装auto run_install_mode = [&](InstallMode install_mode, bool silent) {switch (install_mode) {case INSTALL_PUSH:return install_app_legacy(passthrough_argv.size(), passthrough_argv.data(),use_fastdeploy);case INSTALL_STREAM:return install_app_streamed(passthrough_argv.size(), passthrough_argv.data(),use_fastdeploy);case INSTALL_INCREMENTAL:return install_app_incremental(passthrough_argv.size(), passthrough_argv.data(),incremental_wait, silent);case INSTALL_DEFAULT:default:error_exit("invalid install mode");}};// 如果安装失败,会使用fallback mode再次安装。默认情况下,fallback mode是空(不进行安装)auto res = run_install_mode(primary_mode, fallback_mode.has_value());if (res && fallback_mode.value_or(primary_mode) != primary_mode) {res = run_install_mode(*fallback_mode, false);}return res;
}
install_app_legacy函数定义在"packages/modules/adb/client/adb_install.cpp"中。函数执行时,会在终端输出"Performing Push Install"提示使用者。函数中判断adb install命令是否以.apk字符结束,并将apk文件push到/data/local/tmp/目录下。然后调用pm_command函数安装/data/local/tmp/目录下的apk文件。最后将/data/local/tmp/下面的apk文件删除。
static int install_app_legacy(int argc, const char** argv, bool use_fastdeploy) {printf("Performing Push Install\n");// 这里判断 adb install命令是否以 .apk结尾。如果没有就认为没有提供待安装apk// Find last APK argument.// All other arguments passed through verbatim.int last_apk = -1;for (int i = argc - 1; i >= 0; i--) {if (android::base::EndsWithIgnoreCase(argv[i], ".apex")) {error_exit("APEX packages are only compatible with Streamed Install");}if (android::base::EndsWithIgnoreCase(argv[i], ".apk")) {last_apk = i;break;}}// 如果在adb install的命令最后字符 不是.apk的形式,报错。if (last_apk == -1) error_exit("need APK file on command line");// 创建临时目录 /data/local/tmp/xxx.apk,存放待安装的apkint result = -1;std::vector<const char*> apk_file = {argv[last_apk]};std::string apk_dest = "/data/local/tmp/" + android::base::Basename(argv[last_apk]);// 注意,这里将adb install 中apk文件的路径,换成了 /data/local/tmp/xxx.apkargv[last_apk] = apk_dest.c_str(); /* destination name, not source location */// 如果使用快速部署(默认为false)if (use_fastdeploy) {// 省略 ...}// 将Apk push到/data/local/tmp/目录下if (do_sync_push(apk_file, apk_dest.c_str(), false, CompressionType::Any, false)) {// 执行安装result = pm_command(argc, argv);// 安装完成后,删除临时目录/data/local/tmp/下的apk文件。delete_device_file(apk_dest);}return result;
}
pm_command函数,顾名思义给执行pm(PackageManager)相关的命令。所以在函数中,为字符串拼接了 "pm"字符(aosp提供的pm命令)
// packages/modules/adb/client/adb_install.cpp
static int pm_command(int argc, const char** argv) {std::string cmd = "pm";while (argc-- > 0) {cmd += " " + escape_arg(*argv++);}return send_shell_command(cmd);
}// packages/modules/adb/client/commandline.cppint send_shell_command(const std::string& command, bool disable_shell_protocol,StandardStreamsCallbackInterface* callback) {unique_fd fd;bool use_shell_protocol = false;while (true) {bool attempt_connection = true;// Use shell protocol if it's supported and the caller doesn't explicitly// disable it.if (!disable_shell_protocol) {auto&& features = adb_get_feature_set(nullptr);if (features) {use_shell_protocol = CanUseFeature(*features, kFeatureShell2);} else {// Device was unreachable.attempt_connection = false;}}if (attempt_connection) {std::string error;std::string service_string = ShellServiceString(use_shell_protocol, "", command);// 将命令发送给adbd,由adbd执(注意adb运行在Host端,比如PC。adbd才是运行在Android系统上的服务)fd.reset(adb_connect(service_string, &error));if (fd >= 0) {break;}}fprintf(stderr, "- waiting for device -\n");if (!wait_for_device("wait-for-device")) {return 1;}}return read_and_dump(fd.get(), use_shell_protocol, callback);
}
adbd服务端的处理流程
adbd(daemon进程)在收到Client端(adb)请求,并执行相关动作。在函数StartCommandInProcess中,执行客户端发过来的 “shell pm … /data/local/temp/xxx.apk"命令。
int main(int argc, char* const argv[]) {signal(SIGPIPE, SIG_IGN);int fd = STDIN_FILENO;std::string data;while (true) {std::string error;// 读取客户端的请求if (!ReadProtocolString(fd, &data, &error)) {PLOG(ERROR) << "Failed to read message: " << error;break;}std::string_view name = data;auto protocol = SubprocessProtocol::kShell;if (android::base::ConsumePrefix(&name, "abb:")) {protocol = SubprocessProtocol::kShell;} else if (android::base::ConsumePrefix(&name, "abb_exec:")) {protocol = SubprocessProtocol::kNone;} else {LOG(FATAL) << "Unknown command prefix for abb: " << data;}// 处理请求命令,具体实现可以参考源码。// 基本上就是创建了一个Process,在Process中执行传过来的命令。unique_fd result = StartCommandInProcess(std::string(name), &execCmd, protocol);int max_buf = LINUX_MAX_SOCKET_SIZE;adb_setsockopt(result, SOL_SOCKET, SO_SNDBUF, &max_buf, sizeof(max_buf));// 处理结果发送给客户端if (android::base::SendFileDescriptors(fd, "", 1, result.get()) != 1) {PLOG(ERROR) << "Failed to send an inprocess fd for command: " << data;break;}}
}
pm命令的处理流程
上述流程中最终执行的是 pm install -r -t -d /data/local/tmp/linduo.apk。Android pm命令实际上是一段bash程序。实现在frameworks/base/cmds/pm/pm中,只是简单的调用了cmd package,并将入参传了进去。
#!/system/bin/sh
cmd package "$@"
cmd是一个bin程序,其实现在frameworks/native/cmds/cmd/中。
# frameworks/native/cmds/cmd/Android.bp
package {default_applicable_licenses: ["frameworks_native_cmds_cmd_license"],
}// Added automatically by a large-scale-change
// See: http://go/android-license-faq
license {name: "frameworks_native_cmds_cmd_license",visibility: [":__subpackages__"],license_kinds: ["SPDX-license-identifier-Apache-2.0",],license_text: ["NOTICE",],
}cc_library_static {name: "libcmd",srcs: ["cmd.cpp"],export_include_dirs: ["."],shared_libs: ["libutils","liblog","libselinux","libbinder",],cflags: ["-Wall","-Werror","-DXP_UNIX",],
}cc_binary {name: "cmd",srcs: ["main.cpp"],static_libs: ["libcmd",],shared_libs: ["libutils","liblog","libselinux","libbinder",],cflags: ["-Wall","-Werror","-DXP_UNIX",],
}
如果调用的是cmd package,cmd程序中会通过Android 的ServiceManager连接到PMS服务。然后将命令通过binder发送给PMS服务。
// frameworks/native/cmds/cmd/main.cpp
#include <unistd.h>#include "cmd.h"int main(int argc, char* const argv[]) {signal(SIGPIPE, SIG_IGN);// 这里将入参的第一个参数去掉。比如 cmd package xxxxx,去掉后就是 package xxxxstd::vector<std::string_view> arguments;arguments.reserve(argc - 1);// 0th argument is a program name, skipping.for (int i = 1; i < argc; ++i) {arguments.emplace_back(argv[i]);}// main程序中调用cmdMainreturn cmdMain(arguments, android::aout, android::aerr, STDIN_FILENO, STDOUT_FILENO,STDERR_FILENO, RunMode::kStandalone);
}// frameworks/native/cmds/cmd/cmd.cpp
int cmdMain(const std::vector<std::string_view>& argv, TextOutput& outputLog, TextOutput& errorLog,int in, int out, int err, RunMode runMode) {sp<ProcessState> proc = ProcessState::self();proc->startThreadPool();#if DEBUGALOGD("cmd: starting");
#endif// 获取ServiceManager对象sp<IServiceManager> sm = defaultServiceManager();// 这里判断一下,cmd命令中想要调用的服务在当前系统中是否存在。不存在则返回if ((argc == 1) && (argv[0] == "-l")) {Vector<String16> services = sm->listServices();services.sort(sort_func);outputLog << "Currently running services:" << endl;for (size_t i=0; i<services.size(); i++) {sp<IBinder> service = sm->checkService(services[i]);if (service != nullptr) {outputLog << " " << services[i] << endl;}}return 0;}// 判断是否输入了-w选项。bool waitForService = ((argc > 1) && (argv[0] == "-w"));int serviceIdx = (waitForService) ? 1 : 0;const auto cmd = argv[serviceIdx];Vector<String16> args;String16 serviceName = String16(cmd.data(), cmd.size());for (int i = serviceIdx + 1; i < argc; i++) {args.add(String16(argv[i].data(), argv[i].size()));}sp<IBinder> service;if(waitForService) {service = sm->waitForService(serviceName);} else {service = sm->checkService(serviceName);}if (service == nullptr) {if (runMode == RunMode::kStandalone) {ALOGW("Can't find service %.*s", static_cast<int>(cmd.size()), cmd.data());}errorLog << "cmd: Can't find service: " << cmd << endl;return 20;}sp<MyShellCallback> cb = new MyShellCallback(errorLog);sp<MyResultReceiver> result = new MyResultReceiver();#if DEBUGALOGD("cmd: Invoking %.*s in=%d, out=%d, err=%d",static_cast<int>(cmd.size()), cmd.data(), in, out, err);
#endif// 这里将命令发送给对应的服务端执行。// TODO: block until a result is returned to MyResultReceiver.status_t error = IBinder::shellCommand(service, in, out, err, args, cb, result);// 省略...
}
PackageManagerService接收安装Apk的请求,将Apk安装到Android系统
adb将安装apk的命令发送到adbd(服务端),adbd通过binder调用PMS服务安装APK。PMS接收到shell调用后,会创建PackageManagerShellCommand对象并调用它exec函数的进行处理。
@Override
public void onShellCommand(FileDescriptor in, FileDescriptor out,FileDescriptor err, String[] args, ShellCallback callback,ResultReceiver resultReceiver) {(new PackageManagerShellCommand(this, mContext,mDomainVerificationManager.getShell())).exec(this, in, out, err, args, callback, resultReceiver);
}
PackageManagerShellCommand的exec函数,有其继承的父类定义。该函数中会调用onCommand解析命令并调用不同的处理分支。对于本文中的情况(执行INSTALL_PUSH方式安装)会调用runInstall函数进行处理。runInstall函数中调用makeInstallParams将组织APK安装的参数,然后调用doRunInstall进程安装。
// frameworks/base/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@Override
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 "resolve-activity":return runResolveActivity();case "query-activities":return runQueryIntentActivities();case "query-services":return runQueryIntentServices();case "query-receivers":return runQueryIntentReceivers();case "install":return runInstall();case "install-streaming":return runStreamingInstall();case "install-incremental":return runIncrementalInstall();// 省略...} catch (RemoteException e) {pw.println("Remote exception: " + e);}return -1;
}private int runInstall() throws RemoteException {return doRunInstall(makeInstallParams());
}
makeInstallParams函数会根据adb install [apk相关参数] 时输入的参数进行组织。其实现如下
// frameworks/base/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
// 例如: adb install -t -r -d linduo.apk(注意安装路径会被替换成 /data/local/temp/linduo.apk )
private InstallParams makeInstallParams() {final SessionParams sessionParams = new SessionParams(SessionParams.MODE_FULL_INSTALL);final InstallParams params = new InstallParams();params.sessionParams = sessionParams;// Allowlist all permissions by defaultsessionParams.installFlags |= PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS;String opt;boolean replaceExisting = true;boolean forceNonStaged = false;while ((opt = getNextOption()) != null) {switch (opt) {case "-r": // ignorebreak;case "-R":replaceExisting = false;break;case "-i":params.installerPackageName = getNextArg();if (params.installerPackageName == null) {throw new IllegalArgumentException("Missing installer package");}break;case "-t": sessionParams.installFlags |= PackageManager.INSTALL_ALLOW_TEST;break;case "-f":sessionParams.installFlags |= PackageManager.INSTALL_INTERNAL;break;case "-d":sessionParams.installFlags |= PackageManager.INSTALL_REQUEST_DOWNGRADE;break;case "-g":sessionParams.installFlags |= PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS;break;case "--restrict-permissions":sessionParams.installFlags &=~PackageManager.INSTALL_ALL_WHITELIST_RESTRICTED_PERMISSIONS;break;case "--dont-kill":sessionParams.installFlags |= PackageManager.INSTALL_DONT_KILL_APP;break;case "--originating-uri":sessionParams.originatingUri = Uri.parse(getNextArg());break;case "--referrer":sessionParams.referrerUri = Uri.parse(getNextArg());break;case "-p":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":sessionParams.abiOverride = checkAbiArgument(getNextArg());break;case "--ephemeral":case "--instant":case "--instantapp":sessionParams.setInstallAsInstantApp(true /*isInstantApp*/);break;case "--full":sessionParams.setInstallAsInstantApp(false /*isInstantApp*/);break;case "--preload":sessionParams.setInstallAsVirtualPreload();break;case "--user":params.userId = UserHandle.parseUserArg(getNextArgRequired());break;case "--install-location":sessionParams.installLocation = Integer.parseInt(getNextArg());break;case "--install-reason":sessionParams.installReason = 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": // ignorebreak;case "--apex":sessionParams.setInstallAsApex();sessionParams.setStaged();break;case "--force-non-staged":forceNonStaged = true;break;case "--multi-package":sessionParams.setMultiPackage();break;case "--staged":sessionParams.setStaged();break;case "--force-queryable":sessionParams.setForceQueryable();break;case "--enable-rollback":if (params.installerPackageName == null) {// com.android.shell has the TEST_MANAGE_ROLLBACKS// permission needed to enable rollback for non-module// packages, which is likely what the user wants when// enabling rollback through the shell command. Set// the installer to com.android.shell if no installer// has been provided so that the user doesn't have to// remember to set it themselves.params.installerPackageName = "com.android.shell";}sessionParams.installFlags |= PackageManager.INSTALL_ENABLE_ROLLBACK;break;case "--staged-ready-timeout":params.stagedReadyTimeoutMs = Long.parseLong(getNextArgRequired());break;case "--skip-verification":sessionParams.installFlags |= PackageManager.INSTALL_DISABLE_VERIFICATION;break;default:throw new IllegalArgumentException("Unknown option " + opt);}}if (replaceExisting) {sessionParams.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;}if (forceNonStaged) {sessionParams.isStaged = false;}return params;
}
doRunInstall函数根据安装参数进行APK的安装,该函数主要做了两个事情。一个是通过doCreateSession函数,创建了PackageInstallerSession对象,做了些Apk安装准工作。另一个是通过doCommitSession函数,利用PackageInstaller.Session对象进行apk的安装。
private int doRunInstall(final InstallParams params) throws RemoteException {final PrintWriter pw = getOutPrintWriter();// 判断安装类型final boolean isStreaming = params.sessionParams.dataLoaderParams != null;final boolean isApex =(params.sessionParams.installFlags & PackageManager.INSTALL_APEX) != 0;ArrayList<String> args = getRemainingArgs();final boolean fromStdIn = args.isEmpty() || STDIN_PATH.equals(args.get(0));final boolean hasSplits = args.size() > 1;if (fromStdIn && params.sessionParams.sizeBytes == -1) {pw.println("Error: must either specify a package size or an APK file");return 1;}if (isApex && hasSplits) {pw.println("Error: can't specify SPLIT(s) for APEX");return 1;}if (!isStreaming) {if (fromStdIn && hasSplits) {pw.println("Error: can't specify SPLIT(s) along with STDIN");return 1;}if (args.isEmpty()) {args.add(STDIN_PATH);} else {setParamsSize(params, args);}}// 创建SessionID(PMS会创建PackageInstallerSession对象),可以理解成创建了安装APK的任务// 通过调用PackageInstallerService的createSession实现final int sessionId = doCreateSession(params.sessionParams,params.installerPackageName, params.userId);boolean abandonSession = true;try {if (isStreaming) {if (doAddFiles(sessionId, args, params.sessionParams.sizeBytes, isApex)!= PackageInstaller.STATUS_SUCCESS) {return 1;}} else {if (doWriteSplits(sessionId, args, params.sessionParams.sizeBytes, isApex)!= PackageInstaller.STATUS_SUCCESS) {return 1;}}// 安装APK(通过提交sessionID),主要关注这个函数。if (doCommitSession(sessionId, false /*logSuccess*/)!= PackageInstaller.STATUS_SUCCESS) {return 1;}abandonSession = false;if (params.sessionParams.isStaged && params.stagedReadyTimeoutMs > 0) {return doWaitForStagedSessionReady(sessionId, params.stagedReadyTimeoutMs, pw);}pw.println("Success");return 0;} finally {if (abandonSession) {try {doAbandonSession(sessionId, false /*logSuccess*/);} catch (Exception ignore) {}}}
}
doCommitSession函数中创建了PackageInstaller.Session对象,通过与上面创建的sessionId进行绑定。然后调用PackageInstaller.Session.commit进行APK安装。
private int doCommitSession(int sessionId, boolean logSuccess)throws RemoteException {final PrintWriter pw = getOutPrintWriter();PackageInstaller.Session session = null;try {session = new PackageInstaller.Session(mInterface.getPackageInstaller().openSession(sessionId));if (!session.isMultiPackage() && !session.isStaged()) {// Validity 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() + "]");}}final LocalIntentReceiver receiver = new LocalIntentReceiver();//调用commit安装APKsession.commit(receiver.getIntentSender());final Intent result = receiver.getResult();final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,PackageInstaller.STATUS_FAILURE);// 判断APK安装状态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);}
}
PackageInstaller.Session的实现如下,调用了IPackageInstallerSession(通过mInterface.getPackageInstaller().openSession(sessionId)取得)对象的commit函数。
public static class Session implements Closeable {public Session(IPackageInstallerSession session) {mSession = session;}public void commit(@NonNull IntentSender statusReceiver) {try {mSession.commit(statusReceiver, false);} catch (RemoteException e) {throw e.rethrowFromSystemServer();}}
}
PackageInstallerSession的commit函数,实现在"frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java“。commit函数中,会将处理转到自身的Handle中。通过一系列调用commit -> dispatchSessionSealed -> handleSessionSealed -> dispatchStreamValidateAndCommit -> handleStreamValidateAndCommit -> handleInstall -> verify -> verifyNonStaged,在verifyNonStaged函数中,调用PMS的verifyStage验证APK。验证成功后,通过onVerificationComplete回调,调用install函数,安装APK
// frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
@Override
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {// 省略dispatchSessionSealed();
}private void dispatchSessionSealed() {// 转到Handle线程处理mHandler.obtainMessage(MSG_ON_SESSION_SEALED).sendToTarget();
}private void handleSessionSealed() {assertSealed("dispatchSessionSealed");// Persist the fact that we've sealed ourselves to prevent// mutations of any hard links we create.mCallback.onSessionSealedBlocking(this);dispatchStreamValidateAndCommit();
}private void dispatchStreamValidateAndCommit() {mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget();
}private void handleStreamValidateAndCommit() {// 省略mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
}private void handleInstall() {// 检查、验证APK包verify();
}private void verify() {try {verifyNonStaged();} catch (PackageManagerException e) {final String completeMsg = ExceptionUtils.getCompleteMessage(e);onSessionVerificationFailure(e.error, completeMsg);}
}
// 调用PMS的verifyStage,验证APK。验证成功会,调用install方法进行安装。
private void verifyNonStaged()throws PackageManagerException {final PackageManagerService.VerificationParams verifyingSession =prepareForVerification();if (verifyingSession == null) {return;}if (isMultiPackage()) {// 省略mPm.verifyStage(verifyingSession, verifyingChildSessions);} else {mPm.verifyStage(verifyingSession);}
}// 验证成功,通过回调。调用install开始安装APK
private void onVerificationComplete() {// Staged sessions will be installed later during bootif (isStaged()) {// TODO(b/136257624): Remove this once all verification logic has been transferred out// of StagingManager.mStagingManager.notifyPreRebootVerification_Apk_Complete(mStagedSession);// TODO(b/136257624): We also need to destroy internals for verified staged session,// otherwise file descriptors are never closed for verified staged session until rebootreturn;}install();
}
在install函数中,调用了PMS的installStage函数,对APK进行了安装。
// frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
private void install() {try {installNonStaged();} catch (PackageManagerException e) {final String completeMsg = ExceptionUtils.getCompleteMessage(e);onSessionInstallationFailure(e.error, completeMsg);}
}调用PMS的installStage函数,安装APK
private void installNonStaged()throws PackageManagerException {final PackageManagerService.InstallParams installingSession = makeInstallParams();if (installingSession == null) {throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,"Session should contain at least one apk session for installation");}if (isMultiPackage()) {// 省略...mPm.installStage(installingSession, installingChildSessions);} else {mPm.installStage(installingSession);}
}
PackageManagerService的installStage会对APK进行实际的安装。
// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
void installStage(InstallParams params) {// 首先进行copyfinal Message msg = mHandler.obtainMessage(INIT_COPY);params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));msg.obj = params;Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "installStage",System.identityHashCode(msg.obj));Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "queueInstall",System.identityHashCode(msg.obj));mHandler.sendMessage(msg);
}
转Handle处理INIT_COPY,调用startCopy函数,在startCopy函数中调用handleStartCopy和handleReturnCode进行APK的拷贝,从/data/local/tmp/目录把APK文件拷贝拷贝到/data/app/包名/目录。然后调用processInstallRequestsAsync进行安装。
// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
private abstract class HandlerParams {final void startCopy() {if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);handleStartCopy();handleReturnCode();}
}class InstallParams extends HandlerParams {// 根据安装Flag,进行预处理public void handleStartCopy() {if ((installFlags & PackageManager.INSTALL_APEX) != 0) {mRet = INSTALL_SUCCEEDED;return;}PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,mPackageLite, origin.resolvedPath, installFlags, packageAbiOverride);// For staged session, there is a delay between its verification and install. Device// state can change within this delay and hence we need to re-verify certain conditions.boolean isStaged = (installFlags & INSTALL_STAGED) != 0;if (isStaged) {mRet = verifyReplacingVersionCode(pkgLite, requiredInstalledVersionCode, installFlags);if (mRet != INSTALL_SUCCEEDED) {return;}}mRet = overrideInstallLocation(pkgLite);}@Overridevoid handleReturnCode() {processPendingInstall();}private void processPendingInstall() {InstallArgs args = createInstallArgs(this);if (mRet == PackageManager.INSTALL_SUCCEEDED) {// 这里会对apk进行拷贝。拷贝到/data/app/包名/ 目录下mRet = args.copyApk();}if (mRet == PackageManager.INSTALL_SUCCEEDED) {// 解压apk相关的一些资源F2fsUtils.releaseCompressedBlocks(mContext.getContentResolver(), new File(args.getCodePath()));}if (mParentInstallParams != null) {// 安装多个APKmParentInstallParams.tryProcessInstallRequest(args, mRet);} else {// 安装一个APKPackageInstalledInfo res = createPackageInstalledInfo(mRet);processInstallRequestsAsync(res.returnCode == PackageManager.INSTALL_SUCCEEDED,Collections.singletonList(new InstallRequest(args, res)));}}
}
processInstallRequestsAsync 异步安装APK。最终会调用PMS的installPackagesLI函数进行安装。安装流程包括四个流程:Prepare、Scan、Reconcile、Commit。安装后系统关于该APK的信息会更新,启动该APK时会以新的信息进行启动(安装成功状态)。
// frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
// Queue up an async operation since the package installation may take a little while.
private void processInstallRequestsAsync(boolean success,List<InstallRequest> installRequests) {mHandler.post(() -> {// 省略for (InstallRequest request : apkInstallRequests) {restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult,new PostInstallData(request.args, request.installResult, null));}});
}// Queue up an async operation since the package installation may take a little while.
private void processInstallRequestsAsync(boolean success,List<InstallRequest> installRequests) {mHandler.post(() -> {// 省略...if (success) {for (InstallRequest request : apkInstallRequests) {request.args.doPreInstall(request.installResult.returnCode);}synchronized (mInstallLock) {// 安装APKinstallPackagesTracedLI(apkInstallRequests);}for (InstallRequest request : apkInstallRequests) {request.args.doPostInstall(request.installResult.returnCode, request.installResult.uid);}}for (InstallRequest request : apkInstallRequests) {restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult,new PostInstallData(request.args, request.installResult, null));}});
}@GuardedBy("mInstallLock")
private void installPackagesTracedLI(List<InstallRequest> requests) {try {Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");installPackagesLI(requests);} finally {Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);}
}@GuardedBy("mInstallLock")
private void installPackagesLI(List<InstallRequest> requests) {// 省略..// 安装APK Prepare-> Scan -> Reconcile -> Commit// Prepare: Analyzes any current install state, parses the package and does initial// Scan:Interrogates the parsed packages given the context collected in prepare// Reconcile:Validates scanned packages in the context of each other and the current system // state to ensure that the install will be successful// Commit:Commits all scanned packages and updates system state.
}
总结
通过adb install安装apk,大概流程是 adb(client)端通过socke命令给adbd(service),然后adbd通过ServiceManager以Binder方式将命令发送给PackageManagerService。PackageManagerService解析命令,复制APK及相关资源到指定路径,并更新系统中APK相关配置信息。
相关文章:

【Android12】Android Framework系列---Adb和PMS安装apk源码流程
Adb和PMS安装apk源码流程 adb install命令 通过adb install命令可以将apk安装到Android系统(注意:特定类型的apk,比如persist类型是无法通过adb安装的) 下述命令中adb解析install命令,并调用Android PackageManagerS…...

web漏洞总结大全(基础)
前言 本文章是和cike_y师傅一起写的,cike_y博客:https://blog.csdn.net/weixin_53912233?typeblog 也欢迎大家对本文章进行补充和指正,共同维护这个项目,本文的github项目地址: https://github.com/baimao-box/Sum…...

获取双异步返回值时,如何保证主线程不阻塞?
目录 一、前情提要二、JDK8的CompletableFuture1、ForkJoinPool2、从ForkJoinPool和ThreadPoolExecutor探索CompletableFuture和Future的区别 三、通过CompletableFuture优化 “通过Future获取异步返回值”1、通过Future获取异步返回值关键代码(1)将异步…...

hosts文件修改后无法保存的解决办法
目录 第一步 右键点击C盘里的hosts文件,选择重命名。 第二步 在桌面新建一个txt文件,命名为hosts。并把自己需要的内容写入保存。 第三步 把hosts.txt文件复制到原本hosts文件的路径下。右键选中hosts.txt文件,选择重命名,去掉…...

源码篇--Redis 五种数据类型
文章目录 前言一、 字符串类型:1.1 字符串的编码格式:1.1.1 raw 编码格式:1.1.2 empstr编码格式:1.1.3 int 编码格式:1.1.4 字符串存储结构展示: 二、 list类型:2.1 List 底层数据支持:2.2 List 源码实现:2.3 List 结构…...

Sulfo Cy2 Biotin,水溶性 Cy2 生物素,能够与各种氨基基团特异性结合
您好,欢迎来到新研之家 文章关键词:Sulfo Cyanine2 Biotin,Sulfo Cy2 Biotin,水溶性 Cy2 生物素,Sulfo-Cy2-Biotin,水溶性-Cy2-生物素 一、基本信息 产品简介:Sulfo Cyanine2 Biotin, also k…...

NineData支持制定安全、可靠的SQL开发规范
在和数据库打交道中,不管是数据库管理员(DBA)还是开发人员,经常会做一些CURD操作。因为每个人对数据库的了解程度不一样,所以在项目上线时,往往还需要专职人员对数据库的CURD操作进行审核,确保C…...

LSTM时间序列预测
本文借鉴了数学建模清风老师的课件与思路,可以点击查看链接查看清风老师视频讲解:【1】演示:基于LSTM深度学习网络预测时间序列(MATLAB工具箱)_哔哩哔哩_bilibili % Forecast of time series based on LSTM deep learn…...

Rocky8 顺利安装 Airflow 并解决数据库报错问题
rocky是替代centos的服务器系统,稳定可靠。rocky8会比centos7新,可以支持更多服务软件的安装,免去升级各种库的麻烦,本文运行airflow服务就用rocky8系统。airflow是一个定时任务管理系统,功能强大,目前是ap…...

[足式机器人]Part2 Dr. CAN学习笔记- 最优控制Optimal Control Ch07-3 线性二次型调节器(LQR)
本文仅供学习使用 本文参考: B站:DR_CAN Dr. CAN学习笔记 - 最优控制Optimal Control Ch07-3 线性二次型调节器(LQR) 1. 数学推导2. 案例反洗与代码详解 1. 数学推导 2. 案例反洗与代码详解...

Eyes Wide Shut? Exploring the Visual Shortcomings of Multimodal LLMs
大开眼界?探索多模态模型种视觉编码器的缺陷。 论文中指出,上面这些VQA问题,人类可以瞬间给出正确的答案,但是多模态给出的结果却是错误的。是哪个环节出了问题呢?视觉编码器的问题?大语言模型出现了幻觉&…...

汤姆·齐格弗里德《纳什均衡与博弈论》笔记(4)博弈论与人性
第五章 弗洛伊德的梦——博弈和大脑 大脑和经济学 曾经有一段时间——就像在弗洛伊德的年代——心理学家们无法准确地回答人类行为背后的大脑机制。但随着现代神经科学的兴起,情形改变了。比如,人类的情绪不再像过去一样是个谜。科学家们可以观察当人们…...

MacOS平台翻译OCR软件,双管齐下,还可自定义插件,为其添砖加瓦!
小编昨天为大家分享了Windows系统下的一款功能强大且免费的 OCR 开源工具 Umi-OCR。 今天则为大家推荐一款 MacOS系统下的一款 翻译 OCR 多功能双管齐下的桌面应用软件 Bob。这款软件虽然也上线了GitHub,但它不是一款开源软件,仓库只是作者为了用户反馈…...

使用docker配置semantic slam
一.Docker环境配置 1.拉取Docker镜像 sudo docker pull ubuntu:16.04拉取的为ununtu16版本镜像,环境十分干净,可以通过以下命令查看容器列表 sudo docker images 如果想删除多余的docker image,可以使用指令 sudo docker rmi -f <id&g…...

面试常问的Spring AOP底层原理
AOP底层原理可以划分成四个阶段:创建代理对象阶段、拦截目标对象阶段、调用代理对象阶段、调用目标对象阶段 第一阶段:创建代理对象阶段 通过getBean()方法创建Bean实例根据AOP的配置匹配目标类的类名,判断是否满足切…...

C++拾遗(四)引用与指针
引用和指针是两种不同的概念,尽管它们在某些方面有一些相似之处,但它们在功能和用途上是有所区别 声明与定义 引用:引用是别名,是对已存在变量的另一个称呼,一旦一个变量被引用,就不能再被引用其他变 量…...

k8s架构、工作流程、集群组件详解
目录 k8s概述 特性 作用(为什么使用) k8s架构 k8s工作流程 k8s集群架构与组件 核心组件详解 Master节点 Kube-apiserver Kube-controller-manager Kube-scheduler 存储中心 etcd Node Kubelet Kube-Proxy 网络通信模型 容器引擎 k8s核…...

CF1362C Johnny and Another Rating Drop(二进制、复杂度考虑)
看完数据范围 n ∈ [ 1 , 1 e 18 ] n\in[1,1e18] n∈[1,1e18]就可以先猜一下要不是可以直接推公式,不能暴力去做,更不能遍历一遍,又看到这种2进制的题目,要猜是不是 l o g log log级别的复杂度。 可以依次考虑每一位 所有 i % 2 …...

1 - 搭建Redis数据库服务器|LNP+Redis
搭建Redis数据库服务器|LNPRedis 搭建Redis数据库服务器相关概念Redis介绍安装RedisRedis服务常用管理命令命令set 、 mset 、 get 、 mget命令keys 、 type 、 exists 、 del命令ttl 、 expire 、 move 、 flushdb 、flushall 、save、shutdown 配置文件解析 LNP …...

米贸搜|Facebook“精准营销”越来越难?或许是“受众定位”没彻底搞清!
一、为何要确定目标受众 对于每个广告主而言,面向最有可能成为其客户的用户营销非常重要,因此,确定目标受众,是Facebook广告投放中极其重要的一环。 二、什么是目标受众? 目标受众是您希望向其传达营销信息…...

【C++】内存分区模型
目录 1.程序运行前 2.程序运行后 3. new操作符 3.1 基本语法 3.2 开辟数组 C程序在执行时,将内存大方向划分为4个区域 代码区:存放函数体的二进制代码,由操作系统进行管理的 全局区: 存放全局变量和静态变量以及常量栈区 …...

通过strings二进制文件分析工具排查 version ‘GLIBC_2.25‘ not found 报错
strings命令简介 strings命令用于打印文件中可打印字符串, 可以打印文本文件、可执行程序,库文件等。一般用于分析可执行程序和库文件。strings命令较为常用的功能有以下两种 查看系统的GLIBC版本和目标文件的依赖的GLIBC版本,当系统的GLIBC版本与目标文…...

基于 MQTT 的开源桥接器:自由控制物联网设备 | 开源日报 No.151
Koenkk/zigbee2mqtt Stars: 10.5k License: GPL-3.0 Zigbee2MQTT 是一个 Zigbee 🐝 到 MQTT 桥接器 🌉,可以摆脱专有的 Zigbee 桥接器 🔨 允许您在不使用供应商桥接器或网关的情况下使用 Zigbee 设备通过 MQTT 桥接事件并控制 Z…...

【QT+QGIS跨平台编译】之七:【libjpeg+Qt跨平台编译】(一套代码、一套框架,跨平台编译)
文章目录 一、libjpeg介绍二、文件下载三、文件分析四、pro文件五、编译实践一、libjpeg介绍 libjpeg是一个广泛使用的jpeg图像压缩和解压的函数库,采用 C 语言开发。 2013年1月,Independent JPEG Group发布了版本9,对新引入的无损编码模式进行了改进。2022年1月,发布了版…...

VI / VIM的使用
vi/vim 的区别简单点来说,它们都是多模式编辑器,不同的是 vim 是 vi 的升级版本,它不仅兼容 vi 的所有指令,而且 还有一些新的特性在里面。例如语法加亮,可视化操作不仅可以在终端运行,也可以运行于 x win…...

第十二站(20天):C++泛型编程
模板 C提供了模板(template)编程的概念。所谓模板,实际上是建立一个通用函数或类, 其 类内部的类型和函数的形参类型不具体指定 ,用一个虚拟的类型来代表。这种通用的方式称 为模板。 模板是泛型编程的基础, 泛型编程即以一种独立于任何特定…...

【Docker】Dokcer学习① - 简介
【Docker】Docker学习① - 简介 一、Docker简介1. Docker是什么2. Docker组成3. Docker对比虚拟机4. Linux Namespace技术5. Linux control groups6. 容器管理工具 二、Docker安装及基础命令介绍三、Docker镜像管理四、Docker镜像与制作五、Docker数据管理六、网络部分七、Dock…...

PostgreSQL 100条命令
我会为您提供一些 PostgreSQL 中最常用的命令: 1. 创建数据库:CREATE DATABASE database_name; 2. 连接到数据库:\c database_name; 3. 创建表格:CREATE TABLE table_name (column1 datatype, column2 datatype, ...); 4. 插入数…...

HTTP动态代理的原理及其对网络性能的影响
HTTP动态代理是一种通过代理服务器来转发HTTP请求和响应数据的网络技术,它可以优化网络性能、提高网络安全性,并解决跨域请求的问题。本文将详细介绍HTTP动态代理的原理及其对网络性能的影响。 一、HTTP动态代理的原理 HTTP动态代理的基本原理是在客户…...

69.使用Go标准库compress/gzip压缩数据存入Redis避免BigKey
文章目录 一:简介二:Go标准库compress/gzip包介绍ConstantsVariablestype Headertype Reader 三:代码实践1、压缩与解压工具包2、单元测试3、为何压缩后还要用base64编码 代码地址: https://gitee.com/lymgoforIT/golang-trick/t…...