Android PKMS深入分析安装应用

一、安装的Activity

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

[java]
view plain
copy

《Android PKMS深入分析安装应用》
《Android PKMS深入分析安装应用》

  1. Uri uri = Uri.fromFile(new File(filename));  
  2. Intent inent = new Intent(Intent.ACTION_VIEW);  
  3. intent.SetDataAndType(uri, application/vnd.android.package-archive);  
  4. startActivity(intent);  

在Android的系统应用PackageInstaller中有一个PackageInstallActivity会响应这个Intent。在这个Activity中有两个重要的成员变量mPm和mInstaller,分别是ApplicationPackageManger和PackageInstaller的实例对象,这两个对象也是PackageManagerService和PackageInstallerService在应用中的代理对象。

PackageInstallerAcivity中创建这两个对象的代码如下:

[java]
view plain
copy

《Android PKMS深入分析安装应用》
《Android PKMS深入分析安装应用》

  1. protected void onCreate(Bundler icicle) {  
  2.     super.onCreate(icicle);  
  3.     mPm = getPackageManager();  
  4.     mInstaller = mPm.getPackageInstaller();  
  5.     ……  
  6. }  


二、管理安装会话

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

PackageManagerInstallerService中提供的接口createSession来创建一个Session:

[java]
view plain
copy

《Android PKMS深入分析安装应用》
《Android PKMS深入分析安装应用》

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

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

[java]
view plain
copy

《Android PKMS深入分析安装应用》
《Android PKMS深入分析安装应用》

  1. @Override  
  2. public IPackageInstallerSession openSession(int sessionId) {  
  3.     try {  
  4.         return openSessionInternal(sessionId);  
  5.     } catch (IOException e) {  
  6.         throw ExceptionUtils.wrap(e);  
  7.     }  
  8. }  

openSession返回一个IPackageInstallerSession对象,它是Binder服务PackageInstallerSession的IBinder对象。每个Install Session都会在SystemServer中有一个对应的PackageInstallerSession对象。在PackageInstallerService中mSessions数组保存了所有PackageInstallerSession对象,定义如下:

[java]
view plain
copy

《Android PKMS深入分析安装应用》
《Android PKMS深入分析安装应用》

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

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

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

三、应用安装过程

应用可以调用PackageManager的installPackage方法来开始安装过程,最终会调用到PackageManagerService的installPackage或者installPackageAsUser来执行安装过程,整个安装过程比较复杂。

整个安装过程可以分成两个阶段:

1.第一阶段把需要安装的应用复制到/data/app目录下

2.第二阶段是对apk文件扫描优化,装载到内存中。

3.1 复制文件

PackageManagerService的installPackage方法只是用当前用户安装应用,最后也会调用installPackageAsUser

[java]
view plain
copy

《Android PKMS深入分析安装应用》
《Android PKMS深入分析安装应用》

  1. @Override  
  2. public void installPackage(String originPath, IPackageInstallObserver2 observer,  
  3.         int installFlags, String installerPackageName, VerificationParams verificationParams,  
  4.         String packageAbiOverride) {  
  5.     installPackageAsUser(originPath, observer, installFlags, installerPackageName, verificationParams,  
  6.             packageAbiOverride, UserHandle.getCallingUserId());  
  7. }  

下面我们就来看看installPackageAsUser方法:

[java]
view plain
copy

《Android PKMS深入分析安装应用》
《Android PKMS深入分析安装应用》

  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;  
  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.        verificationParams.setInstallerUid(callingUid);  
  39.   
  40.        final File originFile = new File(originPath);  
  41.        final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile);  
  42.   
  43.        final Message msg = mHandler.obtainMessage(INIT_COPY);  
  44.        msg.obj = new InstallParams(origin, observer, installFlags,//保存参数到InstallParamsm,发送消息  
  45.                installerPackageName, verificationParams, user, packageAbiOverride);  
  46.        mHandler.sendMessage(msg);  
  47.    }  

installPackageAsUser先检查调用进程是否有安装应用的权限,再检查调用进程所属的用户是否有权限安装应用,最后检查指定的用户是否被限制安装应用。如果参数installFlags带有INSTALL_ALL_USERS,则该应用将给系统中所有用户安装,否则只给指定用户安装。
安装应用实践比较长,因此不可能在一个函数中完成。上面函数把数据保存在installParams然后发送了INIT_COPY消息。

下面我们再来看看消息处理:

[java]
view plain
copy

《Android PKMS深入分析安装应用》
《Android PKMS深入分析安装应用》

  1. void doHandleMessage(Message msg) {  
  2.     switch (msg.what) {  
  3.         case INIT_COPY: {  
  4.             HandlerParams params = (HandlerParams) msg.obj;  
  5.             int idx = mPendingInstalls.size();  
  6.             if (DEBUG_INSTALL) Slog.i(TAG, “init_copy idx=” + idx + “: “ + params);  
  7.             // If a bind was already initiated we dont really  
  8.             // need to do anything. The pending install  
  9.             // will be processed later on.  
  10.             if (!mBound) {  
  11.                 // If this is the only one pending we might  
  12.                 // have to bind to the service again.  
  13.                 if (!connectToService()) {//绑定DefaultContainerService  
  14.                     Slog.e(TAG, “Failed to bind to media container service”);  
  15.                     params.serviceError();  
  16.                     return;  
  17.                 } else {//连接成功把安装信息保存到mPendingInstalls  
  18.                     // Once we bind to the service, the first  
  19.                     // pending request will be processed.  
  20.                     mPendingInstalls.add(idx, params);  
  21.                 }  
  22.             } else {//如果已经绑定好了  
  23.                 mPendingInstalls.add(idx, params);  
  24.                 // Already bound to the service. Just make  
  25.                 // sure we trigger off processing the first request.  
  26.                 if (idx == 0) {  
  27.                     mHandler.sendEmptyMessage(MCS_BOUND);  
  28.                 }  
  29.             }  
  30.             break;  
  31.         }  

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

[java]
view plain
copy

《Android PKMS深入分析安装应用》
《Android PKMS深入分析安装应用》

  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

《Android PKMS深入分析安装应用》
《Android PKMS深入分析安装应用》

  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.         // 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) {//如果没有安装消息了,延时发送10秒MCS_UNBIND消息  
  27.                     if (mBound) {  
  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.  
  40.                     if (DEBUG_SD_INSTALL) Log.i(TAG,  
  41.                             “Posting MCS_BOUND for next work”);  
  42.                     mHandler.sendEmptyMessage(MCS_BOUND);//还有消息继续发送MCS_BOUND消息  
  43.                 }  
  44.             }  
  45.         }  
  46.     } else {  
  47.         // Should never happen ideally.  
  48.         Slog.w(TAG, “Empty queue”);  
  49.     }  
  50.     break;  
  51. }  

如果结束了我们看看MCS_UNBIND消息的处理

[java]
view plain
copy

《Android PKMS深入分析安装应用》
《Android PKMS深入分析安装应用》

  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();//断开连接  
  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的连接,安装结束。
下面我们看执行安装的函数startCopy:

[java]
view plain
copy

《Android PKMS深入分析安装应用》
《Android PKMS深入分析安装应用》

  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. }  

handleStartCopy和copyApk代码就不分析了。

handleStartCopy函数先通过DefaultContainerService调用了getMinimallPackageInfo来确定安装位置是否有足够的空间,并在PackageInfoLite对象的recommendedIntallLocation记录错误原因。发现空间不够,会调用installer的freecache方法来释放一部分空间。

再接下来handleStartCopy有很长一段都在处理apk的校验,这个校验过程是通过发送Intent ACTION_PACKAGE_NEEDS_VERIFICATION给系统中所有接受该Intent的应用来完成。如果无需校验,直接调用InstallArgs对象的copyApk方法。

而copyApk方法同样是调用DefaultContainerService的copyPackage将应用的文件复制到/data/app下,如果还有native动态库,也会把包在apk文件中的动态库提取出来。

执行完copyApk后,应用安装到了data/app目录下了。


3.2 装载应用

接下来是第二阶段的工作,把应用的格式装换成oat格式,为应用创建数据目录。最后把应用信息装载进PackageManagerService的数据结构中。

接着上面startCopy方法最后会调用handleReturnCode方法,代码如下:

[java]
view plain
copy

《Android PKMS深入分析安装应用》
《Android PKMS深入分析安装应用》

  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. }  

我们继续看下processPendingInstall函数。

[java]
view plain
copy

《Android PKMS深入分析安装应用》
《Android PKMS深入分析安装应用》

  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.   
  21.             if (!doRestore) {  
  22.                 // 发送POST_INSTALL消息  
  23.                 Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);  
  24.                 mHandler.sendMessage(msg);  
  25.             }  
  26.         }  
  27.     });  
  28. }  

processPendingInstall方法post了一个消息,这样安装过程以异步的方式继续执行。在post消息的处理中,首先调用installPackageLI来装载应用,然后很大的代码在执行备份,备份是通过BackupManagerService来完成的。备份完成后,通过发送POST_INSTALL消息来继续处理。而这个消息的处理主要就是在发送广播,应用安装完成后要通知系统中其他的应用开始处理,比如Launcher中需要增加应用的图标等。

我们来分析下installPackageLI方法:

[java]
view plain
copy

《Android PKMS深入分析安装应用》
《Android PKMS深入分析安装应用》

  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.     // Retrieve PackageSettings and parse package  
  14.     final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY  
  15.             | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)  
  16.             | (onSd ? PackageParser.PARSE_ON_SDCARD : 0);  
  17.     PackageParser pp = new PackageParser();  
  18.     pp.setSeparateProcesses(mSeparateProcesses);  
  19.     pp.setDisplayMetrics(mMetrics);  
  20.   
  21.     final PackageParser.Package pkg;  
  22.     try {  
  23.         pkg = pp.parsePackage(tmpPackageFile, parseFlags);//解析apk文件  
  24.     } catch (PackageParserException e) {  
  25.         res.setError(“Failed parse during installPackageLI”, e);  
  26.         return;  
  27.     }  

这里先调用parsePackage解析apk文件,这个之前分析过,我们就不再分析了。

继续分析processPendingInstall函数

[java]
view plain
copy

《Android PKMS深入分析安装应用》
《Android PKMS深入分析安装应用》

  1. if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {//如果安装的升级应用,继续使用以前的老的包名  
  2.                 String oldName = mSettings.mRenamedPackages.get(pkgName);  
  3.                 if (pkg.mOriginalPackages != null  
  4.                         && pkg.mOriginalPackages.contains(oldName)  
  5.                         && mPackages.containsKey(oldName)) {  
  6.                     // This package is derived from an original package,  
  7.                     // and this device has been updating from that original  
  8.                     // name.  We must continue using the original name, so  
  9.                     // rename the new package here.  
  10.                     pkg.setPackageName(oldName);//设置老的包名  
  11.                     pkgName = pkg.packageName;  
  12.                     replace = true;  
  13.                     if (DEBUG_INSTALL) Slog.d(TAG, “Replacing existing renamed package: oldName=”  
  14.                             + oldName + ” pkgName=” + pkgName);  
  15.                 } else if (mPackages.containsKey(pkgName)) {  
  16.                     // This package, under its official name, already exists  
  17.                     // on the device; we should replace it.  
  18.                     replace = true;  
  19.                     if (DEBUG_INSTALL) Slog.d(TAG, “Replace existing pacakge: “ + pkgName);  
  20.                 }  
  21.             }  

继续分析

[java]
view plain
copy

《Android PKMS深入分析安装应用》
《Android PKMS深入分析安装应用》

  1. if (systemApp && onSd) {//不能将系统应用装载sd卡  
  2.     // Disable updates to system apps on sdcard  
  3.     res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,  
  4.             “Cannot install updates to system apps on sdcard”);  
  5.     return;  
  6. }  
  7.   
  8. if (!args.doRename(res.returnCode, pkg, oldCodePath)) {//重命名出错  
  9.     res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, “Failed rename”);  
  10.     return;  
  11. }  

继续分析

[java]
view plain
copy

《Android PKMS深入分析安装应用》
《Android PKMS深入分析安装应用》

  1. if (replace) {//如果安装的是升级包,调用replacePackageLI  
  2.     replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,  
  3.             installerPackageName, res);  
  4. else {//如果是新应用,调用installNewPackageLI继续处理  
  5.     installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,  
  6.             args.user, installerPackageName, res);  
  7. }  
  8. synchronized (mPackages) {  
  9.     final PackageSetting ps = mSettings.mPackages.get(pkgName);  
  10.     if (ps != null) {  
  11.         res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);  
  12.     }  
  13. }  

下面我们分析下installNewPackageLI函数

[java]
view plain
copy

《Android PKMS深入分析安装应用》
《Android PKMS深入分析安装应用》

  1. private void installNewPackageLI(PackageParser.Package pkg,  
  2.         int parseFlags, int scanFlags, UserHandle user,  
  3.         String installerPackageName, PackageInstalledInfo res) {  
  4.     // Remember this for later, in case we need to rollback this install  
  5.     String pkgName = pkg.packageName;  
  6.   
  7.     if (DEBUG_INSTALL) Slog.d(TAG, “installNewPackageLI: “ + pkg);  
  8.     boolean dataDirExists = getDataPathForPackage(pkg.packageName, 0).exists();  
  9.     synchronized(mPackages) {  
  10.         if (mSettings.mRenamedPackages.containsKey(pkgName)) {  
  11.             // A package with the same name is already installed, though  
  12.             // it has been renamed to an older name.  The package we  
  13.             // are trying to install should be installed as an update to  
  14.             // the existing one, but that has not been requested, so bail.  
  15.             res.setError(INSTALL_FAILED_ALREADY_EXISTS, “Attempt to re-install “ + pkgName  
  16.                     + ” without first uninstalling package running as “  
  17.                     + mSettings.mRenamedPackages.get(pkgName));  
  18.             return;  
  19.         }  
  20.         if (mPackages.containsKey(pkgName)) {  
  21.             // Don’t allow installation over an existing package with the same name.  
  22.             res.setError(INSTALL_FAILED_ALREADY_EXISTS, “Attempt to re-install “ + pkgName  
  23.                     + ” without first uninstalling.”);  
  24.             return;  
  25.         }  
  26.     }  
  27.   
  28.     try {  
  29.         PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanFlags,//调用scanPackageLI  
  30.                 System.currentTimeMillis(), user);  
  31.   
  32.         updateSettingsLI(newPackage, installerPackageName, nullnull, res);  
  33.         // delete the partially installed application. the data directory will have to be  
  34.         // restored if it was already existing  
  35.         if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {  
  36.             // remove package from internal structures.  Note that we want deletePackageX to  
  37.             // delete the package data and cache directories that it created in  
  38.             // scanPackageLocked, unless those directories existed before we even tried to  
  39.             // install.  
  40.             deletePackageLI(pkgName, UserHandle.ALL, falsenullnull,  
  41.                     dataDirExists ? PackageManager.DELETE_KEEP_DATA : 0,  
  42.                             res.removedInfo, true);  
  43.         }  
  44.   
  45.     } catch (PackageManagerException e) {  
  46.         res.setError(“Package couldn’t be installed in “ + pkg.codePath, e);  
  47.     }  
  48. }  

这里和上篇博客分析扫描apk文件类似,我们来看下这个函数scanPackageLI

[java]
view plain
copy

《Android PKMS深入分析安装应用》
《Android PKMS深入分析安装应用》

  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.packageName);  
  12.         }  
  13.     }  
  14. }  

scanPackageLI函数主要调用了scanPackageDirtyLI函数,这个函数前面分析过了就不分析了。

我们再来看下在processPendingInstall函数中调用完installPackageLI函数之后,发送了一个POST_INSTALL消息,我们来看下这个消息的处理

[cpp]
view plain
copy

《Android PKMS深入分析安装应用》
《Android PKMS深入分析安装应用》

  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,//发送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;  

这样安装应用的流程就讲完了。

流程的时序图

《Android PKMS深入分析安装应用》

四 总结

我们主要分析一下scanPackageLI方法,还是仅仅分析,不帖代码,因为代码太长了,帖出来没法看了,这个方法不仅仅是完成apk包的扫描,还解析AndroidManifest.xml文件并提取出所有的intent-filter和permission信息,apk安装的主要功能都由它来完成的,当apk包扫描完成后,系统会调用updatePermissionsLPw方法更新系统所具有的权限。

scanPackageLI方法有两个,其第一个参数分别接受File和PackageParser.Package类型,第一个方法会从File中提取出package信息然后再调用第二个方法,下面分析第二个scanPackageLI方法,其完成的事情如下:

1. 如果包名是android,则会做一些特殊处理,这个包名为android的应用是系统内部应用的,其他应用的包名如果叫android则安装会有问题,大家可以试一下

2. 解析常见的use-feature、shared-userId、use-library标签并保存到成员变量中

3. 进行签名验证,对应的方法是verifySignaturesLP,验证失败则应用无法安装

4. 创建应用程序目录/data/data/包名,同时将apk中提取出dex文件并保存到/data/dalvik-cache,把apk当做zip解压就能得到dex文件

5. 解析AndroidManifest.xml文件,提取出所需信息,包括具有intent-filter的四大组件信息(Activity、Service、BroadcastReceiver、ContentProvider)和声明的系统权限等

到此为止,scanPackageLI方法结束了。而updatePermissionsLPw的作用是对系统中所有的权限进行更新,大家可以查看下/system/etc/permissons目录,下面定义了android系统中所有的权限,开发中最常用的权限定义在目录下的platform.xml里面,大家可以打开看看,可以看到常见的访问网络、读写外部存储等权限等都是在这里定义的。权限更新完毕以后,系统就会发送ACTION_PACKAGE_ADDED广播,告知所有应用有新应用安装了。另外,大家可以查看下data/system/目录,里面有两个文件packages.list和packages.xml,在packages.list里面放的是手机上安装的所有应用列表,而packages.xml中存放的是所有应用的设置应用,比如一个应用声明了哪些系统权限就定义在这里面。关于应用的卸载,我们可以想到是应用安装过程的逆过程,大致要做的是:停止应用、删除各种文件,更新系统设置、权限等,大家感兴趣自己看一下,完全是安装过程的逆过程,这里不介绍了。

6.关于 so 的拷贝我们还是照旧不细说 App 的安装流程了,主要还是和之前一样不论是替换还是新安装,都会调用 PackageManagerService 的 scanPackageLI() 函数,然后跑去 scanPackageDirtyLI 函数,而在这个函数中对于非系统的 APP 他调用了 derivePackageABI 这个函数,通过这个函数他将会觉得系统的abi是多少,并且也会进行我们最关心的 so 拷贝操作。

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