Android5.1--APK包的安装、卸载和优化(PackageManagerService)(二)

在Android中,通过发送Intent,就可以启动应用的安装过程,如下所示:

[java] 
view plain
copy

  1. Uri uri = Uri.fromFile(new File(fileName));  
  2. Intent intent = new Intent(Intent.ACTION_VIEW);  
  3. intent.setDataAndType(Uri, “application/vnd.android.package-archive”);  
  4. startActivity(intent);  


在Android的系统应用PackageInstaller中有一个PackageInstallerActivity会响应这个Intent。在这个Activity中,有两个重要的成员变量mPm,是ApplicationPackageManager的实例对象,也是PackageManagerService在应用中的代理对象。创建代码如下:

[java] 
view plain
copy

  1. mPm = getPackageManager();  

一、管理“安装会话”—-PackageInstallerService

PackageInstallerService是Android5.0新加入的服务,主要用于管理“安装会话(Installer Session)”。在Android5.0中,可以通过PackageInstallerService来分配一个SessionId,这个系统唯一的ID代表一次安装过程,如果一个应用的安装必须分成几个阶段来完成,即使设备重启了,也可以通过这个ID来继续安装过程。

PackageInstallerService中提供了接口createSession()创建一个Session:

[java] 
view plain
copy

  1. public int createSession(SessionParams params, String installerPackageName, int userId) {  
  2.     try {  
  3.         return createSessionInternal(params, installerPackageName, userId);  
  4.     } catch (IOException e) {  
  5.         throw ExceptionUtils.wrap(e);  
  6.     }  
  7. }  


createSession方法将返回一个系统唯一值作为SessionID。如果希望再次使用这个Session,可以通过接口openSession打开它,代码如下:

[java] 
view plain
copy

  1. public IPackageInstallerSession openSession(int sessionId) {  
  2.     try {  
  3.         return openSessionInternal(sessionId);  
  4.     } catch (IOException e) {  
  5.         throw ExceptionUtils.wrap(e);  
  6.     }  
  7. }  
  8.   
  9. private IPackageInstallerSession openSessionInternal(int sessionId) throws IOException {  
  10.     synchronized (mSessions) {  
  11.         final PackageInstallerSession session = mSessions.get(sessionId);  
  12.         if (session == null || !isCallingUidOwner(session)) {  
  13.             throw new SecurityException(“Caller has no access to session “ + sessionId);  
  14.         }  
  15.         session.open();  
  16.         return session;  
  17.     }  
  18. }  


openSession方法返回一个IPackageInstallerSession对象,它是Binder服务PackageInstallerSession的IBinder对象。在PackageInstallerService中mSessions数组保存了所有PackageInstallerSession对象,代码如下:

[java] 
view plain
copy

  1. private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>();  


当系统启动时,PackageManagerService初始化时会创建PackageInstallerService服务,在这个服务的初始化函数中,会读取/data/system目录下install_sessions.xml文件,这个文件中保存了系统中未完成的Install Session。PackageInstallerService会根据文件的内容创建PackageInstallerSession对象并插入到mSessions中。

PacakgeInstallerSession中保存了应用安装相关的数据,如,安装包的路径、安装的进度、中间数据保存的目录等。

二、应用安装第一阶段:复制文件

应用中可以调用PackageManager的installPackage()方法来开始安装过程,这个方法会调用PackageManagerService的installPackage()方法或installPackageAsUser()接口来执行安装过程。整个过程比较复杂,我们先看看安装序列图,如下:

应用安装的过程大概分成两个阶段,第一阶段把需要安装的应用复制到/data/app目录下,第二阶段是对apk文件进行扫描优化,然后装载到内存中。

PackageManagerService的installPackage方法用来给当前用户安装应用,而installPackageAsUser()方法给指定的用户安装应用。installPackage()方法只是使用当前用户的uid来调用installPackageAsUser()方法。代码如下:

[java] 
view plain
copy

  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, nullnull);  
  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为0或2000,设置installFlags  
  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) {//如果installFlags带有标记INSTALL_ALL_USERS,则给所有用户安装  
  33.         user = UserHandle.ALL;  
  34.     } else {  
  35.         user = new UserHandle(userId);  
  36.     }  
  37.   
  38.     verificationParams.setInstallerUid(callingUid);  
  39.   
  40.     final File originFile = new File(originPath);  
  41.     final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile);  
  42.     //保存参数到InstallParams对象并发送消息  
  43.     final Message msg = mHandler.obtainMessage(INIT_COPY);  
  44.     msg.obj = new InstallParams(origin, observer, installFlags,  
  45.             installerPackageName, verificationParams, user, packageAbiOverride);  
  46.     mHandler.sendMessage(msg);  
  47. }  

installPackageAsUser()方法首先检查调用进程是否有安装应用的权限,再检查调用进程的所属用户是否有权限安装应用,最后检查指定的用户是否被限制安装应用。如果参数installFlags带有标记INSTALL_ALL_USERS,则该应用将给系统中所有用户安装,否则只给指定的用户安装。

installPackageAsUser()方法,把调用的参数保存在InstallParams对象中,然后发送INIT_COPY消息。InstallParams是安装过程中主要的数据结构,从HandleParams类派生,用来记录安装应用的参数。从HandleParams派生的还有MoveParams和MeasureParams,MoveParams用于将应用移动到SD卡,MeasureParams用于方法getPackageSizeInfo中。InstallParams成员变量mArgs,是FileInstallArgs类型,用来执行apk文件的复制。

我们看下INIT_COPY消息的处理,在doHandleMessage()方法中,如下:

[java] 
view plain
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()) {//绑定DefaultContainerService服务  
  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<span style=”font-size: 11.8181819915771px; font-family: Arial, Helvetica, sans-serif;”>连接成功,把安装信息保存到mPedingInstalls中</span>  
  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) {//如果mPendingInstalls只有一项,立刻发MCS_BOUND消息  
  25.             mHandler.sendEmptyMessage(MCS_BOUND);  
  26.         }  
  27.     }  
  28.     break;  
  29. }  

在INIT_COPY消息的处理中将绑定DefaultContainerService,因为这是一个异步的过程,要等待绑定的结果通过onServiceConnected()返回,所以,这里将安装的参数信息放到mPendingInstalls列表中。如果这个service以前就绑定好了,现在不在需要再绑定,安装信息也会先放到mPendingInstalls中。如果有多个安装请求同时到达,通过mPendingInstalls列表可以对它们进行排队。如果列表中只有一项,说明没有更多的安装请求,此时会立即发送MCS_BOUND消息,进入下一步的处理。而onServiceConnected()方法的处理同样是发送MCS_BOUND消息,如下:

[java] 
view plain
copy

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

我们看下MCS_BOUND消息是怎么处理的,如下:

[java] 
view plain
copy

  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) {//如果DefaultContainerService没有连接成功  
  7.         // Something seriously wrong. Bail out  
  8.         Slog.e(TAG, “Cannot bind to media container service”);  
  9.         for (HandlerParams params : mPendingInstalls) {  
  10.             // Indicate service bind error  
  11.             params.serviceError();//通过参数中带的回调接口通知调用者出错了  
  12.         }  
  13.         mPendingInstalls.clear();  
  14.     } else if (mPendingInstalls.size() > 0) {  
  15.         HandlerParams params = mPendingInstalls.get(0);  
  16.         if (params != null) {  
  17.             if (params.startCopy()) {//执行安装  
  18.                 // We are done…  look for more work or to  
  19.                 // go idle.  
  20.                 if (DEBUG_SD_INSTALL) Log.i(TAG,  
  21.                         “Checking for more work or unbind…”);  
  22.                 // Delete pending install  
  23.                 if (mPendingInstalls.size() > 0) {  
  24.                     mPendingInstalls.remove(0);//工作完成,删除第一项  
  25.                 }  
  26.                 if (mPendingInstalls.size() == 0) {  
  27.                     if (mBound) {//如果没有安装信息了,发送延时10秒的MCS_UNBIND消息  
  28.                         if (DEBUG_SD_INSTALL) Log.i(TAG,  
  29.                                 “Posting delayed MCS_UNBIND”);  
  30.                         removeMessages(MCS_UNBIND);  
  31.                         Message ubmsg = obtainMessage(MCS_UNBIND);  
  32.                         // Unbind after a little delay, to avoid  
  33.                         // continual thrashing.  
  34.                         sendMessageDelayed(ubmsg, 10000);  
  35.                     }  
  36.                 } else {  
  37.                     // There are more pending requests in queue.  
  38.                     // Just post MCS_BOUND message to trigger processing  
  39.                     // of next pending install.mPendingInstalls还有安装信息,发送MCS_BOUND消息  
  40.                     if (DEBUG_SD_INSTALL) Log.i(TAG,  
  41.                             “Posting MCS_BOUND for next work”);  
  42.                     mHandler.sendEmptyMessage(MCS_BOUND);  
  43.                 }  
  44.             }  
  45.         }  
  46.     } else {  
  47.         // Should never happen ideally.  
  48.         Slog.w(TAG, “Empty queue”);  
  49.     }  
  50.     break;  
  51. }  

MCS_BOUND消息的处理过程就是调用InstallParams类的startCopy()方法来执行安装。只要mPendingInstalls中还有安装信息,就会重复发送MCS_BOUND消息,知道所有的应用都安装完毕,然后发送一个延时10秒的MCS_UNBIND消息。

MCS_UNBIND消息的处理如下:

[java] 
view plain
copy

  1. case MCS_UNBIND: {  
  2.     // If there is no actual work left, then time to unbind.  
  3.     if (DEBUG_INSTALL) Slog.i(TAG, “mcs_unbind”);  
  4.   
  5.     if (mPendingInstalls.size() == 0 && mPendingVerification.size() == 0) {  
  6.         if (mBound) {  
  7.             if (DEBUG_INSTALL) Slog.i(TAG, “calling disconnectService()”);  
  8.   
  9.             disconnectService();//断开与DefaultContainerService的连接  
  10.         }  
  11.     } else if (mPendingInstalls.size() > 0) {  
  12.         // There are more pending requests in queue.  
  13.         // Just post MCS_BOUND message to trigger processing  
  14.         // of next pending install.  
  15.         mHandler.sendEmptyMessage(MCS_BOUND);  
  16.     }  
  17.   
  18.     break;  
  19. }  

处理MCS_UNBIND消息时,如果发现mPendingInstalls列表中又有数据了,则发送MCS_BOUND消息继续安装;否则断开和DefaultContainerService的连接,安装过程就此结束。

继续分析下start_copy()方法,InstallParams类继承HandlerParams抽象类,InstallParams类没有重写startCopy方法,因此调用HandlerParams的startCopy方法,如下:

[java] 
view plain
copy

  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次,退出;MAX_RETRIES=4  
  7.             Slog.w(TAG, “Failed to invoke remote methods on default container service. Giving up”);  
  8.             mHandler.sendEmptyMessage(MCS_GIVE_UP);//发送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);//安装出错,发送MCS_RECONNECT消息,重新连接  
  18.         res = false;  
  19.     }  
  20.     handleReturnCode();  
  21.     return res;  
  22. }  

startCopy()方法通过调用handleStartCopy方法来完成安装过程。但是考虑到安装过程的不确定性,startCopy()主要的工作是进行错误处理,如果捕获到handleStartCopy()方法抛出的异常,startCopy方法将发送消息MCS_RECONNECT。

[java] 
view plain
copy

  1. case MCS_RECONNECT: {  
  2.     if (DEBUG_INSTALL) Slog.i(TAG, “mcs_reconnect”);  
  3.     if (mPendingInstalls.size() > 0) {  
  4.         if (mBound) {  
  5.             disconnectService();  
  6.         }  
  7.         if (!connectToService()) {  
  8.             Slog.e(TAG, “Failed to bind to media container service”);  
  9.             for (HandlerParams params : mPendingInstalls) {  
  10.                 // Indicate service bind error  
  11.                 params.serviceError();  
  12.             }  
  13.             mPendingInstalls.clear();  
  14.         }  
  15.     }  
  16.     break;  
  17. }  

在MCS_RECONNECT的处理中,会重新绑定DefaultContainerService,如果绑定成功,安装过程就会重新开始,startCopy方法会被再次调用。重试的次数记录在mRetries变量中,如果超过4次,安装失败。如果安装成功,调用handleReturnCode方法继续处理。先分析下handleStartCopy方法,如下:

[java] 
view plain
copy

  1. public void handleStartCopy() throws RemoteException {  
  2.     int ret = PackageManager.INSTALL_SUCCEEDED;  
  3.   
  4.     // If we’re already staged, we’ve firmly committed to an install location  
  5.     if (origin.staged) {  
  6.         if (origin.file != null) {  
  7.             installFlags |= PackageManager.INSTALL_INTERNAL;  
  8.             installFlags &= ~PackageManager.INSTALL_EXTERNAL;  
  9.         } else if (origin.cid != null) {  
  10.             installFlags |= PackageManager.INSTALL_EXTERNAL;  
  11.             installFlags &= ~PackageManager.INSTALL_INTERNAL;  
  12.         } else {  
  13.             throw new IllegalStateException(“Invalid stage location”);  
  14.         }  
  15.     }  
  16.   
  17.     final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;  
  18.     final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;  
  19.   
  20.     PackageInfoLite pkgLite = null;  
  21.   
  22.     if (onInt && onSd) {//如果既有安装在内部的标志,又有安装在SD卡上的标志,设置错误后返回  
  23.         // Check if both bits are set.  
  24.         Slog.w(TAG, “Conflicting flags specified for installing on both internal and external”);  
  25.         ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;  
  26.     } else {  
  27.         pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,  
  28.                 packageAbiOverride);//获取安装包包含有应用信息的PackageInfoLite对象  
  29.   
  30.         /* 
  31.          * If we have too little free space, try to free cache 
  32.          * before giving up.如果安装的位置空间不够,释放cache空间 
  33.          */  
  34.         if (!origin.staged && pkgLite.recommendedInstallLocation  
  35.                 == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {  
  36.             // TODO: focus freeing disk space on the target device  
  37.             final StorageManager storage = StorageManager.from(mContext);  
  38.             long lowThreshold = storage.getStorageLowBytes(  
  39.                     Environment.getDataDirectory());  
  40.   
  41.             final long sizeBytes = mContainerService.calculateInstalledSize(  
  42.                     origin.resolvedPath, isForwardLocked(), packageAbiOverride);  
  43.   
  44.             if (mInstaller.freeCache(sizeBytes + lowThreshold) >= 0) {  
  45.                 pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,  
  46.                         installFlags, packageAbiOverride);  
  47.             }  
  48.   
  49.             /* 
  50.              * The cache free must have deleted the file we 
  51.              * downloaded to install. 
  52.              * 
  53.              * TODO: fix the “freeCache” call to not delete 
  54.              *       the file we care about. 
  55.              */  
  56.             if (pkgLite.recommendedInstallLocation  
  57.                     == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {  
  58.                 pkgLite.recommendedInstallLocation  
  59.                     = PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;  
  60.             }  
  61.         }  
  62.     }  
  63.     ……  
  64.   
  65.     final InstallArgs args = createInstallArgs(this);  
  66.     mArgs = args;  
  67.   
  68.     if (ret == PackageManager.INSTALL_SUCCEEDED) {  
  69.          /* 
  70.          * ADB installs appear as UserHandle.USER_ALL, and can only be performed by 
  71.          * UserHandle.USER_OWNER, so use the package verifier for UserHandle.USER_OWNER. 
  72.          */  
  73.         int userIdentifier = getUser().getIdentifier();  
  74.         if (userIdentifier == UserHandle.USER_ALL  
  75.                 && ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0)) {  
  76.             userIdentifier = UserHandle.USER_OWNER;  
  77.         }  
  78.   
  79.         /* 
  80.          * Determine if we have any installed package verifiers. If we 
  81.          * do, then we’ll defer to them to verify the packages. 
  82.          */  
  83.         final int requiredUid = mRequiredVerifierPackage == null ? –1  
  84.                 : getPackageUid(mRequiredVerifierPackage, userIdentifier);  
  85.         if (!origin.existing && requiredUid != –1  
  86.                 && isVerificationEnabled(userIdentifier, installFlags)) {<span style=“font-family: Arial, Helvetica, sans-serif; font-size: 12px;”>                 </span>  

[java] 
view plain
copy

  1. <pre name=“code” class=“java” style=“font-size: 11.8181819915771px;”>                    //执行应用的校验,校验的方法是通过向带校验功能的组件发Intent来完成。  

…… } else { /* * No package verification is enabled, so immediately start * the remote call to initiate copy using temporary file. *///如果无需校验,调用IntallArgs的copyApk方法继续处理 ret = args.copyApk(mContainerService, true); } } mRet = ret; }

handleStartCopy()方法先调用getMinimalPackageInfo()来确定安装位置是否还有足够空间,并在PackageInfoLite对象的recommendedInstallLocation变量中记录错误原因。发现安装空间不够后,handleStartCopy()方法会调用installer的freeCache()方法来释放一部分空间。

接下来handleStartCopy()方法会调用createInstallArgs()来创建InstallArgs对象,在createInstallArgs()方法中,如果应用要求安装在SD卡上,或者是一个forward lock的应用,则创建AsecInstallArgs对象,否则创建FileInstallArgs对象。这样安装流程将会分化,这里主要分析下FileInstallArgs。如下:

[java] 
view plain
copy

  1. private InstallArgs createInstallArgs(InstallParams params) {  
  2.     if (installOnSd(params.installFlags) || params.isForwardLocked()) {  
  3.         return new AsecInstallArgs(params);  
  4.     } else {  
  5.         return new FileInstallArgs(params);  
  6.     }  
  7. }  

接下来handleStartCopy()方法处理APK的校验,这个校验的过程是通过发送Intent.ACTION_PACKAGE_NEEDS_VERIFICATION给系统中所有接收该Intent的应用来完成的。如果无需校验,则直接调用InstallArgs对象的copyApk()方法,FileInstallArgs继承了InstallArgs类,这里调用的是FileInstallArgs类的copyApk()方法,如下:

[java] 
view plain
copy

  1. int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {  
  2.     if (origin.staged) {  
  3.         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 {//在/data/app/下生成临时文件  
  10.         final File tempDir = mInstallerService.allocateInternalStageDirLegacy();  
  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.     //为临时文件创建文件描述符ParcelFileDescriptor,它能通过Binder传递  
  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.     //使用服务DefaultContainerService的copyPackage()方法复制文件  
  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.     //安装应用中自带的native的动态库  
  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. }  

copyApk()同样是调用DefaultContainerService的copyPackage()将应用的文件复制到/data/app下。如果应用中还有native的动态库,也会把包在apk文件中的动态库的文件提取出来。执行完copyApk后,安装的第一阶段工作就完成了,应用安装到了/data/app目录下。

三、装载应用

第二阶段就是将应用的格式转化为oat格式,为应用创建数据目录,最后把应用的信息装载进PMS的数据结构中。

在前面的startCopy()方法中已经看到,最后它会调用handleReturnCode()方法,如下:

[java] 
view plain
copy

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

handleReturnCode()方法只是调用了processpendingInstall()方法,如下:

[java] 
view plain
copy

  1. private void processPendingInstall(final InstallArgs args, final int currentStatus) {  
  2.     // Queue up an async operation since the package installation may take a little while.  
  3.     mHandler.post(new Runnable() {  
  4.         public void run() {  
  5.             mHandler.removeCallbacks(this);  
  6.              // Result object to be returned  
  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) {//如果应用安装成功了  
  13.                 args.doPreInstall(res.returnCode);  
  14.                 synchronized (mInstallLock) {  
  15.                     installPackageLI(args, res);//装载安装的应用  
  16.                 }  
  17.                 args.doPostInstall(res.returnCode, res.uid);  
  18.             }  
  19.   
  20.             // A restore should be performed at this point if (a) the install  
  21.             // succeeded, (b) the operation is not an update, and (c) the new  
  22.             // package has not opted out of backup participation.  
  23.             final boolean update = res.removedInfo.removedPackage != null;  
  24.             final int flags = (res.pkg == null) ? 0 : res.pkg.applicationInfo.flags;  
  25.             boolean doRestore = !update  
  26.                     && ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0);  
  27.   
  28.             // Set up the post-install work request bookkeeping.  This will be used  
  29.             // and cleaned up by the post-install event handling regardless of whether  
  30.             // there’s a restore pass performed.  Token values are >= 1.  
  31.             int token;  
  32.             if (mNextInstallToken < 0) mNextInstallToken = 1;  
  33.             token = mNextInstallToken++;  
  34.   
  35.             PostInstallData data = new PostInstallData(args, res);  
  36.             mRunningInstalls.put(token, data);  
  37.             if (DEBUG_INSTALL) Log.v(TAG, “+ starting restore round-trip “ + token);  
  38.   
  39.             if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {  
  40.                 // Pass responsibility to the Backup Manager.  It will perform a  
  41.                 // restore if appropriate, then pass responsibility back to the  
  42.                 // Package Manager to run the post-install observer callbacks  
  43.                 // and broadcasts.  
  44.                 IBackupManager bm = IBackupManager.Stub.asInterface(  
  45.                         ServiceManager.getService(Context.BACKUP_SERVICE));  
  46.                 if (bm != null) {  
  47.                     if (DEBUG_INSTALL) Log.v(TAG, “token “ + token  
  48.                             + ” to BM for possible restore”);  
  49.                     try {  
  50.                         if (bm.isBackupServiceActive(UserHandle.USER_OWNER)) {  
  51.                             bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);  
  52.                         } else {  
  53.                             doRestore = false;  
  54.                         }  
  55.                     } catch (RemoteException e) {  
  56.                         // can’t happen; the backup manager is local  
  57.                     } catch (Exception e) {  
  58.                         Slog.e(TAG, “Exception trying to enqueue restore”, e);  
  59.                         doRestore = false;  
  60.                     }  
  61.                 } else {  
  62.                     Slog.e(TAG, “Backup Manager not found!”);  
  63.                     doRestore = false;  
  64.                 }  
  65.             }  
  66.   
  67.             if (!doRestore) {//发送POST_INSTALL消息  
  68.                 // No restore possible, or the Backup Manager was mysteriously not  
  69.                 // available — just fire the post-install work request directly.  
  70.                 if (DEBUG_INSTALL) Log.v(TAG, “No restore – queue post-install for “ + token);  
  71.                 Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);  
  72.                 mHandler.sendMessage(msg);  
  73.             }  
  74.         }  
  75.     });  
  76. }  

processPendingInstall()方法中post了一个消息,这样安装过程将以异步的方式继续执行。在post消息的处理中,首先调用installPackageLI()来装载应用,接下来的一大段代码都是执行备份操作,备份是通过BackupManagerService来完成的。备份完成后,通过发送POST_INSTALL消息来继续处理。先分析下installPackageLI()方法,如下:

[java] 
view plain
copy

  1.     private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {  
  2.         final int installFlags = args.installFlags;  
  3.         String installerPackageName = args.installerPackageName;  
  4.         File tmpPackageFile = new File(args.getCodePath());  
  5.         boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);  
  6.         boolean onSd = ((installFlags & PackageManager.INSTALL_EXTERNAL) != 0);  
  7.         boolean replace = false;  
  8.         final int scanFlags = SCAN_NEW_INSTALL | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE;  
  9.         // Result object to be returned  
  10.         res.returnCode = PackageManager.INSTALL_SUCCEEDED;  
  11.   
  12.         if (DEBUG_INSTALL) Slog.d(TAG, “installPackageLI: path=” + tmpPackageFile);  
  13.         if (true != mSecurityBridge.approveAppInstallRequest(  
  14.                         args.getResourcePath(),  
  15.                         Uri.fromFile(args.origin.file).toSafeString())) {  
  16.             res.returnCode = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;  
  17.             return;  
  18.         }  
  19.   
  20.         // Retrieve PackageSettings and parse package创建apk文件的解析器  
  21.         final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY  
  22.                 | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)  
  23.                 | (onSd ? PackageParser.PARSE_ON_SDCARD : 0);  
  24.         PackageParser pp = new PackageParser();  
  25.         pp.setSeparateProcesses(mSeparateProcesses);  
  26.         pp.setDisplayMetrics(mMetrics);  
  27.   
  28.         final PackageParser.Package pkg;  
  29.         try {//解析apk文件  
  30.             pkg = pp.parsePackage(tmpPackageFile, parseFlags);  
  31.         } catch (PackageParserException e) {  
  32.             res.setError(“Failed parse during installPackageLI”, e);  
  33.             return;  
  34.         }  
  35.   
  36.         // Mark that we have an install time CPU ABI override.  
  37.         pkg.cpuAbiOverride = args.abiOverride;  
  38.   
  39.         String pkgName = res.name = pkg.packageName;  
  40.         if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_TEST_ONLY) != 0) {  
  41.             if ((installFlags & PackageManager.INSTALL_ALLOW_TEST) == 0) {  
  42.                 res.setError(INSTALL_FAILED_TEST_ONLY, “installPackageLI”);  
  43.                 return;  
  44.             }  
  45.         }  
  46.   
  47.         if (android.app.AppOpsManager.isStrictEnable()  
  48.                 && ((installFlags & PackageManager.INSTALL_FROM_ADB) != 0)  
  49.                 && Secure.getInt(mContext.getContentResolver(),  
  50.                         Secure.INSTALL_NON_MARKET_APPS, 0) <= 0) {  
  51.             res.returnCode = PackageManager.INSTALL_FAILED_UNKNOWN_SOURCES;  
  52.             return;  
  53.         }  
  54.         //收集应用的签名  
  55.         try {  
  56.             pp.collectCertificates(pkg, parseFlags);  
  57.             pp.collectManifestDigest(pkg);  
  58.         } catch (PackageParserException e) {  
  59.             res.setError(“Failed collect during installPackageLI”, e);  
  60.             return;  
  61.         }  
  62.   
  63.         /* If the installer passed in a manifest digest, compare it now. */  
  64.         if (args.manifestDigest != null) {  
  65.             if (DEBUG_INSTALL) {  
  66.                 final String parsedManifest = pkg.manifestDigest == null ? “null”  
  67.                         : pkg.manifestDigest.toString();  
  68.                 Slog.d(TAG, “Comparing manifests: “ + args.manifestDigest.toString() + ” vs. “  
  69.                         + parsedManifest);  
  70.             }  
  71.   
  72.             if (!args.manifestDigest.equals(pkg.manifestDigest)) {  
  73.                 res.setError(INSTALL_FAILED_PACKAGE_CHANGED, “Manifest digest changed”);  
  74.                 return;  
  75.             }  
  76.         } else if (DEBUG_INSTALL) {  
  77.             final String parsedManifest = pkg.manifestDigest == null  
  78.                     ? “null” : pkg.manifestDigest.toString();  
  79.             Slog.d(TAG, “manifestDigest was not present, but parser got: “ + parsedManifest);  
  80.         }  
  81.   
  82.         // Get rid of all references to package scan path via parser.  
  83.         pp = null;  
  84.         String oldCodePath = null;  
  85.         boolean systemApp = false;  
  86.         synchronized (mPackages) {  
  87.             // Check if installing already existing package如果安装的升级应用,继续使用以前老的包名  
  88.             if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {  
  89.                 String oldName = mSettings.mRenamedPackages.get(pkgName);  
  90.                 if (pkg.mOriginalPackages != null  
  91.                         && pkg.mOriginalPackages.contains(oldName)  
  92.                         && mPackages.containsKey(oldName)) {  
  93.                     // This package is derived from an original package,  
  94.                     // and this device has been updating from that original  
  95.                     // name.  We must continue using the original name, so  
  96.                     // rename the new package here.  
  97.                     pkg.setPackageName(oldName);  
  98.                     pkgName = pkg.packageName;  
  99.                     replace = true;  
  100.                     if (DEBUG_INSTALL) Slog.d(TAG, “Replacing existing renamed package: oldName=”  
  101.                             + oldName + ” pkgName=” + pkgName);  
  102.                 } else if (mPackages.containsKey(pkgName)) {  
  103.                     // This package, under its official name, already exists  
  104.                     // on the device; we should replace it.  
  105.                     replace = true;  
  106.                     if (DEBUG_INSTALL) Slog.d(TAG, “Replace existing pacakge: “ + pkgName);  
  107.                 }  
  108.             }  

[java] 
view plain
copy

  1. PackageSetting ps = mSettings.mPackages.get(pkgName);  
  2.  if (ps != null) {  
  3.      if (DEBUG_INSTALL) Slog.d(TAG, “Existing package: “ + ps);  
  4.   
  5.      // Quick sanity check that we’re signed correctly if updating;  
  6.      // we’ll check this again later when scanning, but we want to  
  7.      // bail early here before tripping over redefined permissions.  
  8.      if (!ps.keySetData.isUsingUpgradeKeySets() || ps.sharedUser != null) {  
  9.          try {  
  10.              verifySignaturesLP(ps, pkg);  
  11.          } catch (PackageManagerException e) {  
  12.              res.setError(e.error, e.getMessage());  
  13.              return;  
  14.          }  
  15.      } else {  
  16.          if (!checkUpgradeKeySetLP(ps, pkg)) {  
  17.              res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, “Package “  
  18.                      + pkg.packageName + ” upgrade keys do not match the “  
  19.                      + “previously installed version”);  
  20.              return;  
  21.          }  
  22.      }  
  23. 存在同名的应用,如果存在,检查应用是否带有系统标志  
  24.      oldCodePath = mSettings.mPackages.get(pkgName).codePathString;  
  25.      if (ps.pkg != null && ps.pkg.applicationInfo != null) {  
  26.          systemApp = (ps.pkg.applicationInfo.flags &  
  27.                  ApplicationInfo.FLAG_SYSTEM) != 0;  
  28.      }  
  29.      res.origUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);  
  30.  }   

。。。。。。

installPackageLI()方法首先解析了安装的应用文件,得到解析的结果后,主要是判断新安装的应用是否和系统中已安装的应用构成升级关系,如果是,调用replacePackageLI()方法继续处理,否则调用installNewPackageLI()方法处理。

再接着看下POST_INSTALL消息是如何处理的:

[java] 
view plain
copy

  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.             sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,  
  17.                     res.pkg.applicationInfo.packageName,  
  18.                     extras, nullnull, firstUsers);//发送ACTION_PACKAGE_ADDED广播  
  19.             final boolean update = res.removedInfo.removedPackage != null;  
  20.             if (update) {  
  21.                 extras.putBoolean(Intent.EXTRA_REPLACING, true);  
  22.             }  
  23.             sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,  
  24.                     res.pkg.applicationInfo.packageName,  
  25.                     extras, nullnull, updateUsers);  
  26.             if (update) {//如果是升级包,还会发送更多的广播  
  27.                 sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,  
  28.                         res.pkg.applicationInfo.packageName,  
  29.                         extras, nullnull, updateUsers);  
  30.                 sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,  
  31.                         nullnull,  
  32.                         res.pkg.applicationInfo.packageName, null, updateUsers);  
  33.   
  34.                 // treat asec-hosted packages like removable media on upgrade  
  35.                 if (isForwardLocked(res.pkg) || isExternal(res.pkg)) {//如果是forward lock应用,或安装在SD卡上的应用,发送广播  
  36.                     if (DEBUG_INSTALL) {  
  37.                         Slog.i(TAG, “upgrading pkg “ + res.pkg  
  38.                                 + ” is ASEC-hosted -> AVAILABLE”);  
  39.                     }  
  40.                     int[] uidArray = new int[] { res.pkg.applicationInfo.uid };  
  41.                     ArrayList<String> pkgList = new ArrayList<String>(1);  
  42.                     pkgList.add(res.pkg.applicationInfo.packageName);  
  43.                     sendResourcesChangedBroadcast(truetrue,  
  44.                             pkgList,uidArray, null);  
  45.                 }  
  46.             }  
  47.             if (res.removedInfo.args != null) {  
  48.                 // Remove the replaced package’s older resources safely now  
  49.                 deleteOld = true;  
  50.             }  
  51.   
  52.             // Log current value of “unknown sources” setting  
  53.             EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,  
  54.                 getUnknownSourcesSettings());  
  55.         }  
  56.         // Force a gc to clear up things  
  57.         Runtime.getRuntime().gc();  
  58.   
  59.         // We delete after a gc for applications  on sdcard.  
  60.         if (deleteOld) {  
  61.             synchronized (mInstallLock) {  
  62.                 res.removedInfo.args.doPostDeleteLI(true);  
  63.             }  
  64.         }  
  65.         if (args.observer != null) {  
  66.             try {//调用参数中的回调接口,通知调用者安装的结果  
  67.                 Bundle extras = extrasForInstallResult(res);  
  68.                 args.observer.onPackageInstalled(res.name, res.returnCode,  
  69.                         res.returnMsg, extras);  
  70.             } catch (RemoteException e) {  
  71.                 Slog.i(TAG, “Observer no longer exists.”);  
  72.             }  
  73.         }  
  74.     } else {  
  75.         Slog.e(TAG, “Bogus post-install token “ + msg.arg1);  
  76.     }  
  77.     sLatestSecureContainerList = PackageHelper.getSecureContainerList();  
  78. }  

POST_INSTALL消息的处理主要就是发送广播,应用安装完成后要通知系统中其他的应用开始处理,例如,Launcher中需要增加应用的图标等。发完广播安装也就结束了。最后通过参数中的回调接口通知调用者安装的结果。

四、卸载应用

PMS还会监视应用目录的变化,做出相应的处理。

卸载应用的接口是deletePackage(),如下:

[java] 
view plain
copy

  1. public void deletePackage(final String packageName,  
  2.         final IPackageDeleteObserver2 observer, final int userId, final int flags) {  
  3.     mContext.enforceCallingOrSelfPermission(  
  4.             android.Manifest.permission.DELETE_PACKAGES, null);//检查执行权限  
  5.     final int uid = Binder.getCallingUid();  
  6.     if (UserHandle.getUserId(uid) != userId) {  
  7.         mContext.enforceCallingPermission(  
  8.                 android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,  
  9.                 “deletePackage for user “ + userId);  
  10.     }  
  11.     if (isUserRestricted(userId, UserManager.DISALLOW_UNINSTALL_APPS)) {  
  12.         try {//如果用户权限不够,发送错误信息  
  13.             observer.onPackageDeleted(packageName,  
  14.                     PackageManager.DELETE_FAILED_USER_RESTRICTED, null);  
  15.         } catch (RemoteException re) {  
  16.         }  
  17.         return;  
  18.     }  
  19.   
  20.     boolean uninstallBlocked = false;  
  21.     if ((flags & PackageManager.DELETE_ALL_USERS) != 0) {  
  22.         int[] users = sUserManager.getUserIds();  
  23.         for (int i = 0; i < users.length; ++i) {  
  24.             if (getBlockUninstallForUser(packageName, users[i])) {  
  25.                 uninstallBlocked = true;  
  26.                 break;  
  27.             }  
  28.         }  
  29.     } else {  
  30.         uninstallBlocked = getBlockUninstallForUser(packageName, userId);  
  31.     }  
  32.     if (uninstallBlocked) {  
  33.         try {  
  34.             observer.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_OWNER_BLOCKED,  
  35.                     null);  
  36.         } catch (RemoteException re) {  
  37.         }  
  38.         return;  
  39.     }  
  40.   
  41.     if (DEBUG_REMOVE) {  
  42.         Slog.d(TAG, “deletePackageAsUser: pkg=” + packageName + ” user=” + userId);  
  43.     }  
  44.     // Queue up an async operation since the package deletion may take a little while.  
  45.     mHandler.post(new Runnable() {//post消息,在消息处理方法中执行卸载应用的操作  
  46.         public void run() {  
  47.             mHandler.removeCallbacks(this);  
  48.             final int returnCode = deletePackageX(packageName, userId, flags);  
  49.             if (observer != null) {  
  50.                 try {  
  51.                     observer.onPackageDeleted(packageName, returnCode, null);//发送卸载应用的结果  
  52.                 } catch (RemoteException e) {  
  53.                     Log.i(TAG, “Observer no longer exists.”);  
  54.                 } //end catch  
  55.             } //end if  
  56.         } //end run  
  57.     });  
  58. }  

deletePackage()方法检查调用者的权限后,post了一个消息。在消息的处理方法中继续执行卸载操作,这样就避免了Binder调用时间过长。实际的卸载是通过调用deletePackageX()方法完成的,如下:

[java] 
view plain
copy

  1. private int deletePackageX(String packageName, int userId, int flags) {  
  2.     final PackageRemovedInfo info = new PackageRemovedInfo();  
  3.     final boolean res;  
  4.   
  5.     final UserHandle removeForUser = (flags & PackageManager.DELETE_ALL_USERS) != 0  
  6.             ? UserHandle.ALL : new UserHandle(userId);  
  7.     //检查userId代表的用户是否有权限删除这个应用  
  8.     if (isPackageDeviceAdmin(packageName, removeForUser.getIdentifier())) {  
  9.         Slog.w(TAG, “Not removing package “ + packageName + “: has active device admin”);  
  10.         return PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER;  
  11.     }  
  12.   
  13.     boolean removedForAllUsers = false;  
  14.     boolean systemUpdate = false;  
  15.   
  16.     // for the uninstall-updates case and restricted profiles, remember the per-  
  17.     // userhandle installed state  
  18.     int[] allUsers;  
  19.     boolean[] perUserInstalled;  
  20.     synchronized (mPackages) {  
  21.         PackageSetting ps = mSettings.mPackages.get(packageName);//获取应用在mSettings中的包的信息  
  22.         allUsers = sUserManager.getUserIds();  
  23.         perUserInstalled = new boolean[allUsers.length];  
  24.         for (int i = 0; i < allUsers.length; i++) {//检查并记录系统中所有用户是否都安装了这个应用  
  25.             perUserInstalled[i] = ps != null ? ps.getInstalled(allUsers[i]) : false;  
  26.         }  
  27.     }  
  28.   
  29.     synchronized (mInstallLock) {  
  30.         if (DEBUG_REMOVE) Slog.d(TAG, “deletePackageX: pkg=” + packageName + ” user=” + userId);  
  31.         res = deletePackageLI(packageName, removeForUser,  
  32.                 true, allUsers, perUserInstalled,  
  33.                 flags | REMOVE_CHATTY, info, true);//调用deletePackageLI方法来卸载应用  
  34.         systemUpdate = info.isRemovedPackageSystemUpdate;  
  35.         if (res && !systemUpdate && mPackages.get(packageName) == null) {  
  36.             removedForAllUsers = true;  
  37.         }  
  38.         if (DEBUG_REMOVE) Slog.d(TAG, “delete res: systemUpdate=” + systemUpdate  
  39.                 + ” removedForAllUsers=” + removedForAllUsers);  
  40.     }  
  41.   
  42.     if (res) {  
  43.         info.sendBroadcast(true, systemUpdate, removedForAllUsers);  
  44.   
  45.         // If the removed package was a system update, the old system package  
  46.         // was re-enabled; we need to broadcast this information  
  47.         if (systemUpdate) {//如果卸载的应用是某个系统应用的升级包,卸载它将导致低版本的系统应用重新使用;因此需要发送广播通知这种变化  
  48.             Bundle extras = new Bundle(1);  
  49.             extras.putInt(Intent.EXTRA_UID, info.removedAppId >= 0  
  50.                     ? info.removedAppId : info.uid);  
  51.             extras.putBoolean(Intent.EXTRA_REPLACING, true);  
  52.   
  53.             sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,  
  54.                     extras, nullnullnull);  
  55.             sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName,  
  56.                     extras, nullnullnull);  
  57.             sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null,  
  58.                     null, packageName, nullnull);  
  59.         }  
  60.     }  
  61.     // Force a gc here.  
  62.     Runtime.getRuntime().gc();  
  63.     // Delete the resources here after sending the broadcast to let  
  64.     // other processes clean up before deleting resources.  
  65.     if (info.args != null) {  
  66.         synchronized (mInstallLock) {  
  67.             info.args.doPostDeleteLI(true);//调用FileInstallArgs的方法做清理工作  
  68.         }  
  69.     }  
  70.   
  71.     return res ? PackageManager.DELETE_SUCCEEDED : PackageManager.DELETE_FAILED_INTERNAL_ERROR;  
  72. }  

deletePackageX()方法收集了应用在不同用户下的安装情况后,调用deletePackageLI方法继续卸载。如果删除的是某个系统应用的升级包,这里还会发出广播通知以前的应用又重新使用了。deletePackageLI方法如下:

[java] 
view plain
copy

  1. private boolean deletePackageLI(String packageName, UserHandle user,  
  2.         boolean deleteCodeAndResources, int[] allUserHandles, boolean[] perUserInstalled,  
  3.         int flags, PackageRemovedInfo outInfo,  
  4.         boolean writeSettings) {  
  5.     if (packageName == null) {  
  6.         Slog.w(TAG, “Attempt to delete null packageName.”);  
  7.         return false;  
  8.     }  
  9.     if (DEBUG_REMOVE) Slog.d(TAG, “deletePackageLI: “ + packageName + ” user “ + user);  
  10.     PackageSetting ps;  
  11.     boolean dataOnly = false;  
  12.     int removeUser = –1;  
  13.     int appId = –1;  
  14.     synchronized (mPackages) {  
  15.         ps = mSettings.mPackages.get(packageName);  
  16.         if (ps == null) {  
  17.             Slog.w(TAG, “Package named ‘” + packageName + “‘ doesn’t exist.”);  
  18.             return false;  
  19.         }  
  20.         if ((!isSystemApp(ps) || (flags&PackageManager.DELETE_SYSTEM_APP) != 0) && user != null  
  21.                 && user.getIdentifier() != UserHandle.USER_ALL) {  
  22.             // 如果调用者只要求卸载某个用户下的应用  
  23.             if (DEBUG_REMOVE) Slog.d(TAG, “Only deleting for single user”);  
  24.             ps.setUserState(user.getIdentifier(),  
  25.                     COMPONENT_ENABLED_STATE_DEFAULT,  
  26.                     false//installed  
  27.                     true,  //stopped  
  28.                     true,  //notLaunched  
  29.                     false//hidden  
  30.                     nullnullnull,  
  31.                     false // blockUninstall  
  32.                     );  
  33.             if (!isSystemApp(ps)) {  
  34.                 if (ps.isAnyInstalled(sUserManager.getUserIds())) {  
  35.                     // 如果还有其他用户在使用这个应用,删除应用在这个被删除用户下的数据  
  36.                     if (DEBUG_REMOVE) Slog.d(TAG, “Still installed by other users”);  
  37.                     removeUser = user.getIdentifier();  
  38.                     appId = ps.appId;  
  39.                     mSettings.writePackageRestrictionsLPr(removeUser);  
  40.                 } else {  
  41.                     // We need to set it back to ‘installed’ so the uninstall  
  42.                     // broadcasts will be sent correctly.没有用户使用了,全部删除应用  
  43.                     if (DEBUG_REMOVE) Slog.d(TAG, “Not installed by other users, full delete”);  
  44.                     ps.setInstalled(true, user.getIdentifier());  
  45.                 }  
  46.             } else {  
  47.                 // 系统应用,可能别的用户还在使用它;因此,这里只清除应用在需要删除的用户目录下的数据目录;  
  48.                 if (DEBUG_REMOVE) Slog.d(TAG, “Deleting system app”);  
  49.                 removeUser = user.getIdentifier();  
  50.                 appId = ps.appId;  
  51.                 mSettings.writePackageRestrictionsLPr(removeUser);  
  52.             }  
  53.         }  
  54.     }  
  55.   
  56.     。。。。。。  
  57.     if (dataOnly) {  
  58.         // Delete application data first,先删除应用的数据  
  59.         if (DEBUG_REMOVE) Slog.d(TAG, “Removing package data only”);  
  60.         removePackageDataLI(ps, nullnull, outInfo, flags, writeSettings);  
  61.         return true;  
  62.     }  
  63.   
  64.     boolean ret = false;  
  65.     if (isSystemApp(ps)) {//删除系统应用  
  66.         if (DEBUG_REMOVE) Slog.d(TAG, “Removing system package:” + ps.name);  
  67.         // When an updated system application is deleted we delete the existing resources as well and  
  68.         // fall back to existing code in system partition  
  69.         ret = deleteSystemPackageLI(ps, allUserHandles, perUserInstalled,  
  70.                 flags, outInfo, writeSettings);  
  71.     } else {//停止应用进程,删除文件  
  72.         if (DEBUG_REMOVE) Slog.d(TAG, “Removing non-system package:” + ps.name);  
  73.         // Kill application pre-emptively especially for apps on sd.  
  74.         killApplication(packageName, ps.appId, “uninstall pkg”);  
  75.         ret = deleteInstalledPackageLI(ps, deleteCodeAndResources, flags,  
  76.                 allUserHandles, perUserInstalled,  
  77.                 outInfo, writeSettings);  
  78.     }  
  79.   
  80.     return ret;  
  81. }  

deletePackageLI方法根据不同用户的安装情况来删除应用的apk文件和数据。

五、通过Intent查询组件

PMS在系统运行过程中,很重要的一项工作就是根据Intent来查询处理Intent的组件信息。处理Intent查询的接口如下:

  • queryIntentActivities():查询处理Intent的Activity列表。
  • queryIntentServices():查询处理Intent的Service列表。
  • queryIntentReceivers():查询处理Intent的Receiver列表。
  • queryIntentConentProviders():查询处理Intent的ContentProvider列表。

系统中响应某个Intent的组件可能有多个,因此,这些接口返回的是列表。查询到的组件信息都是用类ResolverInfo来表示的。Android定义了ResolveInfo类来表示所有组件。 以queryIntentActivities()为例分析查询过程。
[java] 
view plain
copy

  1. public List<ResolveInfo> queryIntentActivities(Intent intent,  
  2.         String resolvedType, int flags, int userId) {  
  3.     if (!sUserManager.exists(userId)) return Collections.emptyList();//检查调用接口的用户的权限  
  4.     enforceCrossUserPermission(Binder.getCallingUid(), userId, falsefalse“query intent activities”);  
  5.     ComponentName comp = intent.getComponent();  
  6.     if (comp == null) {  
  7.         if (intent.getSelector() != null) {  
  8.             intent = intent.getSelector();  
  9.             comp = intent.getComponent();  
  10.         }  
  11.     }  
  12.   
  13.     if (comp != null) {//如果Intent中已经指定了模块和组件名称,则只会有一个匹配  
  14.         final List<ResolveInfo> list = new ArrayList<ResolveInfo>(1);  
  15.         final ActivityInfo ai = getActivityInfo(comp, flags, userId);//通过模块信息得到ActivityInfo  
  16.         if (ai != null) {  
  17.             final ResolveInfo ri = new ResolveInfo();  
  18.             ri.activityInfo = ai;  
  19.             list.add(ri);  
  20.         }  
  21.         return list;  
  22.     }  
  23.   
  24.     // reader  
  25.     synchronized (mPackages) {  
  26.         final String pkgName = intent.getPackage();  
  27.         if (pkgName == null) {//如果intent中没有包名,则在系统所有包中查找  
  28.             List<CrossProfileIntentFilter> matchingFilters =  
  29.                     getMatchingCrossProfileIntentFilters(intent, resolvedType, userId);//匹配所有的IntertFilter  
  30.             // Check for results that need to skip the current profile.首先检查带有SKIP_CURRENT_PROFILE标志的filter  
  31.             ResolveInfo resolveInfo  = querySkipCurrentProfileIntents(matchingFilters, intent,  
  32.                     resolvedType, flags, userId);  
  33.             if (resolveInfo != null) {  
  34.                 List<ResolveInfo> result = new ArrayList<ResolveInfo>(1);  
  35.                 result.add(resolveInfo);  
  36.                 return result;  
  37.             }  
  38.             // Check for cross profile results.  
  39.             resolveInfo = queryCrossProfileIntents(  
  40.                     matchingFilters, intent, resolvedType, flags, userId);  
  41.   
  42.             // Check for results in the current profile.在当前的profile下查找  
  43.             List<ResolveInfo> result = mActivities.queryIntent(  
  44.                     intent, resolvedType, flags, userId);  
  45.             if (resolveInfo != null) {  
  46.                 result.add(resolveInfo);  
  47.                 Collections.sort(result, mResolvePrioritySorter);  
  48.             }  
  49.             return result;  
  50.         }  
  51.         final PackageParser.Package pkg = mPackages.get(pkgName);  
  52.         if (pkg != null) {//如果Intent中有包名,则在指定的包中查找  
  53.             return mActivities.queryIntentForPackage(intent, resolvedType, flags,  
  54.                     pkg.activities, userId);  
  55.         }  
  56.         return new ArrayList<ResolveInfo>();  
  57.     }  
  58. }  

queryIntentActivities()方法会根据Intent中的信息来分别处理。如果Intent中有包名和Activity的信息,在调用getActivityInfo()直接返回ActivityInfo。如果只有包名,调用queryIntentForPackage方法在指定的包中查找Activity。如果包名也没有,调用queryIntent()方法来搜索所有安装包。

    原文作者:u010083774
    原文地址: https://blog.csdn.net/u010083774/article/details/50587981
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞