Android6.0 PKMS扫描目录和调用接口安装应用的区别

Android安装应用分两种,一种是直接调用PKMS的接口安装,另一种是扫描目录安装,比如系统开机的时候会扫描data/app目录进行安装。


一、调用installPackageAsUser函数安装

我们先来看调用接口应用安装应用,都会通过如下函数,这个函数先会通过传进来的uid来判断是否是adb安装的,然后创建了一个InstallParams对象这个对象是HandlerParams的子类,然后发送消息。

  1. @Override  
  2. public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,  
  3.         int installFlags, String installerPackageName, VerificationParams verificationParams,  
  4.         String packageAbiOverride, int userId) {  
  5.     mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);  
  6.   
  7.     final int callingUid = Binder.getCallingUid();  
  8.     enforceCrossUserPermission(callingUid, userId, truetrue"installPackageAsUser");  
  9.   
  10.     if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {  
  11.         try {  
  12.             if (observer != null) {  
  13.                 observer.onPackageInstalled("", INSTALL_FAILED_USER_RESTRICTED, null, null);  
  14.             }  
  15.         } catch (RemoteException re) {  
  16.         }  
  17.         return;  
  18.     }  
  19.   
  20.     if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {  
  21.         installFlags |= PackageManager.INSTALL_FROM_ADB;//根据uid来判断是否是adb安装的  
  22.   
  23.     } else {  
  24.         // Caller holds INSTALL_PACKAGES permission, so we're less strict  
  25.         // about installerPackageName.  
  26.   
  27.         installFlags &= ~PackageManager.INSTALL_FROM_ADB;  
  28.         installFlags &= ~PackageManager.INSTALL_ALL_USERS;  
  29.     }  
  30.   
  31.     UserHandle user;  
  32.     if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {  
  33.         user = UserHandle.ALL;  
  34.     } else {  
  35.         user = new UserHandle(userId);  
  36.     }  
  37.   
  38.     // Only system components can circumvent runtime permissions when installing.  
  39.     if ((installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0  
  40.             && mContext.checkCallingOrSelfPermission(Manifest.permission  
  41.             .INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {  
  42.         throw new SecurityException("You need the "  
  43.                 + "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "  
  44.                 + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");  
  45.     }  
  46.   
  47.     verificationParams.setInstallerUid(callingUid);  
  48.   
  49.     final File originFile = new File(originPath);  
  50.     final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile);  
  51.   
  52.     final Message msg = mHandler.obtainMessage(INIT_COPY);  
  53.     InstallParams installParams = new InstallParams(origin, null, observer, installFlags, installerPackageName,  
  54.             null, verificationParams, user, packageAbiOverride, null);  
  55.     installParams.setHandlerType(HandlerParams.INSTALL_TYPE);  
  56.     msg.obj = installParams;  
  57.     mHandler.sendMessage(msg);//发送消息  
  58. }  


1.1 adb install

普通的安装我们就调用这个接口,我们来看下adb install又是如何实现的。

我们来看下adb中Commandline.cpp中的如下代码,就是执行adb install命令的。

  1. static int install_app(transport_type transport, const char* serial, int argc,  
  2.                        const char** argv)  
  3. {  
  4.     static const char *const DATA_DEST = "/data/local/tmp/%s";  
  5.     static const char *const SD_DEST = "/sdcard/tmp/%s";  
  6.     const char* where = DATA_DEST;  
  7.     int i;  
  8.     struct stat sb;  
  9.   
  10.     for (i = 1; i < argc; i++) {  
  11.         if (!strcmp(argv[i], "-s")) {  
  12.             where = SD_DEST;  
  13.         }  
  14.     }  
  15.   
  16.     // Find last APK argument.  
  17.     // All other arguments passed through verbatim.  
  18.     int last_apk = -1;  
  19.     for (i = argc - 1; i >= 0; i--) {  
  20.         const char* file = argv[i];  
  21.         char* dot = strrchr(file, '.');  
  22.         if (dot && !strcasecmp(dot, ".apk")) {  
  23.             if (stat(file, &sb) == -1 || !S_ISREG(sb.st_mode)) {  
  24.                 fprintf(stderr, "Invalid APK file: %s\n", file);  
  25.                 return -1;  
  26.             }  
  27.   
  28.             last_apk = i;  
  29.             break;  
  30.         }  
  31.     }  
  32.   
  33.     if (last_apk == -1) {  
  34.         fprintf(stderr, "Missing APK file\n");  
  35.         return -1;  
  36.     }  
  37.   
  38.     const char* apk_file = argv[last_apk];  
  39.     char apk_dest[PATH_MAX];  
  40.     snprintf(apk_dest, sizeof apk_dest, where, get_basename(apk_file));  
  41.     int err = do_sync_push(apk_file, apk_dest, 0 /* no show progress */);//先push apk文件  
  42.     if (err) {  
  43.         goto cleanup_apk;  
  44.     } else {  
  45.         argv[last_apk] = apk_dest; /* destination name, not source location */  
  46.     }  
  47.   
  48.     err = pm_command(transport, serial, argc, argv);//调用pm命令  
  49.   
  50. cleanup_apk:  
  51.     delete_file(transport, serial, apk_dest);//删除文件  
  52.     return err;  
  53. }  

上面函数先是把apk文件push到data/local/tmp下面,然后调用pm命令,最后完成之后再删除文件。

下面我们分别看下几个函数。

  1. static int pm_command(transport_type transport, const char* serial,  
  2.                       int argc, const char** argv)  
  3. {  
  4.     std::string cmd = "shell:pm";  
  5.   
  6.     while (argc-- > 0) {  
  7.         cmd += " " + escape_arg(*argv++);  
  8.     }  
  9.   
  10.     return send_shell_command(transport, serial, cmd);  
  11. }  

删除就是调用了adb shell rm命令。

  1. static int delete_file(transport_type transport, const char* serial, char* filename)  
  2. {  
  3.     std::string cmd = "shell:rm -f " + escape_arg(filename);  
  4.     return send_shell_command(transport, serial, cmd);  
  5. }  

我们再看下PM命令,是在Pm.Java文件中,最后也是调用了installPackageAsUser函数,这个我们就不看了。下面继续分析installPackageAsUser函数。


1.2 MCS_BOUND

上面分析到发送一个INIT_COPY消息,我们再来看下消息处理。

  1. case INIT_COPY: {  
  2.     HandlerParams params = (HandlerParams) msg.obj;  
  3.     int idx = mPendingInstalls.size();  
  4.     if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params);  
  5.     // If a bind was already initiated we dont really  
  6.     // need to do anything. The pending install  
  7.     // will be processed later on.  
  8.     if (!mBound) {  
  9.         // If this is the only one pending we might  
  10.         // have to bind to the service again.  
  11.         if (!connectToService()) {  
  12.             Slog.e(TAG, "Failed to bind to media container service");  
  13.             params.serviceError();  
  14.             return;  
  15.         } else {  
  16.             // Once we bind to the service, the first  
  17.             // pending request will be processed.  
  18.             mPendingInstalls.add(idx, params);  
  19.         }  
  20.     } else {  
  21.         mPendingInstalls.add(idx, params);  
  22.         // Already bound to the service. Just make  
  23.         // sure we trigger off processing the first request.  
  24.         if (idx == 0) {  
  25.             mHandler.sendEmptyMessage(MCS_BOUND);  
  26.         }  
  27.     }  
  28.     break;  
  29. }  

消息处理先要看mBound是否为true,为true代表DefaultContainerService连接上了(这个service后续需要copy apk等文件的),这个时候会发送一个MCS_BOUND消息。我们先等等看这个消息的处理。这里如果mBound是false,就要调用connectToService函数。

我们来看这个函数,就是调用BindService来启动一个Service

  1. private boolean connectToService() {  
  2.     if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to bind to" +  
  3.             " DefaultContainerService");  
  4.     Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);  
  5.     Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);  
  6.     if (mContext.bindServiceAsUser(service, mDefContainerConn,  
  7.             Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {  
  8.         Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
  9.         mBound = true;  
  10.         return true;  
  11.     }  
  12.     Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);  
  13.     return false;  
  14. }  


  1. static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(  
  2.         DEFAULT_CONTAINER_PACKAGE,  
  3.         "com.android.defcontainer.DefaultContainerService");  

我们再来看Connection,连接上之后会把这个Service的Binder对象IMediaContainerService ,发送一个MCS_BOUND消息。

  1. final private DefaultContainerConnection mDefContainerConn =  
  2.         new DefaultContainerConnection();  
  3. class DefaultContainerConnection implements ServiceConnection {  
  4.     public void onServiceConnected(ComponentName name, IBinder service) {  
  5.         if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceConnected");  
  6.         IMediaContainerService imcs =  
  7.             IMediaContainerService.Stub.asInterface(service);  
  8.         mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));  
  9.     }  
  10.   
  11.     public void onServiceDisconnected(ComponentName name) {  
  12.         if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected");  
  13.     }  
  14. }  

现在我们再来看这个消息处理。其实这个函数主要就是调用了调用HandlerParams的startCopy函数,其他就是判断Service是否断开,是否mPendingInstalls还有其他没有安装的应用等处理。

  1. case MCS_BOUND: {  
  2.     if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");  
  3.     if (msg.obj != null) {  
  4.         mContainerService = (IMediaContainerService) msg.obj;  
  5.     }  
  6.     if (mContainerService == null) {  
  7.         if (!mBound) {  
  8.             // Something seriously wrong since we are not bound and we are not  
  9.             // waiting for connection. Bail out.  
  10.             Slog.e(TAG, "Cannot bind to media container service");  
  11.             for (HandlerParams params : mPendingInstalls) {  
  12.                 // Indicate service bind error  
  13.                 params.serviceError();  
  14.             }  
  15.             mPendingInstalls.clear();  
  16.         } else {  
  17.             Slog.w(TAG, "Waiting to connect to media container service");  
  18.         }  
  19.     } else if (mPendingInstalls.size() > 0) {  
  20.         HandlerParams params = mPendingInstalls.get(0);  
  21.         if (params != null) {  
  22.             if (params.startCopy()) {//调用HandlerParams的startCopy函数  
  23.                 // We are done...  look for more work or to  
  24.                 // go idle.  
  25.                 if (DEBUG_SD_INSTALL) Log.i(TAG,  
  26.                         "Checking for more work or unbind...");  
  27.                 // Delete pending install  
  28.                 if (mPendingInstalls.size() > 0) {  
  29.                     mPendingInstalls.remove(0);  
  30.                 }  
  31.                 if (mPendingInstalls.size() == 0) {  
  32.                     if (mBound) {  
  33.                         if (DEBUG_SD_INSTALL) Log.i(TAG,  
  34.                                 "Posting delayed MCS_UNBIND");  
  35.                         removeMessages(MCS_UNBIND);  
  36.                         Message ubmsg = obtainMessage(MCS_UNBIND);  
  37.                         // Unbind after a little delay, to avoid  
  38.                         // continual thrashing.  
  39.                         sendMessageDelayed(ubmsg, 10000);  
  40.                     }  
  41.                 } else {  
  42.                     // There are more pending requests in queue.  
  43.                     // Just post MCS_BOUND message to trigger processing  
  44.                     // of next pending install.  
  45.                     if (DEBUG_SD_INSTALL) Log.i(TAG,  
  46.                             "Posting MCS_BOUND for next work");  
  47.                     mHandler.sendEmptyMessage(MCS_BOUND);  
  48.                 }  
  49.             }  
  50.         }  
  51.     } else {  
  52.         // Should never happen ideally.  
  53.         Slog.w(TAG, "Empty queue");  
  54.     }  
  55.     break;  
  56. }  


1.3 startCopy

我们来看下面这个函数,如果超过4次出错,直接发送一个MCS_GIVE_UP消息,这个消息里面会调用mPendingInstalls.remove(0);把mPendingInstalls中第一项去除(就是去除第一个要安装的应用),当我们成功安装完在MCS_BOUND也会将第一个去除的。

当然正常的是调用handleStartCopy函数,只有出现RemoteException的是否才会返回false,代表这个copy不成功,也就不会remove这项应用信息。下次还会再调调用startCopy函数继续处理。

  1. final boolean startCopy() {  
  2.     boolean res;  
  3.     try {  
  4.         if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);  
  5.   
  6.         if (++mRetries > MAX_RETRIES) {//如果安装没有成功,超过4次。错误处理  
  7.             Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");  
  8.             mHandler.sendEmptyMessage(MCS_GIVE_UP);  
  9.             handleServiceError();  
  10.             return false;  
  11.         } else {  
  12.             handleStartCopy();  
  13.             res = true;  
  14.         }  
  15.     } catch (RemoteException e) {  
  16.         if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");  
  17.         mHandler.sendEmptyMessage(MCS_RECONNECT);  
  18.         res = false;  
  19.     }  
  20.     handleReturnCode();  
  21.     return res;  
  22. }  

我们先看下handleServiceError函数,也像handleStartCopy会调用createInstallArgs,创建一个InstallArgs,然后mRet为失败的。这些后面在处理processPendingInstall函数的时候需要

  1. @Override  
  2. void handleServiceError() {  
  3.     mArgs = createInstallArgs(this);  
  4.     mRet = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;  
  5. }  

我们再来看看handleStartCopy函数,先会调用createInstallArgs来创建一个InstallArgs,这里只是一个基类而已,然后会调用InstallArgs的copyApk函数,来copy apk文件到data/app下面.

  1. public void handleStartCopy() throws RemoteException {  
  2.     int ret = PackageManager.INSTALL_SUCCEEDED;  
  3.     ......  
  4.     final InstallArgs args = createInstallArgs(this);  
  5.     mArgs = args;  
  6.     ......  
  7.     else {  
  8.             /* 
  9.              * No package verification is enabled, so immediately start 
  10.              * the remote call to initiate copy using temporary file. 
  11.              */  
  12.             ret = args.copyApk(mContainerService, true);  
  13.         }  
  14.     }  
  15.   
  16.     mRet = ret;  
  17. }  

我们先来看下createInstallArgs函数,根据InstallParams来创建不同的InstallArgs,这里我们只关心FileInstallArgs。

  1. private InstallArgs createInstallArgs(InstallParams params) {  
  2.     if (params.move != null) {  
  3.         return new MoveInstallArgs(params);  
  4.     } else if (installOnExternalAsec(params.installFlags) || params.isForwardLocked()) {  
  5.         return new AsecInstallArgs(params);  
  6.     } else {  
  7.         return new FileInstallArgs(params);  
  8.     }  
  9. }  

我们来看下FileInstallArgs 的copyApk函数,先是调用PackageInstallerService的allocateStageDirLegacy函数来创建一个目录,然后调用ImediaContainerService binder调用copyPackage函数,这个就是之前的DefaultContainerService。

  1. int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {  
  2.     if (origin.staged) {  
  3.         if (DEBUG_INSTALL) Slog.d(TAG, origin.file + " already staged; skipping copy");  
  4.         codeFile = origin.file;  
  5.         resourceFile = origin.file;  
  6.         return PackageManager.INSTALL_SUCCEEDED;  
  7.     }  
  8.   
  9.     try {  
  10.         final File tempDir = mInstallerService.allocateStageDirLegacy(volumeUuid);  
  11.         codeFile = tempDir;  
  12.         resourceFile = tempDir;  
  13.     } catch (IOException e) {  
  14.         Slog.w(TAG, "Failed to create copy file: " + e);  
  15.         return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;  
  16.     }  
  17.   
  18.     final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {  
  19.         @Override  
  20.         public ParcelFileDescriptor open(String name, int mode) throws RemoteException {  
  21.             if (!FileUtils.isValidExtFilename(name)) {  
  22.                 throw new IllegalArgumentException("Invalid filename: " + name);  
  23.             }  
  24.             try {  
  25.                 final File file = new File(codeFile, name);  
  26.                 final FileDescriptor fd = Os.open(file.getAbsolutePath(),  
  27.                         O_RDWR | O_CREAT, 0644);  
  28.                 Os.chmod(file.getAbsolutePath(), 0644);  
  29.                 return new ParcelFileDescriptor(fd);  
  30.             } catch (ErrnoException e) {  
  31.                 throw new RemoteException("Failed to open: " + e.getMessage());  
  32.             }  
  33.         }  
  34.     };  
  35.   
  36.     int ret = PackageManager.INSTALL_SUCCEEDED;  
  37.     ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);  
  38.     if (ret != PackageManager.INSTALL_SUCCEEDED) {  
  39.         Slog.e(TAG, "Failed to copy package");  
  40.         return ret;  
  41.     }  
  42.   
  43.     final File libraryRoot = new File(codeFile, LIB_DIR_NAME);  
  44.     NativeLibraryHelper.Handle handle = null;  
  45.     try {  
  46.         handle = NativeLibraryHelper.Handle.create(codeFile);  
  47.         ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,  
  48.                 abiOverride);  
  49.     } catch (IOException e) {  
  50.         Slog.e(TAG, "Copying native libraries failed", e);  
  51.         ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;  
  52.     } finally {  
  53.         IoUtils.closeQuietly(handle);  
  54.     }  
  55.   
  56.     return ret;  
  57. }  

这里的data/app下的临时文件我们来看下是怎么样的

  1.  # ls  
  2. IflytekInput.apk  
  3. MOffice.apk  
  4. NotePadPlus.apk  
  5. vmdl202298311.tmp  

这里vmdl202298311.tmp只是一个目录,下面还有base.apk 和lib,lib也是一个目录里面还有各个so文件

  1. base.apk  
  2. lib  

这里就完成了将apk copy到data/app下面的临时目录中。


1.4 processPendingInstall函数

然后在startCopy函数中又会调用handleReturnCode函数,这里就是调用了processPendingInstall函数,注意参数mRett就是之前调用copyApk的返回值。

  1. @Override  
  2. void handleReturnCode() {  
  3.     // If mArgs is null, then MCS couldn't be reached. When it  
  4.     // reconnects, it will try again to install. At that point, this  
  5.     // will succeed.  
  6.     if (mArgs != null) {  
  7.         processPendingInstall(mArgs, mRet);  
  8.           
  9.     }  
  10. }  

processPendingInstall函数如下,这里的currentStatus参数就是之前调用copyApk的返回值代表是否copy文件成功,然后调用installPackageLI装载应用到PKMS中,新建一个PostInstallData对象放入mRunningInstalls中代表正在安装的应用,最后发送一个POST_INSTALL消息

  1. private void processPendingInstall(final InstallArgs args, final int currentStatus) {  
  2.      mHandler.post(new Runnable() {  
  3.          public void run() {  
  4.              mHandler.removeCallbacks(this);  
  5.               // Result object to be returned  
  6.                 
  7.              PackageInstalledInfo res = new PackageInstalledInfo();  
  8.              res.returnCode = currentStatus;  
  9.              res.uid = -1;  
  10.              res.pkg = null;  
  11.              res.removedInfo = new PackageRemovedInfo();  
  12.              if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {//copy文件成功  
  13.                  args.doPreInstall(res.returnCode);  
  14.                  synchronized (mInstallLock) {  
  15.                      installPackageLI(args, res);  
  16.                  }  
  17.                  args.doPostInstall(res.returnCode, res.uid);  
  18.              }  
  19.   
  20.              final boolean update = res.removedInfo.removedPackage != null;  
  21.              final int flags = (res.pkg == null) ? 0 : res.pkg.applicationInfo.flags;  
  22.              boolean doRestore = !update  
  23.                      && ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0);  
  24.   
  25.   
  26.              int token;  
  27.              if (mNextInstallToken < 0) mNextInstallToken = 1;  
  28.              token = mNextInstallToken++;  
  29.   
  30.              PostInstallData data = new PostInstallData(args, res);  
  31.              mRunningInstalls.put(token, data);//正在安装的应用  
  32.   
  33.              if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {  
  34.                  IBackupManager bm = IBackupManager.Stub.asInterface(  
  35.                          ServiceManager.getService(Context.BACKUP_SERVICE));  
  36.                  if (bm != null) {  
  37.                      try {  
  38.                          if (bm.isBackupServiceActive(UserHandle.USER_OWNER)) {  
  39.                              bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);  
  40.                          } else {  
  41.                              doRestore = false;  
  42.                          }  
  43.                      } catch (RemoteException e) {  
  44.                          // can't happen; the backup manager is local  
  45.                      } catch (Exception e) {  
  46.                          Slog.e(TAG, "Exception trying to enqueue restore", e);  
  47.                          doRestore = false;  
  48.                      }  
  49.                  } else {  
  50.                      Slog.e(TAG, "Backup Manager not found!");  
  51.                      doRestore = false;  
  52.                  }  
  53.              }  
  54.   
  55.              if (!doRestore) {  
  56.                  Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);  
  57.                  mHandler.sendMessage(msg);  
  58.              }  
  59.          }  
  60.      });  
  61.  }  

上面我们还要注意在调用installPackageLI函数前后都有调用args.doPostInstall函数,下面我么来看这个函数,没有uid参数的那个函数也是一样的。这个函数当没有安装成功就删除之前在data/app下面创建的apk临时目录。特别注意在调用完installPackageLI之后也要调用下这个函数,因为在installPackageLI函数的处理中可以会有比如应用已经安装过这样的情况等,都会导致安装失败,这个时候都要把临时文件清除。

  1. int doPostInstall(int status, int uid) {  
  2.     if (status != PackageManager.INSTALL_SUCCEEDED) {  
  3.         cleanUp();  
  4.     }  
  5.     return status;  
  6. }  

1.5 installPackageLI函数

我们先来看下installPackageLI函数如下代码,我们先会新建一个PackageParser来解析apk文件,这个我们之前的博客中分析过,这里就不看了。后面成功之后会调用args的rename函数,把临时文件重新命名。然后如果是一个新应用会调用installNewPackageLI函数。

  1. private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {  
  2.     final int installFlags = args.installFlags;  
  3.     final String installerPackageName = args.installerPackageName;  
  4.     final String volumeUuid = args.volumeUuid;  
  5.     final File tmpPackageFile = new File(args.getCodePath());  
  6.     final boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);  
  7.     final boolean onExternal = (((installFlags & PackageManager.INSTALL_EXTERNAL) != 0)  
  8.             || (args.volumeUuid != null));  
  9.     ......  
  10.   
  11.     final PackageParser.Package pkg;  
  12.     try {  
  13.         pkg = pp.parsePackage(tmpPackageFile, parseFlags);  
  14.     } catch (PackageParserException e) {  
  15.         res.setError("Failed parse during installPackageLI", e);  
  16.         return;  
  17.     }  
  18.     ......  
  19.   
  20.     if (!args.doRename(res.returnCode, pkg, oldCodePath)) {//重新命名  
  21.         res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");  
  22.         return;  
  23.     }  
  24.   
  25.     startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);  
  26.   
  27.     if (replace) {//升级  
  28.         replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,  
  29.                 installerPackageName, volumeUuid, res);  
  30.     } else {  
  31.         installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,  
  32.                 args.user, installerPackageName, volumeUuid, res);  
  33.     }  
  34.     ......  
  35. }  

FileInstallArgs的doRename函数调用了getNextCodePath函数来获取新的apk的目录的name,然后调用os的rename函数重新命名。然后Package的一些变量改下。

  1. boolean doRename(int status, PackageParser.Package pkg, String oldCodePath) {  
  2.     if (status != PackageManager.INSTALL_SUCCEEDED) {  
  3.         cleanUp();  
  4.         return false;  
  5.     }  
  6.   
  7.     final File targetDir = codeFile.getParentFile();  
  8.     final File beforeCodeFile = codeFile;  
  9.     final File afterCodeFile = getNextCodePath(targetDir, pkg.packageName);//获取新名字  
  10.   
  11.     if (DEBUG_INSTALL) Slog.d(TAG, "Renaming " + beforeCodeFile + " to " + afterCodeFile);  
  12.     try {  
  13.         Os.rename(beforeCodeFile.getAbsolutePath(), afterCodeFile.getAbsolutePath());//rename  
  14.     } catch (ErrnoException e) {  
  15.         Slog.w(TAG, "Failed to rename", e);  
  16.         return false;  
  17.     }  
  18.   
  19.     if (!SELinux.restoreconRecursive(afterCodeFile)) {  
  20.         Slog.w(TAG, "Failed to restorecon");  
  21.         return false;  
  22.     }  
  23.   
  24.     // Reflect the rename internally  
  25.     codeFile = afterCodeFile;  
  26.     resourceFile = afterCodeFile;  
  27.   
  28.     // Reflect the rename in scanned details  
  29.     pkg.codePath = afterCodeFile.getAbsolutePath();  
  30.     pkg.baseCodePath = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile,  
  31.             pkg.baseCodePath);  
  32.     pkg.splitCodePaths = FileUtils.rewriteAfterRename(beforeCodeFile, afterCodeFile,  
  33.             pkg.splitCodePaths);  
  34.   
  35.     // Reflect the rename in app info  
  36.     pkg.applicationInfo.volumeUuid = pkg.volumeUuid;  
  37.     pkg.applicationInfo.setCodePath(pkg.codePath);  
  38.     pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);  
  39.     pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);  
  40.     pkg.applicationInfo.setResourcePath(pkg.codePath);  
  41.     pkg.applicationInfo.setBaseResourcePath(pkg.baseCodePath);  
  42.     pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);  
  43.   
  44.     return true;  
  45. }  

我们看下getNextCodePath获取新的apk目录名字,就是apk的那么加一个后缀。

  1. private File getNextCodePath(File targetDir, String packageName) {  
  2.     int suffix = 1;  
  3.     File result;  
  4.     do {  
  5.         result = new File(targetDir, packageName + "-" + suffix);  
  6.         suffix++;  
  7.     } while (result.exists());  
  8.     return result;  
  9. }  

类似如下:

  1. com.moji.mjweather-1  


1.6 POST_INSTALL消息的处理

我们再来看看之前在processPendingInstall函数中发送的POST_INSTALL消息的处理:

这里主要的逻辑是当应用安装成功后,发送广播(比如Launcher应用接受广播后显示桌面图标)。

  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.             final String packageName = res.pkg.applicationInfo.packageName;  
  13.             res.removedInfo.sendBroadcast(falsetruefalse);  
  14.             Bundle extras = new Bundle(1);  
  15.             extras.putInt(Intent.EXTRA_UID, res.uid);  
  16.   
  17.             // Now that we successfully installed the package, grant runtime  
  18.             // permissions if requested before broadcasting the install.  
  19.             if ((args.installFlags  
  20.                     & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0) {  
  21.                 grantRequestedRuntimePermissions(res.pkg, args.user.getIdentifier(),  
  22.                         args.installGrantPermissions);  
  23.             }  
  24.   
  25.             // Determine the set of users who are adding this  
  26.             // package for the first time vs. those who are seeing  
  27.             // an update.  
  28.             int[] firstUsers;  
  29.             int[] updateUsers = new int[0];  
  30.             if (res.origUsers == null || res.origUsers.length == 0) {  
  31.                 firstUsers = res.newUsers;  
  32.             } else {  
  33.                 firstUsers = new int[0];  
  34.                 for (int i=0; i<res.newUsers.length; i++) {  
  35.                     int user = res.newUsers[i];  
  36.                     boolean isNew = true;  
  37.                     for (int j=0; j<res.origUsers.length; j++) {  
  38.                         if (res.origUsers[j] == user) {  
  39.                             isNew = false;  
  40.                             break;  
  41.                         }  
  42.                     }  
  43.                     if (isNew) {  
  44.                         int[] newFirst = new int[firstUsers.length+1];  
  45.                         System.arraycopy(firstUsers, 0, newFirst, 0,  
  46.                                 firstUsers.length);  
  47.                         newFirst[firstUsers.length] = user;  
  48.                         firstUsers = newFirst;  
  49.                     } else {  
  50.                         int[] newUpdate = new int[updateUsers.length+1];  
  51.                         System.arraycopy(updateUsers, 0, newUpdate, 0,  
  52.                                 updateUsers.length);  
  53.                         newUpdate[updateUsers.length] = user;  
  54.                         updateUsers = newUpdate;  
  55.                     }  
  56.                 }  
  57.             }  
  58.             sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,  
  59.                     packageName, extras, null, null, firstUsers);  
  60.             final boolean update = res.removedInfo.removedPackage != null;  
  61.             if (update) {  
  62.                 extras.putBoolean(Intent.EXTRA_REPLACING, true);  
  63.             }  
  64.             sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,  
  65.                     packageName, extras, null, null, updateUsers);  
  66.             if (update) {  
  67.                 sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,  
  68.                         packageName, extras, null, null, updateUsers);  
  69.                 sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,  
  70.                         null, null, packageName, null, updateUsers);  
  71.   
  72.                 // treat asec-hosted packages like removable media on upgrade  
  73.                 if (res.pkg.isForwardLocked() || isExternal(res.pkg)) {  
  74.                     if (DEBUG_INSTALL) {  
  75.                         Slog.i(TAG, "upgrading pkg " + res.pkg  
  76.                                 + " is ASEC-hosted -> AVAILABLE");  
  77.                     }  
  78.                     int[] uidArray = new int[] { res.pkg.applicationInfo.uid };  
  79.                     ArrayList<String> pkgList = new ArrayList<String>(1);  
  80.                     pkgList.add(packageName);  
  81.                     sendResourcesChangedBroadcast(truetrue,  
  82.                             pkgList,uidArray, null);  
  83.                 }  
  84.             }  
  85.             if (res.removedInfo.args != null) {  
  86.                 // Remove the replaced package's older resources safely now  
  87.                 deleteOld = true;  
  88.             }  
  89.   
  90.             // If this app is a browser and it's newly-installed for some  
  91.             // users, clear any default-browser state in those users  
  92.             if (firstUsers.length > 0) {  
  93.                 // the app's nature doesn't depend on the user, so we can just  
  94.                 // check its browser nature in any user and generalize.  
  95.                 if (packageIsBrowser(packageName, firstUsers[0])) {  
  96.                     synchronized (mPackages) {  
  97.                         for (int userId : firstUsers) {  
  98.                             mSettings.setDefaultBrowserPackageNameLPw(null, userId);  
  99.                         }  
  100.                     }  
  101.                 }  
  102.             }  
  103.             // Log current value of "unknown sources" setting  
  104.             EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,  
  105.                 getUnknownSourcesSettings());  
  106.         }  
  107.         // Force a gc to clear up things  
  108.         Runtime.getRuntime().gc();  
  109.         // We delete after a gc for applications  on sdcard.  
  110.         if (deleteOld) {  
  111.             synchronized (mInstallLock) {  
  112.                 res.removedInfo.args.doPostDeleteLI(true);  
  113.             }  
  114.         }  
  115.         if (args.observer != null) {  
  116.             try {  
  117.                 Bundle extras = extrasForInstallResult(res);  
  118.                 args.observer.onPackageInstalled(res.name, res.returnCode,  
  119.                         res.returnMsg, extras);  
  120.             } catch (RemoteException e) {  
  121.                 Slog.i(TAG, "Observer no longer exists.");  
  122.             }  
  123.         }  
  124.     } else {  
  125.         Slog.e(TAG, "Bogus post-install token " + msg.arg1);  
  126.     }  
  127. break;  


1.7 installNewPackageLI函数

我们来看下这个函数,这个函数当mSettings和mPackages已经有这个pkg了,那就代表这个应用已经安装过了,直接调用PackageInstalledInfo的setError,这个函数会把returnCode变为INSTALL_FAILED_ALREADY_EXISTS,这样在processPendingInstall函数在调用完installPackageLI之后再调用args.doPostInstall的时候会把apk的临时文件删除。然后继续这里调用了scanPackageLI(注意这个参数是Package的那个函数,后面在扫描目录的时候再介绍),最后还是没有安装成功会在deletePackageLI删除目录,以及PKMS各个成员变量的信息。

  1. private void installNewPackageLI(PackageParser.Package pkg, int parseFlags, int scanFlags,  
  2.         UserHandle user, String installerPackageName, String volumeUuid,  
  3.         PackageInstalledInfo res) {  
  4.     // Remember this for later, in case we need to rollback this install  
  5.     String pkgName = pkg.packageName;  
  6.   
  7.     final boolean dataDirExists = Environment  
  8.             .getDataUserPackageDirectory(volumeUuid, UserHandle.USER_OWNER, pkgName).exists();  
  9.     synchronized(mPackages) {  
  10.         if (mSettings.mRenamedPackages.containsKey(pkgName)) {  
  11.             res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName  
  12.                     + " without first uninstalling package running as "  
  13.                     + mSettings.mRenamedPackages.get(pkgName));  
  14.             return;  
  15.         }  
  16.         if (mPackages.containsKey(pkgName)) {  
  17.             res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName  
  18.                     + " without first uninstalling.");  
  19.             return;  
  20.         }  
  21.     }  
  22.   
  23.     try {  
  24.         PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanFlags,  
  25.                 System.currentTimeMillis(), user);  
  26.   
  27.         updateSettingsLI(newPackage, installerPackageName, volumeUuid, null, null, res, user);  
  28.         if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {  
  29.             deletePackageLI(pkgName, UserHandle.ALL, false, null, null,  
  30.                     dataDirExists ? PackageManager.DELETE_KEEP_DATA : 0,  
  31.                             res.removedInfo, true);  
  32.         }  
  33.   
  34.     } catch (PackageManagerException e) {  
  35.         res.setError("Package couldn't be installed in " + pkg.codePath, e);  
  36.     }  
  37. }  


二、扫描目录安装应用

我们知道在PKMS启动的时候会扫描system/app data/app等目录,如果其中有apk文件会完成apk安装

我们先来看扫描目录的函数scanDirLI,大致就是遍历各个文件调用scanPackageLI函数,但是不会扫描子目录。

  1. private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {  
  2.     final File[] files = dir.listFiles();  
  3.     if (ArrayUtils.isEmpty(files)) {  
  4.         Log.d(TAG, "No files in app dir " + dir);  
  5.         return;  
  6.     }  
  7.   
  8.     if (DEBUG_PACKAGE_SCANNING) {  
  9.         Log.d(TAG, "Scanning app dir " + dir + " scanFlags=" + scanFlags  
  10.                 + " flags=0x" + Integer.toHexString(parseFlags));  
  11.     }  
  12.   
  13.     for (File file : files) {  
  14.         final boolean isPackage = (isApkFile(file) || file.isDirectory())  
  15.                 && !PackageInstallerService.isStageName(file.getName());  
  16.         if (!isPackage) {  
  17.             // Ignore entries which are not packages  
  18.             continue;  
  19.         }  
  20.         try {  
  21.             scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,  
  22.                     scanFlags, currentTime, null);  
  23.         } catch (PackageManagerException e) {  
  24.             Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());  
  25.   
  26.             // Delete invalid userdata apps  
  27.             if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&  
  28.                     e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {  
  29.                 logCriticalInfo(Log.WARN, "Deleting invalid package at " + file);  
  30.                 if (file.isDirectory()) {  
  31.                     mInstaller.rmPackageDir(file.getAbsolutePath());  
  32.                 } else {  
  33.                     file.delete();  
  34.                 }  
  35.             }  
  36.         }  
  37.     }  
  38. }  


2.1 scanPackageLI函数

scanPackageLI函数,先是解析apk文件,然后处理升级包,扫描文件签名、应用包冲突、应用代码路径资源路径,设置应用相关路劲等,最后调用了scanPackageLI(注意这个参数是Package)继续。

  1. private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,  
  2.          long currentTime, UserHandle user) throws PackageManagerException {  
  3.      if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);  
  4.      parseFlags |= mDefParseFlags;  
  5.      PackageParser pp = new PackageParser();  
  6.      pp.setSeparateProcesses(mSeparateProcesses);  
  7.      pp.setOnlyCoreApps(mOnlyCore);  
  8.      pp.setDisplayMetrics(mMetrics);  
  9.   
  10.      if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) {  
  11.          parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;  
  12.      }  
  13.   
  14.      final PackageParser.Package pkg;  
  15.      try {  
  16.          pkg = pp.parsePackage(scanFile, parseFlags);//解析  
  17.      } catch (PackageParserException e) {  
  18.          throw PackageManagerException.from(e);  
  19.      }  
  20.   
  21.      ......  
  22.   
  23.      // Set application objects path explicitly.  
  24.      pkg.applicationInfo.volumeUuid = pkg.volumeUuid;  
  25.      pkg.applicationInfo.setCodePath(pkg.codePath);//设置各种路径  
  26.      pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);  
  27.      pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);  
  28.      pkg.applicationInfo.setResourcePath(resourcePath);  
  29.      pkg.applicationInfo.setBaseResourcePath(baseResourcePath);  
  30.      pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);  
  31.   
  32.      PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags  
  33.              | SCAN_UPDATE_SIGNATURE, currentTime, user);  
  34.   
  35.      ......  
  36.   
  37.      return scannedPkg;  
  38.  }  

scanPackageLI就是调用scanPackageDirtyLI函数,出错了再删除相关目录。

  1. private PackageParser.Package scanPackageLI(PackageParser.Package pkg, int parseFlags,  
  2.         int scanFlags, long currentTime, UserHandle user) throws PackageManagerException {  
  3.     boolean success = false;  
  4.     try {  
  5.         final PackageParser.Package res = scanPackageDirtyLI(pkg, parseFlags, scanFlags,  
  6.                 currentTime, user);  
  7.         success = true;  
  8.         return res;  
  9.     } finally {  
  10.         if (!success && (scanFlags & SCAN_DELETE_DATA_ON_FAILURES) != 0) {  
  11.             removeDataDirsLI(pkg.volumeUuid, pkg.packageName);  
  12.         }  
  13.     }  
  14. }  


2.2 scanPackageDirtyLI函数

这个函数之前博客分析过了,这里我们说下几点:

1. 安装应用中的动态库,如果应用自带了本地动态库,安装在data/data/<pacage-name>/lib下。如果是系统应用,展开的动态库放在/system/lib下。

我们来看下墨迹天气的lib目录:

  1. ********:/data/data/com.moji.mjweather/lib # ls  
  2. libedittextutil.so  
  3. libgetuiext.so  
  4. libskinEncrypt.so  
  5. libuninstall.so  
  6. libusedes.so  

2. 重新优化dex,会调用performDexOptLI函数

3. 最后会把应用的Activity、Service、Provider、Receiver、Permission、PermissionGroup信息都提取出来放在如下变量中。

  1. final ActivityIntentResolver mActivities =  
  2.         new ActivityIntentResolver();  
  3.   
  4. final ActivityIntentResolver mReceivers =  
  5.         new ActivityIntentResolver();  
  6.   
  7. final ServiceIntentResolver mServices = new ServiceIntentResolver();  
  8.   
  9. final ProviderIntentResolver mProviders = new ProviderIntentResolver();  

PKMS的performDexOpt函数,最终会调用PackageDexOptimizer的performDexOpt函数,代码如下,看是否需要持锁。然后调用performDexOptLI函数。

  1. int performDexOpt(PackageParser.Package pkg, String[] instructionSets,  
  2.         boolean forceDex, boolean defer, boolean inclDependencies, boolean bootComplete) {  
  3.     ArraySet<String> done;  
  4.     if (inclDependencies && (pkg.usesLibraries != null || pkg.usesOptionalLibraries != null)) {  
  5.         done = new ArraySet<String>();  
  6.         done.add(pkg.packageName);  
  7.     } else {  
  8.         done = null;  
  9.     }  
  10.     synchronized (mPackageManagerService.mInstallLock) {  
  11.         final boolean useLock = mSystemReady;  
  12.         if (useLock) {  
  13.             mDexoptWakeLock.setWorkSource(new WorkSource(pkg.applicationInfo.uid));  
  14.             mDexoptWakeLock.acquire();  
  15.         }  
  16.         try {  
  17.             return performDexOptLI(pkg, instructionSets, forceDex, defer, bootComplete, done);  
  18.         } finally {  
  19.             if (useLock) {  
  20.                 mDexoptWakeLock.release();  
  21.             }  
  22.         }  
  23.     }  
  24. }  

performDexOptLI函数,先看有没有lib需要dex。最后调用了PKMS的mInstaller.dexopt函数来优化。最后就到Installd中执行。

  1. private int performDexOptLI(PackageParser.Package pkg, String[] targetInstructionSets,  
  2.         boolean forceDex, boolean defer, boolean bootComplete, ArraySet<String> done) {  
  3.     final String[] instructionSets = targetInstructionSets != null ?  
  4.             targetInstructionSets : getAppDexInstructionSets(pkg.applicationInfo);  
  5.   
  6.     if (done != null) {  
  7.         done.add(pkg.packageName);  
  8.         if (pkg.usesLibraries != null) {  
  9.             performDexOptLibsLI(pkg.usesLibraries, instructionSets, forceDex, defer,  
  10.                     bootComplete, done);  
  11.         }  
  12.         if (pkg.usesOptionalLibraries != null) {  
  13.             performDexOptLibsLI(pkg.usesOptionalLibraries, instructionSets, forceDex, defer,  
  14.                     bootComplete, done);  
  15.         }  
  16.     }  
  17.   
  18.     if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_HAS_CODE) == 0) {  
  19.         return DEX_OPT_SKIPPED;  
  20.     }  
  21.   
  22.     final boolean vmSafeMode = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_VM_SAFE_MODE) != 0;  
  23.     final boolean debuggable = (pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0;  
  24.   
  25.     final List<String> paths = pkg.getAllCodePathsExcludingResourceOnly();  
  26.     boolean performedDexOpt = false;  
  27.     final String[] dexCodeInstructionSets = getDexCodeInstructionSets(instructionSets);  
  28.     for (String dexCodeInstructionSet : dexCodeInstructionSets) {  
  29.         if (!forceDex && pkg.mDexOptPerformed.contains(dexCodeInstructionSet)) {  
  30.             continue;  
  31.         }  
  32.   
  33.         for (String path : paths) {  
  34.             final int dexoptNeeded;  
  35.             if (forceDex) {  
  36.                 dexoptNeeded = DexFile.DEX2OAT_NEEDED;  
  37.             } else {  
  38.                 try {  
  39.                     dexoptNeeded = DexFile.getDexOptNeeded(path, pkg.packageName,  
  40.                             dexCodeInstructionSet, defer);  
  41.                 } catch (IOException ioe) {  
  42.                     Slog.w(TAG, "IOException reading apk: " + path, ioe);  
  43.                     return DEX_OPT_FAILED;  
  44.                 }  
  45.             }  
  46.   
  47.             if (!forceDex && defer && dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {  
  48.                 // We're deciding to defer a needed dexopt. Don't bother dexopting for other  
  49.                 // paths and instruction sets. We'll deal with them all together when we process  
  50.                 // our list of deferred dexopts.  
  51.                 addPackageForDeferredDexopt(pkg);  
  52.                 return DEX_OPT_DEFERRED;  
  53.             }  
  54.   
  55.             if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {  
  56.                 final String dexoptType;  
  57.                 String oatDir = null;  
  58.                 if (dexoptNeeded == DexFile.DEX2OAT_NEEDED) {  
  59.                     dexoptType = "dex2oat";  
  60.                     try {  
  61.                         oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet);  
  62.                     } catch (IOException ioe) {  
  63.                         Slog.w(TAG, "Unable to create oatDir for package: " + pkg.packageName);  
  64.                         return DEX_OPT_FAILED;  
  65.                     }  
  66.                 } else if (dexoptNeeded == DexFile.PATCHOAT_NEEDED) {  
  67.                     dexoptType = "patchoat";  
  68.                 } else if (dexoptNeeded == DexFile.SELF_PATCHOAT_NEEDED) {  
  69.                     dexoptType = "self patchoat";  
  70.                 } else {  
  71.                     throw new IllegalStateException("Invalid dexopt needed: " + dexoptNeeded);  
  72.                 }  
  73.                   
  74.                 final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);  
  75.                 final int ret = mPackageManagerService.mInstaller.dexopt(path, sharedGid,  
  76.                         !pkg.isForwardLocked(), pkg.packageName, dexCodeInstructionSet,  
  77.                         dexoptNeeded, vmSafeMode, debuggable, oatDir, bootComplete);  
  78.   
  79.                 if (ret == 0) {  
  80.                     performedDexOpt = true;  
  81.                 }  
  82.             }  
  83.         }  
  84.         ......  

这里我们需要注意下oatDir这个参数,是通过下面函数获取的。

  1. oatDir = createOatDirIfSupported(pkg, dexCodeInstructionSet);  

我们来看createOatDirIfSupported,如果codePath是一个目录就返回一个oatDir,否则就是空。

  1. private String createOatDirIfSupported(PackageParser.Package pkg, String dexInstructionSet)  
  2.         throws IOException {  
  3.     if (!pkg.canHaveOatDir()) {  
  4.         return null;  
  5.     }  
  6.     File codePath = new File(pkg.codePath);  
  7.     if (codePath.isDirectory()) {  
  8.         File oatDir = getOatDir(codePath);  
  9.         mPackageManagerService.mInstaller.createOatDir(oatDir.getAbsolutePath(),  
  10.                 dexInstructionSet);  
  11.         return oatDir.getAbsolutePath();  
  12.     }  
  13.     return null;  
  14. }  

传到Installd中,是空的话,默认会在data/dalvik-cache下面,我们来看下,这个是arm目录,如果是64为是在arm64目录下:

  1. *******:/data/dalvik-cache/arm # ls  
  2. data@app@IflytekInput.apk@classes.dex  
  3. data@app@NotePadPlus.apk@classes.dex  
  4. system@app@Bluetooth@Bluetooth.apk@classes.dex  
  5. system@app@Galaxy4@Galaxy4.apk@classes.dex  
  6. system@app@Gallery2@Gallery2.apk@classes.dex  
  7. system@app@HoloSpiralWallpaper@HoloSpiralWallpaper.apk@classes.dex  
  8. system@app@NoiseField@NoiseField.apk@classes.dex  
  9. system@app@PhaseBeam@PhaseBeam.apk@classes.dex  
  10. system@app@webview@webview.apk@classes.dex  
  11. system@framework@am.jar@classes.dex  
  12. system@framework@android.test.runner.jar@classes.dex  
  13. system@framework@appops.jar@classes.dex  
  14. system@framework@appwidget.jar@classes.dex  
  15. system@framework@bmgr.jar@classes.dex  
  16. system@framework@boot.art  
  17. system@framework@boot.oat  
  18. system@framework@bu.jar@classes.dex  
  19. system@framework@com.android.future.usb.accessory.jar@classes.dex  
  20. system@framework@com.android.location.provider.jar@classes.dex  
  21. system@framework@com.android.media.remotedisplay.jar@classes.dex  
  22. system@framework@com.android.mediadrm.signer.jar@classes.dex  
  23. system@framework@com.android.nfc_extras.jar@classes.dex  
  24. system@framework@com.broadcom.bt.jar@classes.dex  
  25. system@framework@com.broadcom.nfc.jar@classes.dex  
  26. system@framework@com.vzw.nfc.jar@classes.dex  
  27. system@framework@content.jar@classes.dex  


普通的应用就在其oat/arm目录下

  1. ******:/data/app/com.moji.mjweather-1/oat/arm # ls  
  2. base.odex  


原文地址

http://blog.csdn.net/kc58236582/article/details/53022791


小结:

序列图


无论是开机扫描各个目录的apk文件,还是安装新的apk最后都会调用scanPackageDirtyLI方法。

我们来简单回顾下,开机扫描各个目录的时候是调用scanDirLI函数,然后调用scanPackageLI函数这个函数第一个参数是File,这个函数会新建一个PackageParser对象,来解析文件。最后也会调用另一个scanPackageLI方法这个参数是Packageparser.Package类。而在这个scanPackageLI方法中最后调用了scanPackageDirtyLI方法。

在安装一个新应用的时候先copy apk文件到指定目录,后面会调用installPackageLI,这个函数也会创建一个PackageParser对象来解析apk文件,后面会调用installNewPackageLI继续处理,最后会发送广播通知其他应用,比如Launcher增加图标等。在installNewPackageLI函数中也会调用scanPackageLI函数,是参数是Packageparser.Package类。而最后就会调用scanPackageDirtyLI方法。

所以其实无论开机扫描还是安装应用其实流程差不多。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值