本文分析 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中的流程
/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主要进行了三件事情:
- 创建session
- 对session进行写操作
- 最后提交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网络通信的架构及命令模式,写的非常巧妙。
用一张图来为这一部分做个总结:
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将先后调用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步介绍其主要内容。
- getMinimalPackageInfo,利用PackageParser解析出APK对应Package的基本信息,然后利用resolveInstallLocation得到适合APK安装路径。
- installLocationPolicy,当成功得到APK对应的PackageInfoLite,并判断安装路径有足够的剩余空间。
- createInstallArgs,处理完潜在的重复安装APK的风险后,PKMS调用createInstallArgs生成安装参数对象,这部分代码较为简单,就是利用参数决定创建哪个InstallArgs的子类,我们主要关注在终端安装APK时,将要使用的FileInstallArgs,后文介绍其功能。
- copyApk,如果不需要进行安装包检查,对于安装在终端内部的APK而言,将调用FileInstallArgs的copyAPK函数。对于非adb安装的APK,将调用DefaultContainerService对应的copyPackage进行处理。
至此整个handleStartCopy流程介绍完毕,可以看出当利用adb安装,handleStartCopy实际上并没有完成什么实际的操作;对于其它方式APK时,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件事情
- InstallArgs#doPreInstall
- PKMS#installPackageTracedLI 进行Apk安装
- InstallArgs#doPostInstall函数
- 利用构造PostInstallData,然后发送POST_INSTALL消息触发后续流程
总结 adb install
从上面的代码来看,整个APK安装过程极其繁琐复杂,但核心思想还是比较简单的:就是将待安装的APK文件拷贝到手机的指定位置,然后利用PackageParser来解析出对应的Package对象,最终将Package对象加入到PKMS中。
整个流程的主干大体如下图所示:
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安装