Android PackageManagerService分析二:安装APK

时间:2022-09-17 00:01:21

转自:http://blog.csdn.net/lilian0118/article/details/25792601


这一章我们开始分析APK的安装过程,当我们从网上download一个APK后,点击这个APK文件,就会启动PackageInstallerActivity这个页面来parse这个APK文件,并提示一些信息给用户,当用户点击安装以后,就会开始APK的安装。在介绍安装APK之前,我们先来分析一下installd。

installd介绍

首先从installd的启动开始介绍,installd是在init.rc脚本中启动的: [plain] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. service installd /system/bin/installd  
  2.     class main  
  3.     socket installd stream 600 system system  

上面的脚本文件会创建一个/dev/socket/installd的unix domain socket,并把socket文件描述符返回给installd。我们简单的来看看installd的main函数: [cpp] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. int main(const int argc, const char *argv[]) {  
  2.     char buf[BUFFER_MAX];  
  3.     struct sockaddr addr;  
  4.     socklen_t alen;  
  5.     int lsocket, s, count;  
  6.   
  7.     ALOGI("installd firing up\n");  
  8.   
  9.     if (initialize_globals() < 0) {  
  10.         ALOGE("Could not initialize globals; exiting.\n");  
  11.         exit(1);  
  12.     }  
  13.   
  14.     if (initialize_directories() < 0) {  
  15.         ALOGE("Could not create directories; exiting.\n");  
  16.         exit(1);  
  17.     }  
  18.   
  19.     drop_privileges();  
  20.   
  21.     lsocket = android_get_control_socket(SOCKET_PATH);  
  22.     if (lsocket < 0) {  
  23.         ALOGE("Failed to get socket from environment: %s\n", strerror(errno));  
  24.         exit(1);  
  25.     }  
  26.     if (listen(lsocket, 5)) {  
  27.         ALOGE("Listen on socket failed: %s\n", strerror(errno));  
  28.         exit(1);  
  29.     }  
  30.     fcntl(lsocket, F_SETFD, FD_CLOEXEC);  
  31.   
  32.     for (;;) {  
  33.         alen = sizeof(addr);  
  34.         s = accept(lsocket, &addr, &alen);  

main函数中首先做一些全局化的初始化设置,然后从底层获取创建的socket号,并在这个socket上面开始listen。在这里installd就是一个server,等待client的连接。那我们再来看一下client的创建过程,也就是Installer对象的实例化,实例化Installer也是在systemServer当中:
[java] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. installer = new Installer();  
  2. installer.ping();  
来看一下 Installer的ping方法,它是用于建立也installd之间的socket连接,并测试是否可以正常的发送命令给installd:
[java] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. public boolean ping() {  
  2.     if (execute("ping") < 0) {  
  3.         return false;  
  4.     } else {  
  5.         return true;  
  6.     }  
  7. }  
  8.   
  9. private int execute(String cmd) {  
  10.     String res = transaction(cmd);  
  11.     try {  
  12.         return Integer.parseInt(res);  
  13.     } catch (NumberFormatException ex) {  
  14.         return -1;  
  15.     }  
  16. }  
  17.   
  18. private synchronized String transaction(String cmd) {  
  19.     if (!connect()) {  
  20.         return "-1";  
  21.     }  
  22.   
  23.     if (!writeCommand(cmd)) {  
  24.         if (!connect() || !writeCommand(cmd)) {  
  25.             return "-1";  
  26.         }  
  27.     }  
  28.     if (readReply()) {  
  29.         String s = new String(buf, 0, buflen);  
  30.         return s;  
  31.     } else {  
  32.         return "-1";  
  33.     }  
  34. }  

connect函数比较简单,就是创建一个socket,并且与前面的installd进行连接,如果成功连接上,就返回true。那看一下writeCommand的实现:
[java] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. private boolean writeCommand(String _cmd) {  
  2.     byte[] cmd = _cmd.getBytes();  
  3.     int len = cmd.length;  
  4.     if ((len < 1) || (len > 1024))  
  5.         return false;  
  6.     buf[0] = (byte) (len & 0xff);  
  7.     buf[1] = (byte) ((len >> 8) & 0xff);  
  8.     try {  
  9.         mOut.write(buf, 02);  
  10.         mOut.write(cmd, 0, len);  
  11.     } catch (IOException ex) {  
  12.         Slog.e(TAG, "write error");  
  13.         disconnect();  
  14.         return false;  
  15.     }  
  16.     return true;  
  17. }  

发送到installd的数据格式:cmd长度+cmd本身。再到installd中看如何处理ping的命令:
[cpp] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. for (;;) {  
  2.     unsigned short count;  
  3.     if (readx(s, &count, sizeof(count))) {  
  4.         ALOGE("failed to read size\n");  
  5.         break;  
  6.     }  
  7.     if ((count < 1) || (count >= BUFFER_MAX)) {  
  8.         ALOGE("invalid size %d\n", count);  
  9.         break;  
  10.     }  
  11.     if (readx(s, buf, count)) {  
  12.         ALOGE("failed to read command\n");  
  13.         break;  
  14.     }  
  15.     buf[count] = 0;  
  16.     if (execute(s, buf)) break;  
  17. }  
首先读出两字节的长度保存在count中,再从socket读出长度为count的命令保存在buf中,最后调用execute去执行命令:
[cpp] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. static int execute(int s, char cmd[BUFFER_MAX])  
  2. {  
  3.     char reply[REPLY_MAX];  
  4.     char *arg[TOKEN_MAX+1];  
  5.     unsigned i;  
  6.     unsigned n = 0;  
  7.     unsigned short count;  
  8.     int ret = -1;  
  9.   
  10.     reply[0] = 0;  
  11.   
  12.     arg[0] = cmd;  
  13.     while (*cmd) {  
  14.         if (isspace(*cmd)) {  
  15.             *cmd++ = 0;  
  16.             n++;  
  17.             arg[n] = cmd;  
  18.             if (n == TOKEN_MAX) {  
  19.                 ALOGE("too many arguments\n");  
  20.                 goto done;  
  21.             }  
  22.         }  
  23.         cmd++;  
  24.     }  
  25.   
  26.     for (i = 0; i < sizeof(cmds) / sizeof(cmds[0]); i++) {  
  27.         if (!strcmp(cmds[i].name,arg[0])) {  
  28.             if (n != cmds[i].numargs) {  
  29.                 ALOGE("%s requires %d arguments (%d given)\n",  
  30.                      cmds[i].name, cmds[i].numargs, n);  
  31.             } else {  
  32.                 ret = cmds[i].func(arg + 1, reply);  
  33.             }  
  34.             goto done;  
  35.         }  
  36.     }  
  37.     ALOGE("unsupported command '%s'\n", arg[0]);  
  38.   
  39. done:  
  40.     if (reply[0]) {  
  41.         n = snprintf(cmd, BUFFER_MAX, "%d %s", ret, reply);  
  42.     } else {  
  43.         n = snprintf(cmd, BUFFER_MAX, "%d", ret);  
  44.     }  
  45.     if (n > BUFFER_MAX) n = BUFFER_MAX;  
  46.     count = n;  
  47.   
  48.     // ALOGI("reply: '%s'\n", cmd);  
  49.     if (writex(s, &count, sizeof(count))) return -1;  
  50.     if (writex(s, cmd, count)) return -1;  
  51.     return 0;  
  52. }  

execute函数也比较简单,首先从命令行中parse出命令和参数,然后从全局的cmds数组中获取到与要执行的命令匹配的函数并执行,最后将返回值和结果发送给client端。这里的cmds数组就表示了所有installd可以执行的命令:
[cpp] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. struct cmdinfo cmds[] = {  
  2.     { "ping",                 0, do_ping },  
  3.     { "install",              4, do_install },  
  4.     { "dexopt",               3, do_dexopt },  
  5.     { "movedex",              2, do_move_dex },  
  6.     { "rmdex",                1, do_rm_dex },  
  7.     { "remove",               2, do_remove },  
  8.     { "rename",               2, do_rename },  
  9.     { "fixuid",               3, do_fixuid },  
  10.     { "freecache",            1, do_free_cache },  
  11.     { "rmcache",              2, do_rm_cache },  
  12.     { "getsize",              6, do_get_size },  
  13.     { "rmuserdata",           2, do_rm_user_data },  
  14.     { "movefiles",            0, do_movefiles },  
  15.     { "linklib",              3, do_linklib },  
  16.     { "mkuserdata",           3, do_mk_user_data },  
  17.     { "rmuser",               1, do_rm_user },  
  18. };  


Package Installer过程分析

当我们点击一个APK文件后,就会发送一个Intent给PackageInstallerActivity,PackageInstallerActivity首先会去检查系统是否支持安装未知来源的APK。然后会去解析这个APK的一些信息显示出来给用户,例如是否需要读取通讯录、是否要访问网络等等。当用户点击这个activity的安装按钮后,就会开始真正的执行安装,它对应的函数如下:[java] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags,  
  2.         installerPackageName, verificationParams, null);  
这个方法的第一个参数是APK所在的路径URI;第二个参数用来监控整个安装过程;第三个参数installFlags默认为0;第四个参数是APK的包名;第五个参数用于做数字签名验证。当然这里调用到PMS的真正实现的函数需要借助binder的跨进程来实现:
[java] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. public void installPackageWithVerificationAndEncryption(Uri packageURI,  
  2.         IPackageInstallObserver observer, int flags, String installerPackageName,  
  3.         VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {  
  4.     mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,  
  5.             null);  
  6.   
  7.     final int uid = Binder.getCallingUid();  
  8.   
  9.     UserHandle user;  
  10.     if ((flags&PackageManager.INSTALL_ALL_USERS) != 0) {  
  11.         user = UserHandle.ALL;  
  12.     } else {  
  13.         user = new UserHandle(UserHandle.getUserId(uid));  
  14.     }  
  15.   
  16.     final int filteredFlags;  
  17.   
  18.     if (uid == Process.SHELL_UID || uid == 0) {  
  19.         if (DEBUG_INSTALL) {  
  20.             Slog.v(TAG, "Install from ADB");  
  21.         }  
  22.         filteredFlags = flags | PackageManager.INSTALL_FROM_ADB;  
  23.     } else {  
  24.         filteredFlags = flags & ~PackageManager.INSTALL_FROM_ADB;  
  25.     }  
  26.   
  27.     verificationParams.setInstallerUid(uid);  
  28.   
  29.     final Message msg = mHandler.obtainMessage(INIT_COPY);  
  30.     msg.obj = new InstallParams(packageURI, observer, filteredFlags, installerPackageName,  
  31.             verificationParams, encryptionParams, user);  
  32.     mHandler.sendMessage(msg);  
  33. }  
这里首先做一些权限的检查,并判断当前安装APK的user是否具有相应的权限。在安装APK的时候分为程序开发人员通过ADB安装和user通过网上下载安装,当通过ADB安装时,往往不需要对程序做验证,这就是INSTALL_FROM_ADB这个flag的作用。最后构造一个INIT_COPY的cmd,并带有InstallParams的message发给PackageHandler处理。在分析PackageHandler处理INIT_COPY之前,先来看一下几种安装Params的关系:

Android PackageManagerService分析二:安装APK

再来看PackageHandler处理INIT_COPY这个命令:
[java] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. case INIT_COPY: {  
  2.     HandlerParams params = (HandlerParams) msg.obj;  
  3.     int idx = mPendingInstalls.size();  
  4.     if (!mBound) {  
  5.         if (!connectToService()) {  
  6.             Slog.e(TAG, "Failed to bind to media container service");  
  7.             params.serviceError();  
  8.             return;  
  9.         } else {  
  10.             mPendingInstalls.add(idx, params);  
  11.         }  
  12.     } else {  
  13.         mPendingInstalls.add(idx, params);  
  14.         if (idx == 0) {  
  15.             mHandler.sendEmptyMessage(MCS_BOUND);  
  16.         }  
  17.     }  
  18.     break;  
  19. }  

在安装APK的时候,PMS还得依赖另一个service,也就是ContainerService,它具体负责实现APK等相关资源文件在内部或外部存储器上的存储工作。如果之前我们没有绑定ContainerService,这里首先会绑定服务,并把我们前面创建的InstallParams加入到mPendingInstalls列表中,mPendingInstalls保存所有的安装请求。如果我们前面绑定过ContainerService,就再发送MCS_BOUND消息给自身。当然,在我们在绑定服务之后,同样也会发送MCS_BOUND消息。我们来看MCS_BOUND的处理流程:
[java] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. case MCS_BOUND: {  
  2.     if (msg.obj != null) {  
  3.         mContainerService = (IMediaContainerService) msg.obj;  
  4.     }  
  5.     if (mContainerService == null) {  
  6.   
  7.     } else if (mPendingInstalls.size() > 0) {  
  8.         HandlerParams params = mPendingInstalls.get(0);  
  9.         if (params != null) {  
  10.             if (params.startCopy()) {  
  11.                 if (mPendingInstalls.size() > 0) {  
  12.                     mPendingInstalls.remove(0);  
  13.                 }  
  14.                 if (mPendingInstalls.size() == 0) {  
  15.                     if (mBound) {  
  16.                         removeMessages(MCS_UNBIND);  
  17.                         Message ubmsg = obtainMessage(MCS_UNBIND);  
  18.                         sendMessageDelayed(ubmsg, 10000);  
  19.                     }  
  20.                 } else {  
  21.                     mHandler.sendEmptyMessage(MCS_BOUND);  
  22.                 }  
  23.             }  
  24.         }  
  25.     } else {  
  26.   
  27.     }  
  28.     break;  
  29. }  

在处理MCS_BOUND消息时,首先把前面得到的ContainerService服务赋给mContainerService。然后不断的处理mPendingInstalls所有的安装请求,这里调用InstallParams的startCopy方法:
[java] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. final boolean startCopy() {  
  2.     boolean res;  
  3.     try {  
  4.   
  5.         if (++mRetries > MAX_RETRIES) {  
  6.             Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");  
  7.             mHandler.sendEmptyMessage(MCS_GIVE_UP);  
  8.             handleServiceError();  
  9.             return false;  
  10.         } else {  
  11.             handleStartCopy();  
  12.             res = true;  
  13.         }  
  14.     } catch (RemoteException e) {  
  15.         if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");  
  16.         mHandler.sendEmptyMessage(MCS_RECONNECT);  
  17.         res = false;  
  18.     }  
  19.     handleReturnCode();  
  20.     return res;  
  21. }  

这里主要调用InstallParams的handleStartCopy方法来完成资源文件的拷贝:
[java] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. public void handleStartCopy() throws RemoteException {  
  2.     int ret = PackageManager.INSTALL_SUCCEEDED;  
  3.     final boolean onSd = (flags & PackageManager.INSTALL_EXTERNAL) != 0;  
  4.     final boolean onInt = (flags & PackageManager.INSTALL_INTERNAL) != 0;  
  5.     PackageInfoLite pkgLite = null;  
  6.   
  7.     if (onInt && onSd) {  
  8.   
  9.     } else {  
  10.         final long lowThreshold;  
  11.   
  12.         final DeviceStorageMonitorService dsm = (DeviceStorageMonitorService) ServiceManager  
  13.                 .getService(DeviceStorageMonitorService.SERVICE);  
  14.         if (dsm == null) {  
  15.             Log.w(TAG, "Couldn't get low memory threshold; no free limit imposed");  
  16.             lowThreshold = 0L;  
  17.         } else {  
  18.             lowThreshold = dsm.getMemoryLowThreshold();  
  19.         }  
  20.   
  21.         try {  
  22.             mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, mPackageURI,  
  23.                     Intent.FLAG_GRANT_READ_URI_PERMISSION);  
  24.   
  25.             final File packageFile;  
  26.             if (encryptionParams != null || !"file".equals(mPackageURI.getScheme())) {  
  27.                  //关于drm文件的安装  
  28.             } else {  
  29.                 packageFile = new File(mPackageURI.getPath());  
  30.             }  
  31.   
  32.             if (packageFile != null) {  
  33.                 final String packageFilePath = packageFile.getAbsolutePath();  
  34.                 pkgLite = mContainerService.getMinimalPackageInfo(packageFilePath, flags,  
  35.                         lowThreshold);  
  36.   
  37.                 if (pkgLite.recommendedInstallLocation  
  38.                         == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {  
  39.                     final long size = mContainerService.calculateInstalledSize(  
  40.                             packageFilePath, isForwardLocked());  
  41.                     if (mInstaller.freeCache(size + lowThreshold) >= 0) {  
  42.                         pkgLite = mContainerService.getMinimalPackageInfo(packageFilePath,  
  43.                                 flags, lowThreshold);  
  44.                     }  
  45.   
  46.                     if (pkgLite.recommendedInstallLocation  
  47.                             == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {  
  48.                         pkgLite.recommendedInstallLocation  
  49.                             = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;  
  50.                     }  
  51.                 }  
  52.             }  
  53.         } finally {  
  54.             mContext.revokeUriPermission(mPackageURI,  
  55.                     Intent.FLAG_GRANT_READ_URI_PERMISSION);  
  56.         }  
  57.     }  
  58.   
  59.     if (ret == PackageManager.INSTALL_SUCCEEDED) {  
  60.         int loc = pkgLite.recommendedInstallLocation;  
  61.         if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {  
  62.             ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;  
  63.         } else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) {  
  64.             ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;  
  65.         } else if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {  
  66.             ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;  
  67.         } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) {  
  68.             ret = PackageManager.INSTALL_FAILED_INVALID_APK;  
  69.         } else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {  
  70.             ret = PackageManager.INSTALL_FAILED_INVALID_URI;  
  71.         } else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) {  
  72.             ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;  
  73.         } else {  
  74.             loc = installLocationPolicy(pkgLite, flags);  
  75.             if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {  
  76.                 ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;  
  77.             } else if (!onSd && !onInt) {  
  78.                 if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {  
  79.                     // Set the flag to install on external media.  
  80.                     flags |= PackageManager.INSTALL_EXTERNAL;  
  81.                     flags &= ~PackageManager.INSTALL_INTERNAL;  
  82.                 } else {  
  83.                     flags |= PackageManager.INSTALL_INTERNAL;  
  84.                     flags &= ~PackageManager.INSTALL_EXTERNAL;  
  85.                 }  
  86.             }  
  87.         }  
  88.     }  

handleStartCopy这个函数比较长,我们先来看前一部分,这里首先从installFlags中检查是否有指定安装位置,然后从DeviceStorageMonitorService中取得系统设置的内存空间的最低阀值。然后调用ContainerService去计算APK文件的大小,并检查当前内存空间是否足够,如果不足够,则调用Installd的freeCache方法尝试去释放一些cache文件。最终再来检查是否有足够的空间,并决定安装位置。接着来看handleStartCopy的实现:
[java] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1.     final InstallArgs args = createInstallArgs(this);  
  2.     mArgs = args;  
  3.   
  4.     if (ret == PackageManager.INSTALL_SUCCEEDED) {  
  5.         int userIdentifier = getUser().getIdentifier();  
  6.         if (userIdentifier == UserHandle.USER_ALL  
  7.                 && ((flags & PackageManager.INSTALL_FROM_ADB) != 0)) {  
  8.             userIdentifier = UserHandle.USER_OWNER;  
  9.         }  
  10.   
  11.         final int requiredUid = mRequiredVerifierPackage == null ? -1  
  12.                 : getPackageUid(mRequiredVerifierPackage, userIdentifier);  
  13.         if (requiredUid != -1 && isVerificationEnabled(flags)) {  
  14.                   
  15.         } else {  
  16.             ret = args.copyApk(mContainerService, true);  
  17.         }  
  18.     }  
  19.   
  20.     mRet = ret;  
  21. }  

这里首先根据安装路径的不同,创建不同的InstallArgs:
[java] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. private InstallArgs createInstallArgs(InstallParams params) {  
  2.     if (installOnSd(params.flags) || params.isForwardLocked()) {  
  3.         return new AsecInstallArgs(params);  
  4.     } else {  
  5.         return new FileInstallArgs(params);  
  6.     }  
  7. }  

AsecInstallArgs就是指安装在外部存储空间上;FileInstallArgs是指安装在内部存储空间。来看一下上面的两者的类图关系:

Android PackageManagerService分析二:安装APK


接下来以分安装在内部存储空间和外部存储空间两种情况来分析:

安装在内部存储空间

首先来看FileInstallArgs的copyApk方法: [java] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {  
  2.     if (temp) {  
  3.         createCopyFile();  
  4.     }  
  5.     File codeFile = new File(codeFileName);  
  6.     if (!created) {  
  7.   
  8.     }  
  9.     ParcelFileDescriptor out = null;  
  10.     try {  
  11.         out = ParcelFileDescriptor.open(codeFile, ParcelFileDescriptor.MODE_READ_WRITE);  
  12.     } catch (FileNotFoundException e) {  
  13.         Slog.e(TAG, "Failed to create file descriptor for : " + codeFileName);  
  14.         return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;  
  15.     }  
  16.     int ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;  
  17.     try {  
  18.         mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,  
  19.                 Intent.FLAG_GRANT_READ_URI_PERMISSION);  
  20.         ret = imcs.copyResource(packageURI, null, out);  
  21.     } finally {  
  22.         IoUtils.closeQuietly(out);  
  23.         mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);  
  24.     }  
  25.   
  26.     if (isFwdLocked()) {  
  27.   
  28.     }  
  29.   
  30.     final File nativeLibraryFile = new File(getNativeLibraryPath());  
  31.     if (nativeLibraryFile.exists()) {  
  32.         NativeLibraryHelper.removeNativeBinariesFromDirLI(nativeLibraryFile);  
  33.         nativeLibraryFile.delete();  
  34.     }  
  35.     try {  
  36.         int copyRet = copyNativeLibrariesForInternalApp(codeFile, nativeLibraryFile);  
  37.         if (copyRet != PackageManager.INSTALL_SUCCEEDED) {  
  38.             return copyRet;  
  39.         }  
  40.     } catch (IOException e) {  
  41.         ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;  
  42.     }  
  43.   
  44.     return ret;  
  45. }  

因为前面传进来的第二个参数是true,所以这里这里首先调用createCopyFile创建一个临时文件:
[java] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. void createCopyFile() {  
  2.     installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir;  
  3.     codeFileName = createTempPackageFile(installDir).getPath();  
  4.     resourceFileName = getResourcePathFromCodePath();  
  5.     libraryPath = getLibraryPathFromCodePath();  
  6.     created = true;  
  7. }  
installDir就是/data/app,createTempPackageFile在/data/app目录下面创建一个以"vmdl"开始,并以".tmp"结尾的临时文件。resourceFileName就是codeFileName所在的路径名。libraryPath在/data/app-lib路径下面以"vmdlxxxx"命名的文件。回到copyApk函数,接着调用ContainerService的copyResource方法将原始packageURI所指向的文件内容拷贝到我们创建的临时文件里面。最后将APK文件里面的lib文件全部拷贝到libraryPath所指定的文件目录下面。当数据拷贝完成,接着到InstallParams的startCopy方法中会调用handleReturnCode来处理拷贝的结果:
[java] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. void handleReturnCode() {  
  2.     if (mArgs != null) {  
  3.         processPendingInstall(mArgs, mRet);  
  4.   
  5.     }  
  6. }  
  7.   
  8. ate void processPendingInstall(final InstallArgs args, final int currentStatus) {  
  9. mHandler.post(new Runnable() {  
  10.     public void run() {  
  11.         mHandler.removeCallbacks(this);  
  12.   
  13.         PackageInstalledInfo res = new PackageInstalledInfo();  
  14.         res.returnCode = currentStatus;  
  15.         res.uid = -1;  
  16.         res.pkg = null;  
  17.         res.removedInfo = new PackageRemovedInfo();  
  18.         if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {  
  19.             args.doPreInstall(res.returnCode);  
  20.             synchronized (mInstallLock) {  
  21.                 installPackageLI(args, true, res);  
  22.             }  
  23.             args.doPostInstall(res.returnCode, res.uid);  
  24.         }  

在processPendingInstall里面,首先调用FileInstallArgs的doPreInstall方法,默认的它什么不会做。然后执行真正的安装过程installPackageLI:
[java] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. private void installPackageLI(InstallArgs args,  
  2.         boolean newInstall, PackageInstalledInfo res) {  
  3.     int pFlags = args.flags;  
  4.     String installerPackageName = args.installerPackageName;  
  5.     File tmpPackageFile = new File(args.getCodePath());  
  6.     boolean forwardLocked = ((pFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);  
  7.     boolean onSd = ((pFlags & PackageManager.INSTALL_EXTERNAL) != 0);  
  8.     boolean replace = false;  
  9.     int scanMode = (onSd ? 0 : SCAN_MONITOR) | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE  
  10.             | (newInstall ? SCAN_NEW_INSTALL : 0);  
  11.   
  12.     res.returnCode = PackageManager.INSTALL_SUCCEEDED;  
  13.   
  14.     int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY  
  15.             | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)  
  16.             | (onSd ? PackageParser.PARSE_ON_SDCARD : 0);  
  17.     PackageParser pp = new PackageParser(tmpPackageFile.getPath());  
  18.     pp.setSeparateProcesses(mSeparateProcesses);  
  19.     final PackageParser.Package pkg = pp.parsePackage(tmpPackageFile,  
  20.             null, mMetrics, parseFlags);  
  21.     if (pkg == null) {  
  22.         res.returnCode = pp.getParseError();  
  23.         return;  
  24.     }  
  25.     String pkgName = res.name = pkg.packageName;  
  26.     if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) {  
  27.         if ((pFlags&PackageManager.INSTALL_ALLOW_TEST) == 0) {  
  28.             res.returnCode = PackageManager.INSTALL_FAILED_TEST_ONLY;  
  29.             return;  
  30.         }  
  31.     }  
  32.     if (GET_CERTIFICATES && !pp.collectCertificates(pkg, parseFlags)) {  
  33.         res.returnCode = pp.getParseError();  
  34.         return;  
  35.     }  

这里首先调用PackageParser的parsePackage方法解析AndroidManifest文件,构造一个Package对象,前面我们讲过Package对象了,大致内容如下:
Android PackageManagerService分析二:安装APK
然后手机APK文件中的数字签名信息。接着来看installPackageLI方法: [java] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1.     pp = null;  
  2.     String oldCodePath = null;  
  3.     boolean systemApp = false;  
  4.     synchronized (mPackages) {  
  5.   
  6.         if ((pFlags&PackageManager.INSTALL_REPLACE_EXISTING) != 0) {  
  7.   
  8.         }  
  9.         PackageSetting ps = mSettings.mPackages.get(pkgName);  
  10.         if (ps != null) {  
  11.   
  12.     }  
  13.   
  14.     if (!args.doRename(res.returnCode, pkgName, oldCodePath)) {  
  15.         res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;  
  16.         return;  
  17.     }  
  18.       
  19.     setApplicationInfoPaths(pkg, args.getCodePath(), args.getResourcePath());  
  20.     pkg.applicationInfo.nativeLibraryDir = args.getNativeLibraryPath();  
  21.     if (replace) {  
  22.   
  23.     } else {  
  24.         installNewPackageLI(pkg, parseFlags, scanMode, args.user,  
  25.                 installerPackageName, res);  
  26.     }  
  27.     synchronized (mPackages) {  
  28.         final PackageSetting ps = mSettings.mPackages.get(pkgName);  
  29.         if (ps != null) {  
  30.             res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);  
  31.         }  
  32.     }  
  33. }  

这里先不关注APK的更新,直接来看FileInstallArgs的doRename方法:
[java] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. boolean doRename(int status, final String pkgName, String oldCodePath) {  
  2.     if (status != PackageManager.INSTALL_SUCCEEDED) {  
  3.   
  4.     } else {  
  5.         final File oldCodeFile = new File(getCodePath());  
  6.         final File oldResourceFile = new File(getResourcePath());  
  7.         final File oldLibraryFile = new File(getNativeLibraryPath());  
  8.   
  9.         final String apkName = getNextCodePath(oldCodePath, pkgName, ".apk");  
  10.         final File newCodeFile = new File(installDir, apkName + ".apk");  
  11.         if (!oldCodeFile.renameTo(newCodeFile)) {  
  12.             return false;  
  13.         }  
  14.         codeFileName = newCodeFile.getPath();  
  15.   
  16.         final File newResFile = new File(getResourcePathFromCodePath());  
  17.         if (isFwdLocked() && !oldResourceFile.renameTo(newResFile)) {  
  18.             return false;  
  19.         }  
  20.         resourceFileName = newResFile.getPath();  
  21.   
  22.         final File newLibraryFile = new File(getLibraryPathFromCodePath());  
  23.         if (newLibraryFile.exists()) {  
  24.             NativeLibraryHelper.removeNativeBinariesFromDirLI(newLibraryFile);  
  25.             newLibraryFile.delete();  
  26.         }  
  27.         if (!oldLibraryFile.renameTo(newLibraryFile)) {  
  28.             Slog.e(TAG, "Cannot rename native library directory "  
  29.                     + oldLibraryFile.getPath() + " to " + newLibraryFile.getPath());  
  30.             return false;  
  31.         }  
  32.         libraryPath = newLibraryFile.getPath();  
  33.   
  34.         if (!setPermissions()) {  
  35.             return false;  
  36.         }  
  37.   
  38.         return true;  
  39.     }  
  40. }  

getNextCodePath返回一个类似"包名-num.apk"的文件名, 例如以新浪微博为例,apkName为"com.sina.weibo-1.apk"。然后将原来的codeFileName改为新的名字。resourceFileName和libraryPath也会做类似的改变。回到installPackageLI函数中,接着调用installNewPackageLI方法将新的package文件里面的资源加入到PMS的数据结构中,让PMS来管理这些activity、service、receiver:
[java] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. private void installNewPackageLI(PackageParser.Package pkg,  
  2.         int parseFlags, int scanMode, UserHandle user,  
  3.         String installerPackageName, PackageInstalledInfo res) {  
  4.   
  5.     String pkgName = pkg.packageName;  
  6.   
  7.     boolean dataDirExists = getDataPathForPackage(pkg.packageName, 0).exists();  
  8.     synchronized(mPackages) {  
  9.         if (mSettings.mRenamedPackages.containsKey(pkgName)) {  
  10.             res.returnCode = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;  
  11.             return;  
  12.         }  
  13.         if (mPackages.containsKey(pkgName) || mAppDirs.containsKey(pkg.mPath)) {  
  14.             res.returnCode = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;  
  15.             return;  
  16.         }  
  17.     }  
  18.     mLastScanError = PackageManager.INSTALL_SUCCEEDED;  
  19.     PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode,  
  20.             System.currentTimeMillis(), user);  
  21.     if (newPackage == null) {  
  22.   
  23.     } else {  
  24.         updateSettingsLI(newPackage,  
  25.                 installerPackageName,  
  26.                 nullnull,  
  27.                 res);  
  28.     }  
  29. }  

在installNewPackageLI方法中首先调用scanPackageLI方法把新package的资源归入到PMS中,并创建一个PackageSettings对象,加入到Settings中的mPackages这个map中。这个函数我们在前面一章中启动PMS中大致介绍过了,这里主要来看一下与安装有关的部分:
[java] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. private PackageParser.Package scanPackageLI(PackageParser.Package pkg,  
  2.         int parseFlags, int scanMode, long currentTime, UserHandle user) {  
  3.                 File dataPath;  
  4.     if (mPlatformPackage == pkg) {  
  5.   
  6.     } else {  
  7.         dataPath = getDataPathForPackage(pkg.packageName, 0);  
  8.   
  9.         boolean uidError = false;  
  10.   
  11.         if (dataPath.exists()) {  
  12.    
  13.         } else {  
  14.   
  15.             int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid,  
  16.                                        pkg.applicationInfo.seinfo);  
  17.   
  18.             if (dataPath.exists()) {  
  19.                 pkg.applicationInfo.dataDir = dataPath.getPath();  
  20.             } else {  
  21.                 Slog.w(TAG, "Unable to create data directory: " + dataPath);  
  22.                 pkg.applicationInfo.dataDir = null;  
  23.             }  
  24.         }  
  25.   
  26.     String path = scanFile.getPath();  
  27.   
  28.     if (pkg.applicationInfo.nativeLibraryDir != null) {  
  29.         try {  
  30.             File nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);  
  31.             final String dataPathString = dataPath.getCanonicalPath();  
  32.   
  33.             if (isSystemApp(pkg) && !isUpdatedSystemApp(pkg)) {  
  34.   
  35.             } else {  
  36.                 if (!isForwardLocked(pkg) && !isExternal(pkg)) {  
  37.                     if (nativeLibraryDir.getPath().startsWith(dataPathString)) {  
  38.                         setInternalAppNativeLibraryPath(pkg, pkgSetting);  
  39.                         nativeLibraryDir = new File(pkg.applicationInfo.nativeLibraryDir);  
  40.                     }  
  41.   
  42.                     try {  
  43.                         if (copyNativeLibrariesForInternalApp(scanFile, nativeLibraryDir) != PackageManager.INSTALL_SUCCEEDED) {  
  44.                             Slog.e(TAG, "Unable to copy native libraries");  
  45.                             mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;  
  46.                             return null;  
  47.                         }  
  48.                     } catch (IOException e) {  
  49.                         Slog.e(TAG, "Unable to copy native libraries", e);  
  50.                         mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;  
  51.                         return null;  
  52.                     }  
  53.                 }  
  54.   
  55.                 if (DEBUG_INSTALL) Slog.i(TAG, "Linking native library dir for " + path);  
  56.                 final int[] userIds = sUserManager.getUserIds();  
  57.                 synchronized (mInstallLock) {  
  58.                     for (int userId : userIds) {  
  59.                         if (mInstaller.linkNativeLibraryDirectory(pkg.packageName,  
  60.                                 pkg.applicationInfo.nativeLibraryDir, userId) < 0) {  
  61.                             Slog.w(TAG, "Failed linking native library dir (user=" + userId  
  62.                                     + ")");  
  63.                             mLastScanError = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;  
  64.                             return null;  
  65.                         }  
  66.                     }  
  67.                 }  
  68.             }  
  69.         } catch (IOException ioe) {  
  70.             Slog.e(TAG, "Unable to get canonical file " + ioe.toString());  
  71.         }  
  72.     }  
  73.     pkg.mScanPath = path;  
  74.   
  75.     if ((scanMode&SCAN_NO_DEX) == 0) {  
  76.         if (performDexOptLI(pkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0false)  
  77.                 == DEX_OPT_FAILED) {  
  78.             mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT;  
  79.             return null;  
  80.         }  
  81.     }  

首先取得Package的dataPath,路径为/data/data/包名,当我们第一次安装时,这个目录还没有创建,所以调用createDataDirsLI去创建data目录:
[java] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. private int createDataDirsLI(String packageName, int uid, String seinfo) {  
  2.     int[] users = sUserManager.getUserIds();  
  3.     int res = mInstaller.install(packageName, uid, uid, seinfo);  
  4.     if (res < 0) {  
  5.         return res;  
  6.     }  
  7.     for (int user : users) {  
  8.         if (user != 0) {  
  9.   
  10.     }  
  11.     return res;  
  12. }  
这里调用Installer的install方法去创建data目录,因为默认的user只有一个,并且userID为0,我们这里先不关注user的部分。来看一下Installd处理install的方法: [cpp] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. static int do_install(char **arg, char reply[REPLY_MAX])  
  2. {  
  3.     return install(arg[0], atoi(arg[1]), atoi(arg[2]), arg[3]); /* pkgname, uid, gid, seinfo */  
  4. }  
  5.   
  6. int install(const char *pkgname, uid_t uid, gid_t gid, const char *seinfo)  
  7. {  
  8.     char pkgdir[PKG_PATH_MAX];  
  9.     char libsymlink[PKG_PATH_MAX];  
  10.     char applibdir[PKG_PATH_MAX];  
  11.     struct stat libStat;  
  12.   
  13.     if ((uid < AID_SYSTEM) || (gid < AID_SYSTEM)) {  
  14.         ALOGE("invalid uid/gid: %d %d\n", uid, gid);  
  15.         return -1;  
  16.     }  
  17.   
  18.     if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, 0)) {  
  19.         ALOGE("cannot create package path\n");  
  20.         return -1;  
  21.     }  
  22.   
  23.     if (create_pkg_path(libsymlink, pkgname, PKG_LIB_POSTFIX, 0)) {  
  24.         ALOGE("cannot create package lib symlink origin path\n");  
  25.         return -1;  
  26.     }  
  27.   
  28.     if (create_pkg_path_in_dir(applibdir, &android_app_lib_dir, pkgname, PKG_DIR_POSTFIX)) {  
  29.         ALOGE("cannot create package lib symlink dest path\n");  
  30.         return -1;  
  31.     }  
  32.   
  33.     if (mkdir(pkgdir, 0751) < 0) {  
  34.         ALOGE("cannot create dir '%s': %s\n", pkgdir, strerror(errno));  
  35.         return -1;  
  36.     }  
  37.     if (chmod(pkgdir, 0751) < 0) {  
  38.         ALOGE("cannot chmod dir '%s': %s\n", pkgdir, strerror(errno));  
  39.         unlink(pkgdir);  
  40.         return -1;  
  41.     }  
  42.   
  43.     if (lstat(libsymlink, &libStat) < 0) {  
  44.         if (errno != ENOENT) {  
  45.             ALOGE("couldn't stat lib dir: %s\n", strerror(errno));  
  46.             return -1;  
  47.         }  
  48.     } else {  
  49.         if (S_ISDIR(libStat.st_mode)) {  
  50.             if (delete_dir_contents(libsymlink, 1, 0) < 0) {  
  51.                 ALOGE("couldn't delete lib directory during install for: %s", libsymlink);  
  52.                 return -1;  
  53.             }  
  54.         } else if (S_ISLNK(libStat.st_mode)) {  
  55.             if (unlink(libsymlink) < 0) {  
  56.                 ALOGE("couldn't unlink lib directory during install for: %s", libsymlink);  
  57.                 return -1;  
  58.             }  
  59.         }  
  60.     }  
  61.   
  62.     if (symlink(applibdir, libsymlink) < 0) {  
  63.         ALOGE("couldn't symlink directory '%s' -> '%s': %s\n", libsymlink, applibdir,  
  64.                 strerror(errno));  
  65.         unlink(pkgdir);  
  66.         return -1;  
  67.     }  
  68.   
  69.     if (chown(pkgdir, uid, gid) < 0) {  
  70.         ALOGE("cannot chown dir '%s': %s\n", pkgdir, strerror(errno));  
  71.         unlink(libsymlink);  
  72.         unlink(pkgdir);  
  73.         return -1;  
  74.     }  
  75.   
  76.     return 0;  
  77. }  
这里首先构造几个目录名:pkgdir为/data/data/包名,libsymlink为/data/data/包名/lib,applibdir为/data/app-lib/包名。然后创建pkgdir的目录,并修改相应的权限。然后创建/data/data/包名/lib指向/data/app-lib/包名的符号链接。我们知道安装到data/app-lib下面的nativeLibrary的目录其实”包名-num“,所以我们需要重新建立符号链接,这也就是在scanPackageLI方法中mInstaller.linkNativeLibraryDirectory的作用:
[cpp] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. static int do_linklib(char **arg, char reply[REPLY_MAX])  
  2. {  
  3.     return linklib(arg[0], arg[1], atoi(arg[2]));  
  4. }  
  5.   
  6. int linklib(const char* pkgname, const char* asecLibDir, int userId)  
  7. {  
  8.     char pkgdir[PKG_PATH_MAX];  
  9.     char libsymlink[PKG_PATH_MAX];  
  10.     struct stat s, libStat;  
  11.     int rc = 0;  
  12.   
  13.     if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, userId)) {  
  14.         ALOGE("cannot create package path\n");  
  15.         return -1;  
  16.     }  
  17.     if (create_pkg_path(libsymlink, pkgname, PKG_LIB_POSTFIX, userId)) {  
  18.         ALOGE("cannot create package lib symlink origin path\n");  
  19.         return -1;  
  20.     }  
  21.   
  22.     if (stat(pkgdir, &s) < 0) return -1;  
  23.   
  24.     if (chown(pkgdir, AID_INSTALL, AID_INSTALL) < 0) {  
  25.         ALOGE("failed to chown '%s': %s\n", pkgdir, strerror(errno));  
  26.         return -1;  
  27.     }  
  28.   
  29.     if (chmod(pkgdir, 0700) < 0) {  
  30.         ALOGE("linklib() 1: failed to chmod '%s': %s\n", pkgdir, strerror(errno));  
  31.         rc = -1;  
  32.         goto out;  
  33.     }  
  34.   
  35.     if (lstat(libsymlink, &libStat) < 0) {  
  36.         if (errno != ENOENT) {  
  37.             ALOGE("couldn't stat lib dir: %s\n", strerror(errno));  
  38.             rc = -1;  
  39.             goto out;  
  40.         }  
  41.     } else {  
  42.         if (S_ISDIR(libStat.st_mode)) {  
  43.             if (delete_dir_contents(libsymlink, 1, 0) < 0) {  
  44.                 rc = -1;  
  45.                 goto out;  
  46.             }  
  47.         } else if (S_ISLNK(libStat.st_mode)) {  
  48.             if (unlink(libsymlink) < 0) {  
  49.                 ALOGE("couldn't unlink lib dir: %s\n", strerror(errno));  
  50.                 rc = -1;  
  51.                 goto out;  
  52.             }  
  53.         }  
  54.     }  
  55.   
  56.     if (symlink(asecLibDir, libsymlink) < 0) {  
  57.         ALOGE("couldn't symlink directory '%s' -> '%s': %s\n", libsymlink, asecLibDir,  
  58.                 strerror(errno));  
  59.         rc = -errno;  
  60.         goto out;  
  61.     }  
这里的第一个参数是packageName;第二个参数是nativeLibraryDir,也就是/data/app-lib/包名-num;第三个参数userID为0。在linklib中,与install一样,首先创建几个目录路径:pkgdir为/data/data/包名,libsymlink为/data/data/包名/lib。然后先把/data/data/包名/lib这个符号链接删除,然后创建一个新的符号链接/data/data/包名/lib指向/data/app-lib/包名-num。回到scanPackageLI函数中,最后调用performDexOptLI对APK安装包中的dex文件做优化,当然这里会依据当前是采用DVM还是ART的运行环境,分别用dexopt和dex2oat两种不同的命令对classes.dex文件做优化并存储在/data/dalvik-cache下面的文件里,关于dex文件优化的部分,可以参考一下Android虚拟机相关知识。接着回到installNewPackageLI函数中调用updateSettingsLI去更新Settings中的设置:
[java] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName,  
  2.         int[] allUsers, boolean[] perUserInstalled,  
  3.         PackageInstalledInfo res) {  
  4.     String pkgName = newPackage.packageName;  
  5.     synchronized (mPackages) {  
  6.         mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_INCOMPLETE);  
  7.         mSettings.writeLPr();  
  8.     }  
  9.   
  10.     if ((res.returnCode = moveDexFilesLI(newPackage))  
  11.             != PackageManager.INSTALL_SUCCEEDED) {  
  12.         // Discontinue if moving dex files failed.  
  13.         return;  
  14.     }  
  15.   
  16.     if (DEBUG_INSTALL) Slog.d(TAG, "New package installed in " + newPackage.mPath);  
  17.   
  18.     synchronized (mPackages) {  
  19.         updatePermissionsLPw(newPackage.packageName, newPackage,  
  20.                 UPDATE_PERMISSIONS_REPLACE_PKG | (newPackage.permissions.size() > 0  
  21.                         ? UPDATE_PERMISSIONS_ALL : 0));  
  22.         if (isSystemApp(newPackage)) {  
  23.   
  24.         }  
  25.         res.name = pkgName;  
  26.         res.uid = newPackage.applicationInfo.uid;  
  27.         res.pkg = newPackage;  
  28.         mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_COMPLETE);  
  29.         mSettings.setInstallerPackageName(pkgName, installerPackageName);  
  30.         res.returnCode = PackageManager.INSTALL_SUCCEEDED;  
  31.         //to update install status  
  32.         mSettings.writeLPr();  
  33.     }  
  34. }  

这里首先调用Settings的writeLPr方法更新packages.xml文件,将新安装的package信息写到这个xml文件。moveDexFilesLI这里用于处理系统更新后安装目录有变化的情况,需要将原来的优化后的dex文件做重命名。updatePermissionsLPw我们在前面一章也介绍过,它用于给当前安装的APK分配权限,并把相应的gid号保存在PackageSetting或者SharedUserSetting的gids数组中。到这里installPackageLI就介绍完了,回到processPendingInstall方法中:
[java] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1.             final boolean update = res.removedInfo.removedPackage != null;  
  2.             boolean doRestore = (!update  
  3.                     && res.pkg != null  
  4.                     && res.pkg.applicationInfo.backupAgentName != null);  
  5.   
  6.             int token;  
  7.             if (mNextInstallToken < 0) mNextInstallToken = 1;  
  8.             token = mNextInstallToken++;  
  9.   
  10.             PostInstallData data = new PostInstallData(args, res);  
  11.             mRunningInstalls.put(token, data);  
  12.   
  13.             if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {  
  14.                  //与BackAgent(云备份)相关  
  15.             }  
  16.   
  17.             if (!doRestore) {  
  18.                 Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);  
  19.                 mHandler.sendMessage(msg);  
  20.             }  
  21.         }  
  22.     });  
  23. }  

首先构造一个PostInstallData数据结构并添加到正在安装的列表mRunningInstalls中,这里跳过与BackAgent相关的部分。最后往PackageHandler发送一个POST_INSTALL消息,来看PackageHandler如何处理POST_INSTALL消息:
[java] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. case POST_INSTALL: {  
  2.     if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);  
  3.     PostInstallData data = mRunningInstalls.get(msg.arg1);  
  4.     mRunningInstalls.delete(msg.arg1);  
  5.     boolean deleteOld = false;  
  6.   
  7.     if (data != null) {  
  8.         InstallArgs args = data.args;  
  9.         PackageInstalledInfo res = data.res;  
  10.   
  11.         if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {  
  12.             res.removedInfo.sendBroadcast(falsetruefalse);  
  13.             Bundle extras = new Bundle(1);  
  14.             extras.putInt(Intent.EXTRA_UID, res.uid);  
  15.   
  16.             int[] firstUsers;  
  17.             int[] updateUsers = new int[0];  
  18.             if (res.origUsers == null || res.origUsers.length == 0) {  
  19.                 firstUsers = res.newUsers;  
  20.             } else {  
  21.   
  22.             }  
  23.             sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,  
  24.                     res.pkg.applicationInfo.packageName,  
  25.                     extras, nullnull, firstUsers);  
  26.             final boolean update = res.removedInfo.removedPackage != null;  
  27.             if (update) {  
  28.                 extras.putBoolean(Intent.EXTRA_REPLACING, true);  
  29.             }  
  30.             sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,  
  31.                     res.pkg.applicationInfo.packageName,  
  32.                     extras, nullnull, updateUsers);  
  33.             if (update) {  
  34.   
  35.             }  
  36.             if (res.removedInfo.args != null) {  
  37.                 deleteOld = true;  
  38.             }  
  39.         }  
  40.   
  41.         Runtime.getRuntime().gc();  
  42.   
  43.         if (deleteOld) {  
  44.             synchronized (mInstallLock) {  
  45.                 res.removedInfo.args.doPostDeleteLI(true);  
  46.             }  
  47.         }  
  48.         if (args.observer != null) {  
  49.             try {  
  50.                 args.observer.packageInstalled(res.name, res.returnCode);  
  51.             } catch (RemoteException e) {  
  52.                 Slog.i(TAG, "Observer no longer exists.");  
  53.             }  
  54.         }  
  55.     } else {  
  56.         Slog.e(TAG, "Bogus post-install token " + msg.arg1);  
  57.     }  
  58. break;  

在POST_INSTALL消息中,主要就是发送一些安装成功的广播,这些广播可以被launch接收,并在桌面上面增加图标等。然后向我们注册的observer回调安装成功的callback。这样在内部存储空间上APK安装过程就介绍完了。我们来看一下大致的的流程图:

Android PackageManagerService分析二:安装APK

从前面的介绍,我们可以把安装APK的过程简化为以下几步: 1.复制文件到code、library和resource等文件 2.解析待安装的APK的manifest文件,并把activity、service、provider等信息更新到PMS的全局数据结构中 3.更新Settings中的PackageSetting等信息 4.调用installd在/data/data和/data/dalvik-cache中新建package的文件目录,并link相应的文件

安装在外部存储空间

在介绍AsecInstallArgs的copyApk之前,我们先来简单的了解一下Android上面的目录结构:/system 存放的是rom的信息;/system/app 存放rom本身附带的软件即系统软件;/system/data 存放/system/app 中核心系统软件的数据文件信息。
    /data 存放的是用户的软件信息(非自带rom安装的软件);/data/app 存放用户安装的软件;/data/data 存放所有软件(包括/system/app 和 /data/app 和 /mnt/asec中装的软件)的一些lib和xml文件等数据信息;/data/dalvik-cache 缓存的dex文件,这里的文件都是可以删除的。
    /mnt 目录,熟悉linux的人都清楚,linux默认挂载外部设备都会挂到这个目录下面去,如将sd卡挂载上去后,会生成一个/mnt/sdcard 目录。
    /sdcard 目录,这是一个软链接(相当于windows的文件夹的快捷方式),链接到/mnt/sdcard 目录,即这个目录的内容就是sdcard的内容。
    在Android 2.2之后的版本允许将应用程序安装于SD卡,每一个安装在SD卡的应用程序,都可以在SD卡中的/sdcard/.android_secure 目录里找到名称中有出现它的程序名,和副文件名为asec的经过特殊加密处理后的档案。当SD卡挂载于手机时,/mnt/sdcard/.android_secure 目录会被映射到/mnt/asec 目录和 /mnt/secure 目录。其中/mnt/asec 目录中主要是程序的安装目录,包括其执行文件和lib文件等;而/mnt/secure 目录中就存放程序加密后的档案。也就是说,在/mnt路径下看到的/mnt/asec目录和/mnt/secure目录并不是真正存在在手机内存或者sd卡的分区挂载目录,它们只是/mnt/sdcard/.android_secure目录的一个影像而已。
    因此,用户程序安装到到sd卡上后,其内容可能分散到:/mnt/asec , /mnt/secure , /data/data 。

我们来看AsecInstallArgs的copyApk方法,通过前面的介绍,copyApk就是将原来的安装文件以及library复制到SD卡中的android_secure目录中,这里会借助ContainerService和MountService。从前面安装到内部存储空间的流程上我们知道,FileInstallArgs和AsecInstallArgs主要区别就是在copyApk和doRename两个方法上面: [java] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {  
  2.     if (temp) {  
  3.         createCopyFile();  
  4.     } else {  
  5.         /* 
  6.          * Pre-emptively destroy the container since it's destroyed if 
  7.          * copying fails due to it existing anyway. 
  8.          */  
  9.         PackageHelper.destroySdDir(cid);  
  10.     }  
  11.   
  12.     final String newCachePath;  
  13.     try {  
  14.         mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, packageURI,  
  15.                 Intent.FLAG_GRANT_READ_URI_PERMISSION);  
  16.         newCachePath = imcs.copyResourceToContainer(packageURI, cid, getEncryptKey(),  
  17.                 RES_FILE_NAME, PUBLIC_RES_FILE_NAME, isExternal(), isFwdLocked());  
  18.     } finally {  
  19.         mContext.revokeUriPermission(packageURI, Intent.FLAG_GRANT_READ_URI_PERMISSION);  
  20.     }  
  21.   
  22.     if (newCachePath != null) {  
  23.         setCachePath(newCachePath);  
  24.         return PackageManager.INSTALL_SUCCEEDED;  
  25.     } else {  
  26.         return PackageManager.INSTALL_FAILED_CONTAINER_ERROR;  
  27.     }  
  28. }  

首先调用createCopyFile顺序的在SD卡上面创建目录结构,这些目录用于保存待安装的APK文件以及它们的library等资源:
[java] view plain copy Android PackageManagerService分析二:安装APKAndroid PackageManagerService分析二:安装APK
  1. void createCopyFile() {  
  2.     cid = getTempContainerId();  
  3. }  
  4.   
  5. ic String getTempContainerId() {  
  6. int tmpIdx = 1;  
  7. String list[] = PackageHelper.getSecureContainerList();  
  8. if (list != null) {  
  9.     for (final String name : list) {  
  10.         // Ignore null and non-temporary container entries  
  11.         if (name == null || !name.startsWith(mTempContainerPrefix)) {  
  12.             continue;  
  13.         }  
  14.   
  15.         String subStr = name.substring(mTempContainerPrefix.length());  
  16.         try {  
  17.             int cid = Integer.parseInt(subStr);  
  18.             if (cid >= tmpIdx) {  
  19.                 tmpIdx = cid + 1;  
  20.             }  
  21.         } catch (NumberFormatException e) {  
  22.         }  
  23.     }  
  24. }  
  25. return mTempContainerPrefix + tmpIdx;  

getTempContainerId调用MountService的方法去获取/mnt/asec下面的所有子目录,这些子目录都是以"smdl2tmp"开始,结尾是一个数字来命名。getTempContainerId找到一个尚未被使用的目录名赋予给cid。在copyApk方法中调用ContainService的copyResourceToContainer方法完成真正的创建目录以及拷贝文件的操作,这部分有兴趣的读者可以看看这个方法的实现。