当前位置:   article > 正文

使用PackageInstaller安装app流程学习小结

packageinstaller

前言

  • 首先本文不是做PackageManagerService学习总结,PackageManagerService这货有1万2千多行代码,学习起来颇费劲,并且这货功能强大,本文只会总结其中一个小小的功能
  • 为何要做这个总结呢?说来话长,鄙人菜鸟一枚,接到一个安装应用过程中重启的问题,原因找到,但不知如何解决,无奈,只有硬着头皮学习了下这部分内容
  • OK,废话不多说,接下来直接上干货,如果文中有问题或有质疑的地方可以直接修改,不胜感激。


PackageInstaller介绍

  • PackageInstaller是个神马东西呢?
我们知道安装app有很多中方式,诸如adb install,应用助手(豌豆荚),开机安装(开机启动时),下载到手机存储后点击安装。PackageInstaller这哥就是给手动安装app提供一个界面的apk。
当我们点安装应用时会启动这个应用来显示安装过程,安装的事情并不是他在做,正在安装是由PackageManagerService来完成,当然幕后英雄确是Installer。
  • 代码位置
packages/apps/PackageInstaller
  • 疑问【以下有几个问题,如果亲都知道的话,那么可以不用再看本文啦】
如何使用PackageInstaller来安装应用?
应用首选安装位置是在何时确定的?是在设置里设置的,还是在app中定义的,还是PackageManagerService这货说了算?
安装应用过程中,哪些服务和类会插手这件事?
安装过程中首先会生成一个.tmp临时文件,这个文件在何时被rename为apk的?
应用都有uid,这个uid是在什么时候被赋值的?
packages.xml and packages.list有什么用?
安装app主要做了哪些事?


App安装过程中涉及类

  • 先来盘类图看看,如对PackageManagerService不熟悉的话,先看后面的流程,看完再来看这个类图
App Install ClassDiagram.jpg
  • 安装过程时会插手的主要类如下
packages/apps/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallAppProgress.java
frameworks/base /core/java/android/app/ApplicationPackageManager.java
frameworks/base /core/java/android/content/pm/PackageParser.java
frameworks/base /core/java/android/content/res/AssetManager.java
frameworks/base /packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
frameworks/base /services/java/com/android/server/pm/Installer.java
frameworks/base /services/java/com/android/server/pm/PackageManagerService.java
frameworks/base /services/java/com/android/server/pm/Settings.java
  • 简单说明这些类作用
PackageInstallerActivity.java:在文件管理器里点击apk后就会调用该类,主要用于显示要安装的apk的一些权限信息
InstallAppProgress.java:当看完所有权限后,点安装后就会调用该类,用于显示安装进度,这时候PackageManagerService就在默默的安装应用
ApplicationPackageManager.java:这是类是PackageManager的儿子,我们使用mContext.getPackageManager得到的其实就是ApplicationPackageManager的对象,它爹PackageManager是个抽象类,对外的方法都定义在里面
PackageParser.java:解析app,主要解析apk中的AndroidManifest.xml,解析里面的四大组件以及权限信息放入内存里,最后写到packages.xml和package.list(/data/system下)中
AssetManager.java:把AndroidManifest.xml从app中拿出来给PackageParser.java去解析
DefaultContainerService.java:这个服务用于检查存储状态,得到合适的安装位置
Installer.java:PackageManagerService调用它去执行安装,他会把PackageManagerService传过来的数据封装成命令,然后让底层的Installer去执行
PackageManagerService.java:管理app的大神,安装、移动、卸载、查询等都由他管


App安装流程分析

  • 先来个时序图--安装成功的时序图。点击两次可看大图



  • 流程分析,当然对着代码看上面的时序图也很明了

1.当点击文件管理器中的apk时,会调用FolderFragment的openFile方法,该方法里会将应用信息传给PackageInstallerActivity,并启动PackageInstaller

代码位置:vendor/qcom/proprietary/qrdplus/FileExplorer/src/com/android/qrdfileexplorer/FolderFragment.java

  1. private void openFile(File f) {
  2. final Uri fileUri = Uri.fromFile(f);
  3. final Intent intent = new Intent();
  4. intent.setAction(android.content.Intent.ACTION_VIEW);
  5. intent.putExtra(Intent.EXTRA_TITLE, f.getName());
  6. intent.putExtra(EXTRA_ALL_VIDEO_FOLDER, true);
  7. Uri contentUri = null;
  8. String type = getMIMEType(f);
  9. ......
  10. if (contentUri != null) {
  11. intent.setDataAndType(contentUri, type);
  12. } else {
  13. intent.setDataAndType(fileUri, type);
  14. }
  15. try {
  16. startActivitySafely(intent);
  17. }
  18. ......
  19. }

2.PackageInstaller启动过后会检查是否开启未知来源,未开启就需要先进入设置设置后,方可继续安装,之后会依次调用initiateInstall()->startInstallConfirm();
在initiateInstall中会检查是否已经安装过,是否是系统应用等,调用startInstallConfirm去初始化界面,显示权限信息,当点击安装按钮时,启动安装,切换界面到InstallAppProgress

代码位置:packages/apps/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java

  1. @Override
  2. protected void onCreate(Bundle icicle) {
  3. ......
  4. mPm = getPackageManager();
  5. boolean requestFromUnknownSource = isInstallRequestFromUnknownSource(intent);
  6. ......
  7. initiateInstall();
  8. }
  9. private void initiateInstall() {
  10. String pkgName = mPkgInfo.packageName;
  11. String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName });
  12. if (oldName != null && oldName.length > 0 && oldName[0] != null) {
  13. pkgName = oldName[0];
  14. mPkgInfo.packageName = pkgName;
  15. mPkgInfo.applicationInfo.packageName = pkgName;
  16. }
  17. // Check if package is already installed. display confirmation dialog if replacing pkg
  18. try {
  19. // This is a little convoluted because we want to get all uninstalled
  20. // apps, but this may include apps with just data, and if it is just
  21. // data we still want to count it as "installed".
  22. mAppInfo = mPm.getApplicationInfo(pkgName,
  23. PackageManager.GET_UNINSTALLED_PACKAGES);
  24. if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
  25. mAppInfo = null;
  26. }
  27. } catch (NameNotFoundException e) {
  28. mAppInfo = null;
  29. }
  30. mInstallFlowAnalytics.setReplace(mAppInfo != null);
  31. mInstallFlowAnalytics.setSystemApp(
  32. (mAppInfo != null) && ((mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0));
  33. startInstallConfirm();
  34. }

3.在InstallAppProgress中会调用initView去初始化界面并调用ApplicationPackageManager的installPackageWithVerificationAndEncryption方法来安装

代码位置:packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallAppProgress.java

  1. @Override
  2. public void onCreate(Bundle icicle) {
  3. ......
  4. initView();
  5. }
  6. public void initView() {
  7. ......
  8. if ("package".equals(mPackageURI.getScheme())) {
  9. try {
  10. pm.installExistingPackage(mAppInfo.packageName);
  11. observer.packageInstalled(mAppInfo.packageName,
  12. PackageManager.INSTALL_SUCCEEDED);
  13. } catch (PackageManager.NameNotFoundException e) {
  14. observer.packageInstalled(mAppInfo.packageName,
  15. PackageManager.INSTALL_FAILED_INVALID_APK);
  16. }
  17. } else {
  18. pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags,
  19. installerPackageName, verificationParams, null);
  20. }
  21. }

4.ApplicationPackageManager的installPackageWithVerificationAndEncryption里也是调用PMS的installPackageWithVerificationAndEncryption方法

代码位置:frameworks/base/core/java/android/app/ApplicationPackageManager.java

  1. @Override
  2. public void installPackageWithVerificationAndEncryption(Uri packageURI,
  3. IPackageInstallObserver observer, int flags, String installerPackageName,
  4. VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
  5. try {
  6. mPM.installPackageWithVerificationAndEncryption(packageURI, observer, flags,
  7. installerPackageName, verificationParams, encryptionParams);
  8. } catch (RemoteException e) {
  9. // Should never happen!
  10. }
  11. }

5.installPackageWithVerificationAndEncryption方法里,首先会获取设置中的用户安装位置,并且会把InstallParams对象和安装位置flag封装到Message里,然后发出一个消息后就撒手不管了。

代码位置:frameworks/base/services/java/com/android/server/pm/PackageManagerService.java

  1. public void installPackageWithVerificationAndEncryption(Uri packageURI,
  2. IPackageInstallObserver observer, int flags, String installerPackageName,
  3. VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {
  4. mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,
  5. null);
  6. final int uid = Binder.getCallingUid();
  7. if(getInstallLocation() == PackageHelper.APP_INSTALL_INTERNAL){
  8. userFilteredFlags = flags |PackageManager.INSTALL_INTERNAL;
  9. } else if(getInstallLocation() == PackageHelper.APP_INSTALL_EXTERNAL){
  10. userFilteredFlags = flags |PackageManager.INSTALL_EXTERNAL;
  11. } else{
  12. userFilteredFlags = filteredFlags;
  13. }
  14. final Message msg = mHandler.obtainMessage(INIT_COPY);
  15. msg.obj = new InstallParams(packageURI, observer, userFilteredFlags, installerPackageName,
  16. verificationParams, encryptionParams, user);
  17. mHandler.sendMessage(msg);
  18. }

6.接下来就该PackageHandler上场了,会依次处理INIT_COPY、MCS_BOUN消息,这里面会去连接DefaultContainerService服务,接着会InstallParams的startCopy方法

代码位置:frameworks/base/services/java/com/android/server/pm/PackageManagerService.java ->内部类:PackageHandler

  1. public void handleMessage(Message msg) {
  2. try {
  3. doHandleMessage(msg);
  4. } finally {
  5. Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
  6. }
  7. }
  8. void doHandleMessage(Message msg) {
  9. switch (msg.what) {
  10. case INIT_COPY: {
  11. HandlerParams params = (HandlerParams) msg.obj;
  12. if (!mBound) {
  13. if (!connectToService()) {
  14. params.serviceError();
  15. return;
  16. } else {
  17. mPendingInstalls.add(idx, params);
  18. }
  19. } else {
  20. mPendingInstalls.add(idx, params);
  21. if (idx == 0) {
  22. mHandler.sendEmptyMessage(MCS_BOUND);
  23. }
  24. }
  25. break;
  26. }
  27. case MCS_BOUN: {
  28. if (msg.obj != null) {
  29. mContainerService = (IMediaContainerService) msg.obj;
  30. }
  31. if (mContainerService == null) {
  32. for (HandlerParams params : mPendingInstalls) {
  33. params.serviceError();
  34. }
  35. mPendingInstalls.clear();
  36. } else if (mPendingInstalls.size() > 0) {
  37. HandlerParams params = mPendingInstalls.get(0);
  38. if (params != null) {
  39. if (params.startCopy()) {
  40. ......
  41. }
  42. }
  43. } else {
  44. Slog.w(TAG, "Empty queue");
  45. }
  46. break;
  47. }
  48. ......
  49. case POST_INSTALL: {
  50. ...
  51. if (data != null) {
  52. InstallArgs args = data.args;
  53. PackageInstalledInfo res = data.res;
  54. if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
  55. ......
  56. sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
  57. res.pkg.applicationInfo.packageName, extras, null, null, firstUsers);
  58. final boolean update = res.removedInfo.removedPackage != null;
  59. if (update) {
  60. extras.putBoolean(Intent.EXTRA_REPLACING, true);
  61. }
  62. sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED,
  63. res.pkg.applicationInfo.packageName, extras, null, null, updateUsers);
  64. if (update) {
  65. sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
  66. res.pkg.applicationInfo.packageName, extras, null, null, updateUsers);
  67. sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
  68. null, null, res.pkg.applicationInfo.packageName, null, updateUsers);
  69. if (isForwardLocked(res.pkg) || isExternal(res.pkg)) {
  70. ......
  71. sendResourcesChangedBroadcast(true, true, pkgList,uidArray, null);
  72. }
  73. }
  74. if (res.removedInfo.args != null) {
  75. deleteOld = true;
  76. }
  77. }
  78. ......
  79. if (args.observer != null) {
  80. try {
  81. args.observer.packageInstalled(res.name, res.returnCode);
  82. } catch (RemoteException e) {
  83. Slog.i(TAG, "Observer no longer exists.");
  84. }
  85. }
  86. } else {
  87. Slog.e(TAG, "Bogus post-install token " + msg.arg1);
  88. }
  89. break;
  90. }
  91. }
  92. }

7.InstallParams的startCopy方法里,会调用handleStartCopy方法

代码位置:frameworks/base/services/java/com/android/server/pm/PackageManagerService.java ->内部类:InstallParams 继承于HandlerParams

  1. final boolean startCopy() {
  2. boolean res;
  3. try {
  4. if (++mRetries > MAX_RETRIES) {
  5. Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
  6. mHandler.sendEmptyMessage(MCS_GIVE_UP);
  7. handleServiceError();
  8. return false;
  9. } else {
  10. handleStartCopy();
  11. res = true;
  12. }
  13. } catch (RemoteException e) {
  14. mHandler.sendEmptyMessage(MCS_RECONNECT);
  15. res = false;
  16. }
  17. handleReturnCode();
  18. return res;
  19. }

8.handleStartCopy方法中会检查应用是否能安装,如不合法则返回FAILED的CODE,接着会调用DefaultContainerService的getMinimalPackageInfo方法,该方法用于获取存储状态,返回合适的安装位置
如果返回码是INSTALL_SUCCEEDED,那接下来就会调用InstallParams的copyApk,如果安装到内置,调用的就是FileInstallArgs的copyApk方法,如安装到外置就调用AsecInstallArgs的copyApk方法
AsecInstallArgs和FileInstallArgs都是InstallParams的子类

代码位置:frameworks/base/services/java/com/android/server/pm/PackageManagerService.java ->内部类:FileInstallArgs 继承于InstallParams

  1. public void handleStartCopy() throws RemoteException {
  2. ......
  3. if (onInt && onSd) {
  4. ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
  5. } else {
  6. ......
  7. try {
  8. mContext.grantUriPermission(DEFAULT_CONTAINER_PACKAGE, mPackageURI,
  9. Intent.FLAG_GRANT_READ_URI_PERMISSION);
  10. ........
  11. if (packageFile != null) {
  12. ......
  13. pkgLite = mContainerService.getMinimalPackageInfo(packageFilePath, flags, lowThreshold);
  14. }
  15. }
  16. }
  17. final InstallArgs args = createInstallArgs(this);
  18. mArgs = args;
  19. ......
  20. if (ret == PackageManager.INSTALL_SUCCEEDED) {
  21. ......
  22. ret = args.copyApk(mContainerService, true);
  23. ......
  24. }
  25. mRet = ret;
  26. }

9.copyApk方法中会依次调用FileInstallArgs 的createCopyFile->PackageManagerService的createTempPackageFile方法去创建临时文件。

代码位置:frameworks/base/services/java/com/android/server/pm/PackageManagerService.java ->内部类:FileInstallArgs 继承于InstallParams
          frameworks/base/services/java/com/android/server/pm/PackageManagerService.java
vmdl*.tmp就是copy成的临时文件
App Install pic 2.jpg

  1. void createCopyFile() {
  2. installDir = isFwdLocked() ? mDrmAppPrivateInstallDir : mAppInstallDir;
  3. codeFileName = createTempPackageFile(installDir).getPath();
  4. resourceFileName = getResourcePathFromCodePath();
  5. libraryPath = getLibraryPathFromCodePath();
  6. created = true;
  7. }
  8. private File createTempPackageFile(File installDir) {
  9. File tmpPackageFile;
  10. try {
  11. tmpPackageFile = File.createTempFile("vmdl", ".tmp", installDir);
  12. } catch (IOException e) {
  13. Slog.e(TAG, "Couldn't create temp file for downloaded package file.");
  14. return null;
  15. }
  16. try {
  17. FileUtils.setPermissions(
  18. tmpPackageFile.getCanonicalPath(), FileUtils.S_IRUSR|FileUtils.S_IWUSR,
  19. -1, -1);
  20. if (!SELinux.restorecon(tmpPackageFile)) {
  21. return null;
  22. }
  23. } catch (IOException e) {
  24. Slog.e(TAG, "Trouble getting the canoncical path for a temp file.");
  25. return null;
  26. }
  27. return tmpPackageFile;
  28. }

10.临时文件已经有了,handleStartCopy方法走完,接着回到步骤7,调用InstallParams的handleReturnCode方法,handleReturnCode中会执行processPendingInstall,在该方法中做了大量工作

  1. @Override
  2. void handleReturnCode() {
  3. if (mArgs != null) {
  4. processPendingInstall(mArgs, mRet);
  5. if (mTempPackage != null) {
  6. if (!mTempPackage.delete()) {
  7. Slog.w(TAG, "Couldn't delete temporary file: " +
  8. mTempPackage.getAbsolutePath());
  9. }
  10. }
  11. }
  12. }

11.来看看processPendingInstall到底做了什么?processPendingInstall中最关键方法--installPackageLI,主要的操作(验证签名,创建/data/data,分配UID,dexopt)都在这个方法中完成。

  1. private void processPendingInstall(final InstallArgs args, final int currentStatus) {
  2. mHandler.post(new Runnable() {
  3. public void run() {
  4. mHandler.removeCallbacks(this);
  5. PackageInstalledInfo res = new PackageInstalledInfo();
  6. res.returnCode = currentStatus;
  7. res.uid = -1;
  8. res.pkg = null;
  9. res.removedInfo = new PackageRemovedInfo();
  10. if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
  11. args.doPreInstall(res.returnCode);
  12. synchronized (mInstallLock) {
  13. installPackageLI(args, true, res);
  14. }
  15. args.doPostInstall(res.returnCode, res.uid);
  16. }
  17. ......
  18. if (!doRestore) {
  19. Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
  20. mHandler.sendMessage(msg);
  21. }
  22. }
  23. });
  24. }

12.我们来看一下installPackageLI方法,首选会让parsePackage去解析apk里的AndroidManifest.xml,使用的是parsePackage方法,把解析出来的内容放到Package对象中
接着调用doRename去将之前的tmp文件重命名为apk。apk已经在/data/app下了,apk的属性也被解析出来放在内存(Package对象)中了
那么现在还需要做什么呢?apk有了,数据目录(/data/data)还没有,所以后面会进行uid赋值,验证签名,创建相应的/data/data目录,dexopt操作,这些工作是由installNewPackageLI来完成

  1. private void installPackageLI(InstallArgs args,
  2. boolean newInstall, PackageInstalledInfo res) {
  3. ......
  4. int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY
  5. | (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)
  6. | (onSd ? PackageParser.PARSE_ON_SDCARD : 0);
  7. PackageParser pp = new PackageParser(tmpPackageFile.getPath());
  8. pp.setSeparateProcesses(mSeparateProcesses);
  9. final PackageParser.Package pkg = pp.parsePackage(tmpPackageFile,
  10. null, mMetrics, parseFlags);
  11. ......
  12. if (!args.doRename(res.returnCode, pkgName, oldCodePath)) {
  13. res.returnCode = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
  14. return;
  15. }
  16. ......
  17. if (replace) {
  18. replacePackageLI(pkg, parseFlags, scanMode, args.user,
  19. installerPackageName, res);
  20. } else {
  21. installNewPackageLI(pkg, parseFlags, scanMode | SCAN_DELETE_DATA_ON_FAILURES, args.user,
  22. installerPackageName, res);
  23. }
  24. synchronized (mPackages) {
  25. final PackageSetting ps = mSettings.mPackages.get(pkgName);
  26. if (ps != null) {
  27. res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
  28. }
  29. }
  30. }

13.让我们来看看installNewPackageLI具体怎么完成这些工作的吧。

  1. private void installNewPackageLI(PackageParser.Package pkg,
  2. int parseFlags, int scanMode, UserHandle user,
  3. String installerPackageName, PackageInstalledInfo res) {
  4. ......
  5. PackageParser.Package newPackage = scanPackageLI(pkg, parseFlags, scanMode, System.currentTimeMillis(), user);
  6. if (newPackage == null) {
  7. ......
  8. } else {
  9. updateSettingsLI(newPackage, installerPackageName, null, null, res);
  10. ......
  11. if (res.returnCode != PackageManager.INSTALL_SUCCEEDED) {
  12. deletePackageLI(pkgName, UserHandle.ALL, false, null, null, dataDirExists ? PackageManager.DELETE_KEEP_DATA : 0,
  13. res.removedInfo, true);
  14. }
  15. }
  16. }

14.scanPackageLI方法中,先调用getPackageLPw->newUserIdLPw(Settings类方法)去设置uid,在调用verifySignaturesLP验证签名
然后调用createDataDirsLI创建/data/data数据目录,最后调用performDexOptLI进行dexopt操作
createDataDirsLI是靠调用mInstaller.install方法来完成目录创建,framework中的Installer会和底层幕后Installer勾兑,完成目录创建工作
performDexOptLI操作最后也是通过mInstaller.dexopt来完成的

  1. private PackageParser.Package scanPackageLI(PackageParser.Package pkg,
  2. int parseFlags, int scanMode, long currentTime, UserHandle user) {
  3. ......
  4. synchronized (mPackages) {
  5. ......
  6. pkgSetting = mSettings.getPackageLPw(pkg, origPackage, realName, suid, destCodeFile,
  7. destResourceFile, pkg.applicationInfo.nativeLibraryDir,
  8. pkg.applicationInfo.flags, user, false);
  9. if (pkgSetting == null) {
  10. mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
  11. return null;
  12. }
  13. ......
  14. if (!verifySignaturesLP(pkgSetting, pkg)) {
  15. if ((parseFlags&PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
  16. return null;
  17. }
  18. // The signature has changed, but this package is in the system
  19. // image... let's recover!
  20. pkgSetting.signatures.mSignatures = pkg.mSignatures;
  21. // However... if this package is part of a shared user, but it
  22. // doesn't match the signature of the shared user, let's fail.
  23. // What this means is that you can't change the signatures
  24. // associated with an overall shared user, which doesn't seem all
  25. // that unreasonable.
  26. if (pkgSetting.sharedUser != null) {
  27. if (compareSignatures(pkgSetting.sharedUser.signatures.mSignatures,
  28. pkg.mSignatures) != PackageManager.SIGNATURE_MATCH) {
  29. Log.w(TAG, "Signature mismatch for shared user : " + pkgSetting.sharedUser);
  30. mLastScanError = PackageManager.INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES;
  31. return null;
  32. }
  33. }
  34. // File a report about this.
  35. String msg = "System package " + pkg.packageName
  36. + " signature changed; retaining data.";
  37. reportSettingsProblem(Log.WARN, msg);
  38. }
  39. ......
  40. }
  41. ......
  42. if (mPlatformPackage == pkg) {
  43. ......
  44. } else {
  45. ......
  46. if (dataPath.exists()) {
  47. ......
  48. } else {
  49. ......
  50. int ret = createDataDirsLI(pkgName, pkg.applicationInfo.uid,
  51. pkg.applicationInfo.seinfo);
  52. if (ret < 0) {
  53. // Error from installer
  54. mLastScanError = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
  55. return null;
  56. }
  57. if (dataPath.exists()) {
  58. pkg.applicationInfo.dataDir = dataPath.getPath();
  59. } else {
  60. Slog.w(TAG, "Unable to create data directory: " + dataPath);
  61. pkg.applicationInfo.dataDir = null;
  62. }
  63. }
  64. /*
  65. * Set the data dir to the default "/data/data/<package name>/lib"
  66. * if we got here without anyone telling us different (e.g., apps
  67. * stored on SD card have their native libraries stored in the ASEC
  68. * container with the APK).
  69. *
  70. * This happens during an upgrade from a package settings file that
  71. * doesn't have a native library path attribute at all.
  72. */
  73. if (pkg.applicationInfo.nativeLibraryDir == null && pkg.applicationInfo.dataDir != null) {
  74. if (pkgSetting.nativeLibraryPathString == null) {
  75. setInternalAppNativeLibraryPath(pkg, pkgSetting);
  76. } else {
  77. pkg.applicationInfo.nativeLibraryDir = pkgSetting.nativeLibraryPathString;
  78. }
  79. }
  80. pkgSetting.uidError = uidError;
  81. }
  82. ......
  83. // We also need to dexopt any apps that are dependent on this library. Note that
  84. // if these fail, we should abort the install since installing the library will
  85. // result in some apps being broken.
  86. if (clientLibPkgs != null) {
  87. if ((scanMode&SCAN_NO_DEX) == 0) {
  88. for (int i=0; i<clientLibPkgs.size(); i++) {
  89. PackageParser.Package clientPkg = clientLibPkgs.get(i);
  90. if (performDexOptLI(clientPkg, forceDex, (scanMode&SCAN_DEFER_DEX) != 0, false)
  91. == DEX_OPT_FAILED) {
  92. if ((scanMode & SCAN_DELETE_DATA_ON_FAILURES) != 0) {
  93. removeDataDirsLI(pkg.packageName);
  94. }
  95. mLastScanError = PackageManager.INSTALL_FAILED_DEXOPT;
  96. return null;
  97. }
  98. }
  99. }
  100. }
  101. // Request the ActivityManager to kill the process(only for existing packages)
  102. // so that we do not end up in a confused state while the user is still using the older
  103. // version of the application while the new one gets installed.
  104. ......
  105. // Also need to kill any apps that are dependent on the library.
  106. ......
  107. return pkg;
  108. }
  1. private int createDataDirsLI(String packageName, int uid, String seinfo) {
  2. int[] users = sUserManager.getUserIds();
  3. int res = mInstaller.install(packageName, uid, uid, seinfo);
  4. if (res < 0) {
  5. return res;
  6. }
  7. for (int user : users) {
  8. if (user != 0) {
  9. res = mInstaller.createUserData(packageName,
  10. UserHandle.getUid(user, uid), user);
  11. if (res < 0) {
  12. return res;
  13. }
  14. }
  15. }
  16. return res;
  17. }
  1. private int performDexOptLI(PackageParser.Package pkg, boolean forceDex, boolean defer,
  2. boolean inclDependencies) {
  3. ......
  4. return performDexOptLI(pkg, forceDex, defer, done);
  5. }
  6. private int performDexOptLI(PackageParser.Package pkg, boolean forceDex, boolean defer,
  7. HashSet<String> done) {
  8. ......
  9. if ((pkg.applicationInfo.flags&ApplicationInfo.FLAG_HAS_CODE) != 0) {
  10. ......
  11. try {
  12. if (forceDex || dalvik.system.DexFile.isDexOptNeeded(path)) {
  13. if (!forceDex && defer) {
  14. if (mDeferredDexOpt == null) {
  15. mDeferredDexOpt = new HashSet<PackageParser.Package>();
  16. }
  17. mDeferredDexOpt.add(pkg);
  18. return DEX_OPT_DEFERRED;
  19. } else {
  20. final int sharedGid = UserHandle.getSharedAppGid(pkg.applicationInfo.uid);
  21. ret = mInstaller.dexopt(path, sharedGid, !isForwardLocked(pkg));
  22. pkg.mDidDexOpt = true;
  23. performed = true;
  24. }
  25. }
  26. } catch (FileNotFoundException e) {
  27. ......
  28. } catch (IOException e) {
  29. ......
  30. } catch (dalvik.system.StaleDexCacheError e) {
  31. ......
  32. } catch (Exception e) {
  33. ......
  34. }
  35. if (ret < 0) {
  36. //error from installer
  37. return DEX_OPT_FAILED;
  38. }
  39. }
  40. return performed ? DEX_OPT_PERFORMED : DEX_OPT_SKIPPED;
  41. }

performDexOptLI后生成的文件
App Install pic3.jpg

15.到目前为止,scanPackageLI已经走完了,接下来就该更新packages.list,packages.xml了
系统中所有app的信息都保存在这两个文件中,当有app安装、卸载、更新时都会更新这两个文件
回到步骤13,当installNewPackageLI中的scanPackageLI走完后,后面会调用updateSettingsLI去更新文件
mSettings.writeLPr()来完成往packages.list,packages.xml中更新数据

  1. private void updateSettingsLI(PackageParser.Package newPackage, String installerPackageName,
  2. int[] allUsers, boolean[] perUserInstalled,
  3. PackageInstalledInfo res) {
  4. String pkgName = newPackage.packageName;
  5. synchronized (mPackages) {
  6. ......
  7. mSettings.setInstallStatus(pkgName, PackageSettingBase.PKG_INSTALL_INCOMPLETE);
  8. mSettings.writeLPr();
  9. }
  10. ......
  11. synchronized (mPackages) {
  12. updatePermissionsLPw(newPackage.packageName, newPackage,
  13. UPDATE_PERMISSIONS_REPLACE_PKG | (newPackage.permissions.size() > 0
  14. ? UPDATE_PERMISSIONS_ALL : 0));
  15. ......
  16. mSettings.writeLPr();
  17. }
  18. }

packages.list文件部分截取
App Install pic 5.jpg
packages.xml部分截取,包含包名,安装时间,签名,权限,文件安装路径等信息
App Install pic 6.jpg

16.installPackageLI到这里已经执行完了,现在回到步骤11,后续会执行到Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0); mHandler.sendMessage(msg);发送消息
消息发出后,回到步骤POST_INSTALL,这里面主要做了两件事,发生一条ACTION_PACKAGE_ADDED广播告诉大家,又有新包了,这是launcher什么的赶紧把图标加上
然后回调args.observer.packageInstalled(res.name, res.returnCode);告诉PackageInstaller安装结果
然后就显示安装完成界面。欧拉,应用安装结束。


总结

小结一下安装app到底主要做了哪些事情?

1.验证是否允许安装未知来源应用
2.得到用户设置的首选安装位置
3.检验app有效性,检查存储状态,得到最佳安装位置
4.拷贝app到安装位置,此时为.tmp临时文件
5.解析AndroidManifest.xml
6.重命名tmp为apk
7.赋值UID,验证权限
8.创建/data/data/下数据目录
9.执行dexopt操作
10.更新packages.xml,packages.list
11.发送广播,回调安装状态


声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/199101
推荐阅读
相关标签
  

闽ICP备14008679号