Android Framework之PMS篇

84478543 7年前
   <h2>1. PMS运行时的一些规则</h2>    <p>PMS相关的目录与文件,以及PMS操作它们的规则。</p>    <h2>/data/app</h2>    <p>用户安装的第三方apk,以及app所依赖的native library都放在这里。在Android 6.0时,此目录增加了一个文件夹“oat”,用来存放此app第一次运行时由dex2oat生成的此app的oat文件。在之前的Android版本中,用户安装的app的oat文件存储在/data/dalvik-cache中。6.0时,此目录只存放系统自带的apk的oat文件。</p>    <h2>/data/data</h2>    <p>是系统当前用户安装的所有app的沙箱目录。该目录实际上是data/user/用户ID目录的引用。随着用户的切换,”/data/data/“也会映射为不同的用户。</p>    <h2>PMS的配置文件</h2>    <p>PMS会产生一些配置文件,用来记录系统当前安装的app,这些文件存储在:data/system/users/userId/</p>    <ol>     <li>packages.xml------->记录系统中所有已经安装的应用信息,包括基本信息,签名和权限。</li>    </ol>    <p><img src="https://simg.open-open.com/show/812ad676d6cbe189f7e21fcaf7b25e16.png"></p>    <p>packages内容之Package标签.png</p>    <p>Package标签</p>    <ul>     <li>Name:程序包名称</li>     <li>codePath:程序包所在路径</li>     <li>nativeLibraryPath:该程序所使用的native库文件路径。</li>     <li>primaryCpuAbi:apk支持的abi类型(优先)</li>     <li>userId:应用程序对应的Linux用户Id</li>     <li>sharedUserId:若在androidManifest.xml中定义了sharedUserId,则此处使用它而非userId。</li>     <li> <p>Sigs:签名信息。一个应用程序只能有一个签名。</p> </li>     <li> <p>Perms:一个应用程序所申请的权限列表。androidManifest.xml中每使用一个<uses-permission>,则packages.xml中<perms>标签就会增加一项。</p> </li>    </ul>    <p><img src="https://simg.open-open.com/show/91bcfba8f7436e7ceeb7d8803e3b7cc1.png"></p>    <p>packages内容之share-user标签.png</p>    <p>Shared-user标签</p>    <ul>     <li> <p>定义了共享用户id对应的签名和权限</p> <p>当操作该文件的时候,总会创建备份文件packages-backup.xml。当正常操作完成的时候,会删除该备份。否则,当PMS下次启动的时候,一旦发现有backup文件,就会优先解析备份文件。当一个app被升级覆盖安装时,会使用<updated-packages>表示,当新旧版本app的包名发生改变时,会使用<renamed-package>记录。</p> </li>    </ul>    <p>2.packages-stoped.xml------->记录系统中被强制停止运行的app的信息。它同样可能存在一个packages-stoped-backup.xml的备份文件,当备份文件存在的时候,优先使用备份文件。因为原文件可能已经损坏了。</p>    <p>3.packages.list------->保存应用的数据目录和uid信息。</p>    <p>如:</p>    <pre>  <code class="language-java">com.qihoo.appstore 10067 0 /data/data/com.qihoo.appstore default 3002,3003,3001</code></pre>    <p>第一列为app的包名,第二列为10067为此app的用户ID,第三列中的0,表示此app所属的系统用户ID,第四列为此app的数据文件目录。</p>    <p>default为seinfo,SEAndroid相关机制会使用该字段。</p>    <p>最后一列记录了该app所在的权限组,也就是说拥有哪些权限。</p>    <h2>系统硬件特性和权限</h2>    <p>PMS启动的时候会从/system/etc/permissions/中读取当前Android设备的硬件特性和设定的相关权限。</p>    <p>xxx.xml 包含很多feature,用来描述手机应该支持的硬件特性,如支持camera,蓝牙等</p>    <p>Platform.xml 建立上层permission同底层uid/gid的关系</p>    <p>正是因为解析了该目录中的文件,所以可以通过pm命令查看features和permissions等信息。</p>    <h2>多用户管理</h2>    <p>PMS还要对多用户进行管理。因为安装apk的时候,可以PMS可以指定给某个特定的用户,也可以安装给全部的用户。</p>    <h2>权限动态管理</h2>    <p>Android M 中 允许动态授权和取消App中申请的权限。修改后会相应的写入runtime-permissions.xml</p>    <h2>2. PMS的机制与实现</h2>    <h2>2.1 PMS体系结构</h2>    <p style="text-align:center"><img src="https://simg.open-open.com/show/7e8ab4329c1fab9a0f260b4147ac32d7.jpg"></p>    <p>PMS体系结构图</p>    <h2>2.2 PMS相关代码路径</h2>    <pre>  <code class="language-java">frameworks/base/core/java/android/content/pm/PackageManager.java  frameworks/base/core/java/android/content/pm/PackageParser.java    frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java  frameworks/base/services/core/java/com/android/server/pm/Settings.java  frameworks/base/services/core/java/com/android/server/pm/Installer.java    //Runtime-Permission相关  frameworks/base/services/core/java/com/android/server/pm/ DefaultPermissionGrantPolicy.java  frameworks/base/core/java/android/os/FileObserver.java    frameworks/base/core/java/android/app/ApplicationPackageManager.java    frameworks/base/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java    frameworks/native/cmds/installd/commands.c  frameworks/native/cmds/installd/installd.c  frameworks/native/cmds/installd/utils.c</code></pre>    <h2>2.3 PMS的类关系图</h2>    <p><img src="https://simg.open-open.com/show/50d956f9128c348a80c165ee698dee94.jpg"></p>    <p>PMS类关系图</p>    <h2>2.4PMS的启动流程</h2>    <p style="text-align:center"><img src="https://simg.open-open.com/show/885d887dbd71f2395d43d0051e03ab1b.jpg"></p>    <p>PMS的启动流程图</p>    <h3>2.4.1 获取系统默认配置</h3>    <pre>  <code class="language-java">public PackageManagerService(Context context, Installer installer,              boolean factoryTest, boolean onlyCore) {        ……        mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type"));          long dexOptLRUThresholdInMinutes;          if (mLazyDexOpt) {              dexOptLRUThresholdInMinutes = 30; // only last 30 minutes of apps for eng builds.          } else {              dexOptLRUThresholdInMinutes = 7 * 24 * 60; // apps used in the 7 days for users.          }          mDexOptLRUThresholdInMills = dexOptLRUThresholdInMinutes * 60 * 1000;          mMetrics = new DisplayMetrics();          String separateProcesses = SystemProperties.get("debug.separate_processes");        ……</code></pre>    <ol>     <li>PMS启动阶段主要获取ro.build.type和debug.separate_processes两个值。</li>     <li>ro.build.type:用于标记版本是eng还是usr。如果为eng,则mLazyDexOpt为true。通过mLazyDexOpt来设定dexOptLRUThresholdInMinutes的值(该值在filterRecentlyUsedApps方法中用于过滤最近使用的apps,如果大于mDexOptLRUThresholdInMills则不进行dexopt操作。usr版本为7 days,eng版本为30 mins) 。</li>     <li>debug.separate_processes:用于标记是否在独立进程中运行某个程序。根据该值设置PMS.mDefParseFlags和PMS.mSeparateProcesses两个全局变量,后续scanDirLi扫描并安装APK的时候用到。</li>     <li>获取系统默认显示参数<br> 通过new DisplayMetrics()获取系统默认显示参数,存入PMS.mMetrics变量中,主要用于匹配APK中的asset和resource。 <h3>2.4.2 创建并初始化Settings对象</h3> </li>    </ol>    <pre>  <code class="language-java">public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) {        ……          mSettings = new Settings(context);          mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,                  ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);          mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,                  ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);          mSettings.addSharedUserLPw("android.uid.log", LOG_UID,                  ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);          mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,                  ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);          mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,                  ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);          mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,                  ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);</code></pre>    <p>Setting初始化阶段包括以下两部分工作:</p>    <p>1) 调用构造函数初始化new Settings(context)</p>    <p>2) 调用addSharedUserLPw方法添加6个默认共享用户ID</p>    <p>1.调用构造函数初始化</p>    <pre>  <code class="language-java">Settings(Context context) {          this(context, Environment.getDataDirectory());      }      Settings(Context context, File dataDir) {          mSystemDir = new File(dataDir, "system");          mSystemDir.mkdirs();          FileUtils.setPermissions(mSystemDir.toString(),                  FileUtils.S_IRWXU|FileUtils.S_IRWXG                  |FileUtils.S_IROTH|FileUtils.S_IXOTH,                  -1, -1);          mSettingsFilename = new File(mSystemDir, "packages.xml");//记录系统中所有已安装的apk信息,安装和卸载时会更新          mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");          mPackageListFilename = new File(mSystemDir, "packages.list");//记录系统中所有已安装APK的简略信息          FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID);          mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");          mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");      }</code></pre>    <h3>2.4.3 创建dexopt优化器对象</h3>    <pre>  <code class="language-java">mInstaller = installer;          mPackageDexOptimizer = new PackageDexOptimizer(this);          mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());          mOnPermissionChangeListeners = new OnPermissionChangeListeners(FgThread.get().getLooper());</code></pre>    <p>创建PackageDexOptimizer对象,该类主要用来执行ART中的patchoat命令,用来对oat文件的偏移值进行随机化。该类是Android M 中才有的。创建监听权限更改的监听者。因为Android M中允许动态修改App权限。</p>    <h3>2.4.4 解析系统Permissions和Feature信息</h3>    <pre>  <code class="language-java">public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) {          SystemConfig systemConfig = SystemConfig.getInstance();          mGlobalGids = systemConfig.getGlobalGids();          mSystemPermissions = systemConfig.getSystemPermissions();          mAvailableFeatures = systemConfig.getAvailableFeatures();          ……</code></pre>    <p>SystemConfig负责解析系统Permissions和Feature信息,路经为system/etc/sysconfig和system/etc/permissions。其流程如下:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/e4b3cd7d50228d116d3a9e4a8843cc94.jpg"></p>    <p>SystemConfig解析Permissions和Feature流程</p>    <p>解析完成后,Pemissions文件中各标签和SystemConfig及PMS变量的对应关系如下:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/ed1528f1da532b8351e3b2abe4c0207c.png"></p>    <p>Paste_Image.png</p>    <h3>2.4.5 启动PackageHandler</h3>    <p>进入mPackages同步块后首先启动PackageHandler</p>    <pre>  <code class="language-java">public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) {  synchronized (mInstallLock) {          // writer          synchronized (mPackages) {              mHandlerThread = new ServiceThread(TAG,                      Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);              mHandlerThread.start();              mHandler = new PackageHandler(mHandlerThread.getLooper());              Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);              ……</code></pre>    <p>PackageHandler是PMS的内部类,用于接收并处理其他模块程序发送的消息,这些消息可以来自adb或者其他应用程序</p>    <p>在PackageHandler的handleMessage方法中调用doHandleMessage方法处理消息,代码如下:</p>    <pre>  <code class="language-java">class PackageHandler extends Handler {          void doHandleMessage(Message msg) {              switch (msg.what) {                  case INIT_COPY:                   case MCS_BOUND:                   case MCS_CHECK:                   case MCS_RECONNECT:                   case MCS_UNBIND:                   case MCS_GIVE_UP:                  case SEND_PENDING_BROADCAST:                   case START_CLEANING_PACKAGE:                  case POST_INSTALL:                  case UPDATED_MEDIA_STATUS:                  case WRITE_SETTINGS:                  case WRITE_PACKAGE_RESTRICTIONS:                   case CHECK_PENDING_VERIFICATION:                   case PACKAGE_VERIFIED:   ……</code></pre>    <p>以上消息主要用于APK的复制和更名操作,但这些操作并不是在PackageHandler中完成的。而是在PackageHandler的消息处理函数中会通过connectToService方法绑定到MCS服务,即DefaultContainerService。</p>    <h3>2.4.6 创建data目录并初始化UserManagerService</h3>    <pre>  <code class="language-java">public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) {          File dataDir = Environment.getDataDirectory();//即“/data”目录              mAppDataDir = new File(dataDir, "data");// data/data              mAppInstallDir = new File(dataDir, "app");// data/app              mAppLib32InstallDir = new File(dataDir, "app-lib");// data/app-lib              mAsecInternalPath = new File(dataDir, "app-asec").getPath();// data/app-asec              mUserAppDataDir = new File(dataDir, "user");// data/user              mDrmAppPrivateInstallDir = new File(dataDir, "app-private");// data/app-private                sUserManager = new UserManagerService(context, this,mInstallLock, mPackages);  ……</code></pre>    <p>由以上代码可知,首先初始化成员变量来存放数据目录信息,然后创建UserManagerService。</p>    <p>在UserManagerService的构造函数中,创建data/system/users、data/system/users/0目录和data/system/users/userlist.xml文件,然后调用readUserListLocked()方法解析userlist.xml</p>    <h3>2.4.7 解析packages文件</h3>    <pre>  <code class="language-java">public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) {  synchronized (mInstallLock) {          // writer          synchronized (mPackages) {     mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),mSdkVersion, mOnlyCore);</code></pre>    <p style="text-align:center"><img src="https://simg.open-open.com/show/64be6694abc24837c69b9f16dacd207f.jpg"></p>    <p>readLPw流程</p>    <h3>2.4.8 Dexopt优化判定</h3>    <pre>  <code class="language-java">public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) {  synchronized (mInstallLock) {          // writer          synchronized (mPackages) {    final ArraySet<String>alreadyDexOpted = new ArraySet<String>();              final String bootClassPath = System.getenv("BOOTCLASSPATH");  /*BOOTCLASSPATH是Android Linux的一个环境变量,可以在adb shell下用$BOOTCLASSPATH看到。BOOTCLASSPATH即系统核心JAR包的路径,直接加入alreadyDexOpted列表,表示不需要进行dexopt操作  /system/framework/core-libart.jar:/system/framework/conscrypt.jar:/system/framework/okhttp.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/telephony-common.jar:/system/framework/voip-common.jar:/system/framework/ims-common.jar:/system/framework/mms-common.jar:/system/framework/android.policy.jar:/system/framework/apache-xml.jar:/system/framework/mediatek-common.jar:/system/framework/mediatek-framework.jar:/system/framework/mediatek-telephony-common.jar*/              final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");  /*/system/framework/services.jar:/system/framework/ethernet-service.jar:/system/framework/wifi-service.jar*/              if (bootClassPath != null) {                  String[] bootClassPathElements = splitString(bootClassPath, ':');                  for (String element : bootClassPathElements) {                      alreadyDexOpted.add(element);                  }              } else {                  Slog.w(TAG, "No BOOTCLASSPATH found!");              }              if (systemServerClassPath != null) {  ……//同bootClassPath处理流程          }  /*该段代码主要是把BOOTCLASSPATH和SYSTEMSERVERCLASSPATH里面的文件添加到alreadyDexOpted这个HashSet中,因为它们在zygote启动时已经进过Dex优化了。*/              final List<String> allInstructionSets = getAllInstructionSets();//arm arm64              final String[] dexCodeInstructionSets =                  getDexCodeInstructionSets(allInstructionSets.toArray(new String[allInstructionSets.size()]));//arm arm64              /**               * Ensure all external libraries have had dexopt run on them.               */              if (mSharedLibraries.size() > 0) {                  for (String dexCodeInstructionSet : dexCodeInstructionSets) {                      for (SharedLibraryEntry libEntry : mSharedLibraries.values()) {                          final String lib = libEntry.path;                          try {                              int dexoptNeeded = DexFile.getDexOptNeeded(lib, null, dexCodeInstructionSet, false);                              if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {                                  alreadyDexOpted.add(lib);                                  mInstaller.dexopt(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded);                              }                              }                       }   }              File frameworkDir = new File(Environment.getRootDirectory(), "framework");              // Gross hack for now: we know this file doesn't contain any              // code, so don't dexopt it to avoid the resulting log spew.              alreadyDexOpted.add(frameworkDir.getPath() + "/framework-res.apk");              alreadyDexOpted.add(frameworkDir.getPath() + "/mediatek-res/mediatek-res.apk");              File customFrameworkDir = new File("/custom/framework");              alreadyDexOpted.add(customFrameworkDir.getPath() + "/framework-res.apk");              alreadyDexOpted.add(customFrameworkDir.getPath() + "/mediatek-res.apk");              alreadyDexOpted.add(frameworkDir.getPath() + "/core-libart.jar");                String[] frameworkFiles = frameworkDir.list();              if (frameworkFiles != null) {                  // TODO: We could compile these only for the most preferred ABI. We should                  // first double check that the dex files for these commands are not referenced                  // by other system apps.                  for (String dexCodeInstructionSet : dexCodeInstructionSets) {                      for (int i=0; i<frameworkFiles.length; i++) {                          File libPath = new File(frameworkDir, frameworkFiles[i]);                          String path = libPath.getPath();                          // Skip the file if it is not a type we want to dexopt.                          if (!path.endsWith(".apk") && !path.endsWith(".jar")) {                              continue;                          }  //同mSharedLibraries处理流程  ……</code></pre>    <p>system/framework/framework-res.apk 、mediatek-res.apk中没有代码,不需要进行dexopt操作,直接存入alreadyDexOpted</p>    <p>如果system/framework、custom/framework、system/plugin、custom/plugin目录下的APK和JAR包需要优化,调用dexopt流程进行优化</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/76f3e981e9ac2bfe658a765abe3d11ae.jpg"></p>    <p>Dexopt流程</p>    <h3>2.4.8 Dexopt优化判定</h3>    <pre>  <code class="language-java">public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) {  synchronized (mInstallLock) {          // writer          synchronized (mPackages) {    final ArraySet<String>alreadyDexOpted = new ArraySet<String>();              final String bootClassPath = System.getenv("BOOTCLASSPATH");  /*BOOTCLASSPATH是Android Linux的一个环境变量,可以在adb shell下用$BOOTCLASSPATH看到。BOOTCLASSPATH即系统核心JAR包的路径,直接加入alreadyDexOpted列表,表示不需要进行dexopt操作  /system/framework/core-libart.jar:/system/framework/conscrypt.jar:/system/framework/okhttp.jar:/system/framework/core-junit.jar:/system/framework/bouncycastle.jar:/system/framework/ext.jar:/system/framework/framework.jar:/system/framework/telephony-common.jar:/system/framework/voip-common.jar:/system/framework/ims-common.jar:/system/framework/mms-common.jar:/system/framework/android.policy.jar:/system/framework/apache-xml.jar:/system/framework/mediatek-common.jar:/system/framework/mediatek-framework.jar:/system/framework/mediatek-telephony-common.jar*/              final String systemServerClassPath = System.getenv("SYSTEMSERVERCLASSPATH");  /*/system/framework/services.jar:/system/framework/ethernet-service.jar:/system/framework/wifi-service.jar*/              if (bootClassPath != null) {                  String[] bootClassPathElements = splitString(bootClassPath, ':');                  for (String element : bootClassPathElements) {                      alreadyDexOpted.add(element);                  }              } else {                  Slog.w(TAG, "No BOOTCLASSPATH found!");              }              if (systemServerClassPath != null) {  ……//同bootClassPath处理流程          }  /*该段代码主要是把BOOTCLASSPATH和SYSTEMSERVERCLASSPATH里面的文件添加到alreadyDexOpted这个HashSet中,因为它们在zygote启动时已经进过Dex优化了。*/              final List<String> allInstructionSets = getAllInstructionSets();//arm arm64              final String[] dexCodeInstructionSets =                  getDexCodeInstructionSets(allInstructionSets.toArray(new String[allInstructionSets.size()]));//arm arm64              /**               * Ensure all external libraries have had dexopt run on them.               */              if (mSharedLibraries.size() > 0) {                  for (String dexCodeInstructionSet : dexCodeInstructionSets) {                      for (SharedLibraryEntry libEntry : mSharedLibraries.values()) {                          final String lib = libEntry.path;                          try {                              int dexoptNeeded = DexFile.getDexOptNeeded(lib, null, dexCodeInstructionSet, false);                              if (dexoptNeeded != DexFile.NO_DEXOPT_NEEDED) {                                  alreadyDexOpted.add(lib);                                  mInstaller.dexopt(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet, dexoptNeeded);                              }                              }                       }   }              File frameworkDir = new File(Environment.getRootDirectory(), "framework");              // Gross hack for now: we know this file doesn't contain any              // code, so don't dexopt it to avoid the resulting log spew.              alreadyDexOpted.add(frameworkDir.getPath() + "/framework-res.apk");              alreadyDexOpted.add(frameworkDir.getPath() + "/mediatek-res/mediatek-res.apk");              File customFrameworkDir = new File("/custom/framework");              alreadyDexOpted.add(customFrameworkDir.getPath() + "/framework-res.apk");              alreadyDexOpted.add(customFrameworkDir.getPath() + "/mediatek-res.apk");              alreadyDexOpted.add(frameworkDir.getPath() + "/core-libart.jar");                String[] frameworkFiles = frameworkDir.list();              if (frameworkFiles != null) {                  // TODO: We could compile these only for the most preferred ABI. We should                  // first double check that the dex files for these commands are not referenced                  // by other system apps.                  for (String dexCodeInstructionSet : dexCodeInstructionSets) {                      for (int i=0; i<frameworkFiles.length; i++) {                          File libPath = new File(frameworkDir, frameworkFiles[i]);                          String path = libPath.getPath();                          // Skip the file if it is not a type we want to dexopt.                          if (!path.endsWith(".apk") && !path.endsWith(".jar")) {                              continue;                          }  //同mSharedLibraries处理流程  ……</code></pre>    <p>system/framework/framework-res.apk 、mediatek-res.apk中没有代码,不需要进行dexopt操作,直接存入alreadyDexOpted</p>    <p>如果system/framework、custom/framework、system/plugin、custom/plugin目录下的APK和JAR包需要优化,调用dexopt流程进行优化</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/76f3e981e9ac2bfe658a765abe3d11ae.jpg"></p>    <p>Dexopt流程</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/ae9a6a54b2c5b507730e1940da9f803d.png"></p>    <p>dexopt优化后生成内容</p>    <p>如果是升级系统时,则进行如下处理,如果没有进行系统升级,则忽略这段代码。</p>    <pre>  <code class="language-java">public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) {  synchronized (mInstallLock) {            // writer            synchronized (mPackages) {  final VersionInfo ver = mSettings.getInternalVersion();              mIsUpgrade = !Build.FINGERPRINT.equals(ver.fingerprint);//根据fingerprint判断是否是OTA升级              // when upgrading from pre-M, promote system app permissions from install to runtime              mPromoteSystemApps =  mIsUpgrade && ver.sdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1;              // save off the names of pre-existing system packages prior to scanning; we don't              // want to automatically grant runtime permissions for new system apps              if (mPromoteSystemApps) {                  Iterator<PackageSetting> pkgSettingIter = mSettings.mPackages.values().iterator();                  while (pkgSettingIter.hasNext()) {                      PackageSetting ps = pkgSettingIter.next();                      if (isSystemApp(ps)) {                          mExistingSystemPackages.add(ps.name);                      }                  }              }</code></pre>    <h3>2.4.9 调用scanDirLI方法扫描并安装APK包</h3>    <pre>  <code class="language-java">private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,              long currentTime, UserHandle user) throws PackageManagerException {         ……  PackageSetting ps = null;          PackageSetting updatedPkg;          // reader          synchronized (mPackages) {              String oldName = mSettings.mRenamedPackages.get(pkg.packageName);              if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName)) {                  ps = mSettings.peekPackageLPr(oldName);              }              if (ps == null) {                  ps = mSettings.peekPackageLPr(pkg.packageName);              }  /*这里主要是处理应用升级后包名不一致的情况,当设备第一次开机时,不存在这样的情况。其他情况下,开机会解析packages.xml,当前后有apk的包名发生变化时,该app在packages.xml中会以标签标记。而且还会把这些包名更改了的信息计入 PMS的mSettings变量的ArrayMap 类型的变量mRenamedPackages中,key是newname.*/   // Check to see if this package could be hiding/updating a system package.  Must look for it either under the original or real              // package name depending on our state.              updatedPkg = mSettings.getDisabledSystemPkgLPr(ps != null ? ps.name : pkg.packageName);              if (DEBUG_INSTALL && updatedPkg != null) Slog.d(TAG, "updatedPkg = " + updatedPkg);          }          boolean updatedPkgBetter = false;          // First check if this is a system package that may involve an update          if (updatedPkg != null) {  /*处理系统更新后,检查是否对系统app有影响。即是否将系统app更新为更高的新版本了。是的话,要处理。*/  }     private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,              long currentTime, UserHandle user) throws PackageManagerException {          ……           /*A new system app appeared, but we already had a non-system one of thesame name installed earlier.*/          boolean shouldHideSystemApp = false;          if (updatedPkg == null && ps != null                  && (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0 && !isSystemApp(ps)) {              /*Check to make sure the signatures match first. If they don't, wipe the installed application and its data. */              if (compareSignatures(ps.signatures.mSignatures, pkg.mSignatures)!= PackageManager.SIGNATURE_MATCH) {                  deletePackageLI(pkg.packageName, null, true, null, null, 0, null, false);                  ps = null;              } else {                  /*If the newly-added system app is an older version than thealready installed version, hide it. It will be scanned later                   and re-added like an update. */                  if (pkg.mVersionCode <= ps.versionCode) {                      shouldHideSystemApp = true;                  } else {                      /*                       * The newly found system app is a newer version that the one previously installed. Simply remove the                       * already-installed application and replace it with our own while keeping the application data.                       */                      InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),                              ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));                      synchronized (mInstallLock) {                          args.cleanUpResourcesLI();                      }                  }              }          }  /*此处主要处理系统升级后,系统多出一些system app与已安装的非系统包重名的情况*/   ……   if (shouldHideSystemApp) {              synchronized (mPackages) {                  mSettings.disableSystemPackageLPw(pkg.packageName);              }          }  /*如果扫描的系统app需要被隐藏,那么通过mSettings.disableSystemPackageLPw方法将其信息记录在mSettings的mDisabledSysPackages中。*/</code></pre>    <h2>解析一个app有多个apk的情况</h2>    <p>Split APK是Google为解决65536上限,以及APK安装包越来越大等问题,在Android L中引入的机制。</p>    <p>Split APK可以将一个庞大的APK,按屏幕密度,ABI等形式拆分成多个独立的APK,在应用程序更新时,不必下载整个APK,只需单独下载某个模块即可安装更新。</p>    <p>Split APK将原来一个APK中多个模块共享同一份资源的模型分离成多个APK使用各自的资源,并且可以继承Base APK中的资源,多个APK有相同的data,cache目录,多个dex文件,相同的进程,在Settings.apk中只显示一个APK,并且使用相同的包名。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/eb87f99e2a865e59672cf540fd44832b.jpg"></p>    <p>image011.jpg</p>    <p>想了解更多关于Split APK机制,请参考如下文章</p>    <p><a href="/misc/goto?guid=4959741011260503589" rel="nofollow,noindex">Android动态部署一:Google原生Split APK浅析</a><img src="https://simg.open-open.com/show/7aed496887db0c49734062866cbd7d3d.jpg"></p>    <p>解析app时序图1</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/001372f92df218036a24025e153ace80.jpg"></p>    <p>解析app时序图2</p>    <h3>2.4.10 扫描用户安装的app</h3>    <pre>  <code class="language-java">/*处理有升级包的系统应用,也就是执行过OTA升级后,第一次启动时,需要关心的逻辑*/  // Prune any system packages that no longer exist.             final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<String>();             if (!mOnlyCore) {                 Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();                 while (psit.hasNext()) {                     PackageSetting ps = psit.next();                     /* If this is not a system app, it can't be a disable system app. */                     if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {                         continue;//忽略普通应用                     }                     /* If the package is scanned, it's not erased. */                     final PackageParser.Package scannedPkg = mPackages.get(ps.name);                     if (scannedPkg != null) {                // packages.xml中<updated-package>修饰的package会被记录到mSettings中的disable列表中去                          // 这说明扫描的系统app是带有升级包的                         if (mSettings.isDisabledSystemPackageLPr(ps.name)) {                             removePackageLI(ps, true);//将其从mPackages中移除                             mExpectingBetter.put(ps.name, ps.codePath);// 将其添加到mExpectingBetter,后续处理                         }                         continue;                     }                     // 运行到这里说明packages.xml中记录的app,但此时还未扫描到。                     if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {                       // 如果这个app在packages.xml也不属于<updated-package>                       // 意味着这个应用是残留在packages.xml中的,可能还会剩下沙箱数据,因此也要删掉                         psit.remove();                         removeDataDirsLI(null, ps.name);                     } else {                       // 如果这个app在packages.xml属于<updated-package>                       // 将其加入possiblyDeletedUpdatedSystemApps                         final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name);                         if (disabledPs.codePath == null || !disabledPs.codePath.exists()) {                             possiblyDeletedUpdatedSystemApps.add(ps.name);                         }                     }                 }             }  //扫描并删除未成功安装的apk包(针对第三方app)  //look for any incomplete package installations  ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr();  //clean up list  for(int i = 0; i < deletePkgsList.size(); i++) {      cleanupInstallFailedPackage(deletePkgsList.get(i));  }  // 删除临时文件  deleteTempPackageFiles();  // 把从mSettings中没有关联任何应用的SharedUserSetting对象删掉  mSettings.pruneSharedUsersLPw();  //开始扫描用户安装的app,即data/app和data/priv-app  scanDirLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);    scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,          scanFlags | SCAN_REQUIRE_KNOWN, 0);    处理possiblyDeletedUpdatedSystemApps。它里面存储的是在packages.xml中被标记为,但是前面又没有扫描到的其apk文件的app。  for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {      // 在扫描了用户app目录之后,再次尝试查找是否有这些app      PackageParser.Package deletedPkg = mPackages.get(deletedAppName);      mSettings.removeDisabledSystemPackageLPw(deletedAppName);        String msg;      // 依旧没有,那么就删除他们的数据目录      if (deletedPkg == null) {          msg = "Updated system package " + deletedAppName                  + " no longer exists; wiping its data";          removeDataDirsLI(null, deletedAppName);      } else {        // 找到了,说明是在用户app目录中找到的,那么移除系统权限          msg = "Updated system app + " + deletedAppName                  + " no longer present; removing system privileges for "                  + deletedAppName;            deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;            PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);          deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;      }      logCriticalInfo(Log.WARN, msg);  }        //  处理存放到mExpectingBetter是那些带有升级包的系统应用      for (int i = 0; i < mExpectingBetter.size(); i++) {          final String packageName = mExpectingBetter.keyAt(i);          if (!mPackages.containsKey(packageName)) {              final File scanFile = mExpectingBetter.valueAt(i);                logCriticalInfo(Log.WARN, "Expected better " + packageName                      + " but never showed up; reverting to system");                //确保是在              ///system/priv-app、system/app、vendor/app、oem/app这四个目录中。              final int reparseFlags;              if (FileUtils.contains(privilegedAppDir, scanFile)) {                  reparseFlags = PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR                          | PackageParser.PARSE_IS_PRIVILEGED;              } else if (FileUtils.contains(systemAppDir, scanFile)) {                  reparseFlags = PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR;              } else if (FileUtils.contains(vendorAppDir, scanFile)) {                  reparseFlags = PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR;              } else if (FileUtils.contains(oemAppDir, scanFile)) {                  reparseFlags = PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR;              } else {                  Slog.e(TAG, "Ignoring unexpected fallback path " + scanFile);                  continue;              }              // 会将其加入mSettings的mPackages中              mSettings.enableSystemPackageLPw(packageName);              /// 重新扫描这些文件              try {                  scanPackageLI(scanFile, reparseFlags, scanFlags, 0, null);              } catch (PackageManagerException e) {                  Slog.e(TAG, "Failed to parse original system package: "                          + e.getMessage());              }          }      }  }    // 清除mExpectingBetter    mExpectingBetter.clear();</code></pre>    <p>接下来的代码作用是更新所有应用的动态库路径,如果是OTA升级导致前后SDK版本不一致,还要进行权限重新检查,并且删除app oat cache目录。更新数据库版本,调用mSettings.writeLPr更新package.xml、package.list、runtime-permission.xml等文件。</p>    <h3>2.4.11 调用mSettings.writeLPr更新package.xml</h3>    <p style="text-align:center"><img src="https://simg.open-open.com/show/3a4f6c7d858b067fe3118e2efdac5f97.jpg"></p>    <p>ddfefff.jpg</p>    <p>最后创建mInstallerService对象:</p>    <pre>  <code class="language-java">mInstallerService = new PackageInstallerService(context, this);</code></pre>    <p>PMS构造方法的执行过程就是先读取保存在packages.xml中记录的系统关机前记录所有安装的app信息,保存在mSettings中的mPackages中。</p>    <p>然后扫描指定的若干目录中的app,并把信息记录在PMS的mPackages中。最后对两者进行对比,看是否能发现有升级的app,然后进行相关处理,最后在写入packages.xml中。</p>    <h2>2.5 PMS类图补充</h2>    <p style="text-align:center"><img src="https://simg.open-open.com/show/3233e36d8824816211067c2eed1ade47.jpg"></p>    <p>PMS类图补充</p>    <h2>2.6 PMS启动过程中使用的核心组件Installer</h2>    <p>PMS启动过程中使用了Installer的多个方法。Android APK的安装和卸载主要是由Installer和Installd完成的。</p>    <p>Installer是Java层提供的Java API接口,Installd则是init进程启动的Daemon Service。Installer与Installd通过Socket通信,Installer是Socket的Client端,Installd则是Socket的Server端。通过Socket通信,将Installer的API调用转化为Installd中具体命令,这种转化关系通过cmds[]数组配置和映射。Installer和Installd的关系如图所示:</p>    <p><img src="https://simg.open-open.com/show/dbd6897b175d02888a40fd34ce622d95.jpg"></p>    <p>Installer方法映射图</p>    <h2>2.7 Dexopt优化原则</h2>    <p>PMS的启动流程执行完后,将会进入到mPackageManagerService.performBootDexOpt()流程,此处PMS会按一定的顺序对app做dexopt,规则流程如下:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/338be724067e836a8b90ccab261516f9.jpg"></p>    <p>Dexopt优化原则</p>    <h2>2.8 首次开机Runtime-permission生成机制</h2>    <p><img src="https://simg.open-open.com/show/8178efbf201189e0fa7d8b0723a7043a.jpg"></p>    <p>Runtime Permission时序图</p>    <h2>3. APK的安装过程</h2>    <p>当Android存储中的一个apk文件时,实际上是调用了Packageinstaller来完成的。安装过程中具体界面如下:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/a968d4096b6225e1ba8ae31874b01b17.png"></p>    <p>apk安装过程界面</p>    <p>Packageinstaller内部也是对PMS的调用,安装时,会先调用PMS相关接口,解析APK文件,也就是其AndroidMainifest.xml文件,这样就得到了该app的组件,权限,包名等信息。然后以包名为key,检查该app是否已经安装,安装的话,设置replace的flag:INSTALL_REPLACE_EXISTING。如果是之前没安装过的,那么会弹出一个activity,显示该app有哪些权限,底部有两个Button:”取消”和“安装”。点击”安装”,就开始安装了。如果该app之前安装过了,弹出的Activity中会提示:“你要安装此应用的新版本吗?。。。。”,最后还会罗列出新app相比已经安装在设备上的app的权限有哪些变化,比如新添加了哪些权限等等。底部同样会提供两个Button:”取消”和“安装”。点击”安装”,就开始安装了。</p>    <p>当点击”安装”button之后,实际上跳转到PackageInstaller的InstallAppProgress这个activity了。其真正安装开始于</p>    <pre>  <code class="language-java">pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags,          installerPackageName, verificationParams, null);</code></pre>    <p>其代码实现:Android6.0/frameworks/base/core/java/android/app/ApplicationPackageManager.java</p>    <pre>  <code class="language-java">public void installPackageWithVerificationAndEncryption(Uri packageURI,              PackageInstallObserver observer, int flags, String installerPackageName,              VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {          installCommon(packageURI, observer, flags, installerPackageName, verificationParams,                  encryptionParams);      }</code></pre>    <p>内部直接又调用了installCommon方法:</p>    <pre>  <code class="language-java">private void installCommon(Uri packageURI, PackageInstallObserver observer, int flags, String installerPackageName,              VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) {          if (!"file".equals(packageURI.getScheme())) {              throw new UnsupportedOperationException("Only file:// URIs are supported");          }          if (encryptionParams != null) {              throw new UnsupportedOperationException("ContainerEncryptionParams not supported");          }          final String originPath = packageURI.getPath();          try {              mPM.installPackage(originPath, observer.getBinder(), flags, installerPackageName, verificationParams, null);          } catch (RemoteException ignored) {          }      }</code></pre>    <p>做了一系列判断后,接着调用mPM的installPackage方法。mPM就是PMS的一个代理。也就是说这里实际会调用PMS的installPackage方法:</p>    <pre>  <code class="language-java">public void installPackage(String originPath, IPackageInstallObserver2 observer,              int installFlags, String installerPackageName, VerificationParams verificationParams, String packageAbiOverride) {          installPackageAsUser(originPath, observer, installFlags, installerPackageName,                  verificationParams, packageAbiOverride, UserHandle.getCallingUserId());      }</code></pre>    <p>整个安装过程很复杂,大体上可分为三个过程:</p>    <ol>     <li>权限检查</li>     <li>复制文件</li>     <li>装载应用 <h2>3.1 权限检查</h2> </li>    </ol>    <pre>  <code class="language-java">public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,              int installFlags, String installerPackageName, VerificationParams verificationParams, String packageAbiOverride, int userId) {          mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);          //利用binder机制,获取安装发起进程的uid          final int callingUid = Binder.getCallingUid();          enforceCrossUserPermission(callingUid, userId, true, true, "installPackageAsUser");       ……</code></pre>    <p>先检查权限:</p>    <pre>  <code class="language-java">void enforceCrossUserPermission(int callingUid, int userId, boolean requireFullPermission, boolean checkShell, String message) {          if (userId < 0) {              throw new IllegalArgumentException("Invalid userId " + userId);          }          if (checkShell) {              enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);          }          if (userId == UserHandle.getUserId(callingUid)) return;          if (callingUid != Process.SYSTEM_UID && callingUid != 0) {              if (requireFullPermission) {                  mContext.enforceCallingOrSelfPermission(                          android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);              } else {                  try {                      mContext.enforceCallingOrSelfPermission(                              android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);                  } catch (SecurityException se) {                      mContext.enforceCallingOrSelfPermission(                              android.Manifest.permission.INTERACT_ACROSS_USERS, message);                  }              }          }      }</code></pre>    <p>这里的权限检查主要是检查进程是否有权限安装。</p>    <p>继续installPackageAsUser代码:</p>    <pre>  <code class="language-java">public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,              int installFlags, String installerPackageName, VerificationParams verificationParams, String packageAbiOverride, int userId) {          ……..          if (isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {//检查当前系统用户是否具备安装app的权限              try {                  if (observer != null) {                      observer.onPackageInstalled("", INSTALL_FAILED_USER_RESTRICTED, null, null);                  }              } catch (RemoteException re) {              }              return;          }  //如果是发起端进程是shell或者root,那么添加flags:PackageManager.INSTALL_FROM_ADB          if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {               installFlags |= PackageManager.INSTALL_FROM_ADB;          } else {  // 从flags中去掉INSTALL_FROM_ADB和INSTALL_ALL_USERS              installFlags &= ~PackageManager.INSTALL_FROM_ADB;              installFlags &= ~PackageManager.INSTALL_ALL_USERS;          }            UserHandle user; //创建一个当前用户的handle          if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {              user = UserHandle.ALL;          } else {              user = new UserHandle(userId);          }            // Android 6.0 当权限属于运行时权限时,需要弹出框,让用户授权,对于system app,应该取消运行时权限弹框授权,而是直接授权。   // 那么就要在system app中加入INSTALL_GRANT_RUNTIME_PERMISSIONS   // 我们安装第三方app,没有INSTALL_GRANT_RUNTIME_PERMISSIONS          if ((installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0                  && mContext.checkCallingOrSelfPermission(Manifest.permission                  .INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {              throw new SecurityException("You need the "                      + "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "                      + "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");          }          verificationParams.setInstallerUid(callingUid);            //发送INIT_COPY消息</code></pre>    <p>这里主要是对当前用户是否有权限安装app进行检查,以及安装的app是仅仅为当前用户安装,还是给所有的用户安装。从以上代码可以得出,当安装进程是shell或者root时,flags中又包含了INSTALL_ALL_USERS时,才会给所有用户安装,否则大多数情况下,仅仅安装给当前的用户。当我们使用pm命令安装的时候,可以选择安装给哪个用户,也可以是全部用户,就是这个原因。</p>    <pre>  <code class="language-java">final File originFile = new File(originPath);          final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile);          final Message msg = mHandler.obtainMessage(INIT_COPY);          msg.obj = new InstallParams(origin, null, observer, installFlags, installerPackageName,null, verificationParams, user, packageAbiOverride, null);          mHandler.sendMessage(msg);</code></pre>    <p>构造InstallParams,注意packageAbiOverride为null,然后利用Android中的Handler机制,发送给相关的线程进行安装。</p>    <p>installPackageAsUser整个执行逻辑如下图所示所示。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/79a419a40fd39d2984e1f44ca0c40340.jpg"></p>    <p>installPackageAsUser流程</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/3cc6881654c8dd66977ccf76ef7b2da5.jpg"></p>    <p>ccc.jpg</p>    <p>.jpg</p>    <h2>3.2 复制文件</h2>    <p>前面发送了INIT_COPY消息,接下来看如何处理:</p>    <pre>  <code class="language-java">void doHandleMessage(Message msg) {              switch (msg.what) {                  case INIT_COPY: {                      HandlerParams params = (HandlerParams) msg.obj;                      int idx = mPendingInstalls.size();                      if (!mBound) {                          //将绑定DefaultContainerService服务                          if (!connectToService()) {                              Slog.e(TAG, "Failed to bind to media container service");                              params.serviceError();                              return;                          } else {                              mPendingInstalls.add(idx, params);                          }                      }               ……                      break;                  }</code></pre>    <p>INIT_COPY消息的处理中将绑定DefaultContainerService,因为这是一个异步的过程,要等待的绑定的结果通过onServiceConnected()返回,所以这里就将安装的参数信息放到了mPendingInstalls列表中,如果这个Service之前就绑定好了,现在就不要再次绑定了,安装信息同样要放到mPendingInstalls中。如果有多个安装请求同时到达,就可以通过mPendingInstalls列表对它们进行排队。如果列表中只有一项,说明没有更多的安装请求,因此这种情况下,需要立即发出MCS_BOUND消息,进入下一步的处理。</p>    <pre>  <code class="language-java">private boolean connectToService() {              //("com.android.defcontainer", "com.android.defcontainer.DefaultContainerService")              Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);              Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);              if (mContext.bindServiceAsUser(service, mDefContainerConn, Context.BIND_AUTO_CREATE, UserHandle.OWNER)) {                  Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);                  mBound = true;                  //此处为MTK为解决adb install cannot response after run MTK-MTBF for a period of time而添加。当绑定未成功时,再重新绑定3次                  final long DEFCONTAINER_CHECK = 1 * 1000;                  final Message msg = mHandler.obtainMessage(MCS_CHECK);                  mHandler.sendMessageDelayed(msg, DEFCONTAINER_CHECK);                  return true;              }              Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);              return false;          }        final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();      class DefaultContainerConnection implements ServiceConnection {          public void onServiceConnected(ComponentName name, IBinder service) {              mServiceConnected = true;              mServiceCheck = 0;              //此处返回的service为DefaultContainerService中定义的IMediaContainerService.Stub,使用了AIDL进行的进程间通信。              //其定义于:frameworks/base/core/java/com/android/internal/app/IMediaContainerService.aidl              IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service);              mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));          }        ……</code></pre>    <p>可以看到当绑定成功后在onServiceConnected中将一个IBinder转换成了一个IMediaContainerService.这个就是在onServiceConnected回调函数中根据参数传进来的IMediaContainerService.Stub的对象引用创建的一个远程代理对象。以后PMS务通过该代理对象访问DefaultContainerService服务。</p>    <p>接下来分析MCS_BOUND消息:</p>    <pre>  <code class="language-java">void doHandleMessage(Message msg) {              switch (msg.what) {             ……                  case MCS_BOUND: {                      if (msg.obj != null) {                          mContainerService = (IMediaContainerService) msg.obj;                      }                      if (mContainerService == null) {                          if (!mBound) {                              for (HandlerParams params : mPendingInstalls) {                                  params.serviceError();                              }                              mPendingInstalls.clear();                          } else {                              Slog.w(TAG, "Waiting to connect to media container service");                          }                      } else if (mPendingInstalls.size() > 0) {                          HandlerParams params = mPendingInstalls.get(0);                          if (params != null) {                              if (params.startCopy()) {                                  if (mPendingInstalls.size() > 0) {                                      mPendingInstalls.remove(0);                                  }                                  if (mPendingInstalls.size() == 0) {                                      if (mBound) {                                          removeMessages(MCS_UNBIND);                                          Message ubmsg = obtainMessage(MCS_UNBIND);                                          sendMessageDelayed(ubmsg, 10000);                                      }                                  } else {                                      mHandler.sendEmptyMessage(MCS_BOUND);                                  }                              }                          }                      break;                  }</code></pre>    <p>MCS_BOUND消息的处理过程就是调用InstallParams类的startCopy()方法来执行拷贝操作。只要mPendingInstalls中还有安装信息,就会重复发送MCS_BOUND消息,直到所有的应用都安装完毕,然后在发送一个延时10秒的MCS_UNBIND消息。</p>    <pre>  <code class="language-java">case MCS_UNBIND: {                      if (mPendingInstalls.size() == 0 && mPendingVerification.size() == 0) {                          if (mBound) {                              disconnectService();                          }                      } else if (mPendingInstalls.size() > 0) {                          mHandler.sendEmptyMessage(MCS_BOUND);                      }                      break;                  }</code></pre>    <p>MCS_UNBIND消息的处理就简单了,当mPendingInstalls中没有安装信息的时候,就调用disconnectService断开与DefaultContainerService的连接。如果发现还有安装信息,则继续发送MCS_BOUND消息。</p>    <p>接下来分析真正的拷贝方法:startCopy</p>    <pre>  <code class="language-java">final boolean startCopy() {              boolean res;              try {                  if (++mRetries > MAX_RETRIES) {                      mHandler.sendEmptyMessage(MCS_GIVE_UP);                      handleServiceError();                      return false;                  } else {                      handleStartCopy();                      res = true;                  }              } catch (RemoteException e) {                  mHandler.sendEmptyMessage(MCS_RECONNECT);                  res = false;              }              handleReturnCode();              return res;          }</code></pre>    <p>startCopy()方法通过调用其子类InstallParams的handleStartCopy()来完成拷贝操作。考虑到安装过程的不确定性,startCopy主要工作是进行错误处理,当捕获到handleStartCopy跑出的异常时,startCopy将发送MCS_RECONNECT.在MCS_RECONNECT消息的处理中,将会重新绑定DefaultContainerService,如果绑定成功,那么安装过程将会重新开始。startCopy也将会再次被调用,重试的次数记录在mRetries中,当累计重试超过4次时,安装将失。如果安装失败,那么startCopy将会调用handleReturnCode()来继续处理。</p>    <p>handleStartCopy操作流程如下:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/b2768c8d99fac240a899d8038798c559.jpg"></p>    <p>InstallParms.handleStartCopy流程</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/3cc6881654c8dd66977ccf76ef7b2da5.jpg"></p>    <p>InstallParams和InstallArgs关系</p>    <p>createInstallArgs传入的params,在本例中就是InstallParams,在它的handleStartCopy()中已经确定了安装在哪里。</p>    <pre>  <code class="language-java">private InstallArgs createInstallArgs(InstallParams params) {          if (params.move != null) {              return new MoveInstallArgs(params);//移动app          } else if (installOnExternalAsec(params.installFlags) || params.isForwardLocked()) {              return new AsecInstallArgs(params);//安装在SD卡          } else {              return new FileInstallArgs(params);//安装在内部存储          }      }</code></pre>    <p>此处我们是安装在内部存储的,所以创建的就是FileInstallArgs了,那么调用的copyApk,自然就是FileInstallArgs的了。FileInstallArgs. copyApk主要是对apk内容进行拷贝,其数据流如下:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/4034f15a9f747d8d3258df07ce240df6.jpg"></p>    <p>数据流向</p>    <h2>3.3 装载应用</h2>    <p>主要完成将dex转换为ART虚拟机的oat格式的执行文件,并为应用创建数据沙箱目录,最后把应用的信息装载进PMS的数据结构中去。</p>    <p>在前面的处理MCS_BOUND时调用的HandlerParams的startCopy方法中当复制完文件之后,会调用InstallParams的handleReturnCode方法:</p>    <pre>  <code class="language-java">void handleReturnCode() {              if (mArgs != null) {                  processPendingInstall(mArgs, mRet);              }          }        private void processPendingInstall(final InstallArgs args, final int currentStatus) {           mHandler.post(new Runnable() {              public void run() {                  mHandler.removeCallbacks(this);                  PackageInstalledInfo res = new PackageInstalledInfo();                  res.returnCode = currentStatus;                  res.uid = -1;                  res.pkg = null;                  res.removedInfo = new PackageRemovedInfo();                  if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {                      args.doPreInstall(res.returnCode);                      synchronized (mInstallLock) {                          installPackageLI(args, res);                      }                      args.doPostInstall(res.returnCode, res.uid);                  }             //省略备份代码                  if (!doRestore) {                      Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);                      mHandler.sendMessage(msg);                  }              }          });      }</code></pre>    <p>processPendingInstall()方法中post了一个消息,这样安装过程将以异步的方式继续执行。在post消息中,首先是调用installPackageLI()来装载应用,接下来的一大段代码是在执行设备备份操作,备份是通过BackupManagerService来完成的,这里就不分析了。备份完成之后,通过发送POST_INSTALL消息继续处理。</p>    <p>接着看installPackageLI()方法</p>    <pre>  <code class="language-java">private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {          final int installFlags = args.installFlags;//得到installFlags,里面记录了app需要安装到哪里          final String installerPackageName = args.installerPackageName; // 安装程序的包名          final String volumeUuid = args.volumeUuid; // 与sd卡安装有关,一般为null          final File tmpPackageFile = new File(args.getCodePath());// 前面已经把apk拷贝到了临时阶段性文件夹/data/app/vmdl<安装回话id>.tmp/这个目录了          final boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0);          final boolean onExternal = (((installFlags & PackageManager.INSTALL_EXTERNAL) != 0)                  || (args.volumeUuid != null)); // 是否安装到外部存储          boolean replace = false; // 初始化替换flag          int scanFlags = SCAN_NEW_INSTALL | SCAN_UPDATE_SIGNATURE;          if (args.move != null) {              // moving a complete application; perfom an initial scan on the new install location              scanFlags |= SCAN_INITIAL;          }          res.returnCode = PackageManager.INSTALL_SUCCEEDED;            final int parseFlags = mDefParseFlags | PackageParser.PARSE_CHATTY| (forwardLocked ? PackageParser.PARSE_FORWARD_LOCK : 0)  | (onExternal ? PackageParser.PARSE_EXTERNAL_STORAGE : 0);          PackageParser pp = new PackageParser();          pp.setSeparateProcesses(mSeparateProcesses);          pp.setDisplayMetrics(mMetrics);            final PackageParser.Package pkg;          try {              //解析APK,也就是解析AndroidMainifest.xml文件,将结果记录在PackageParser.Package中               pkg = pp.parsePackage(tmpPackageFile, parseFlags);          } catch (PackageParserException e) {              res.setError("Failed parse during installPackageLI", e);              return;          }            // Mark that we have an install time CPU ABI override.          pkg.cpuAbiOverride = args.abiOverride;          ……          try {//收集apk的签名信息              pp.collectCertificates(pkg, parseFlags);              pp.collectManifestDigest(pkg);          } catch (PackageParserException e) {              res.setError("Failed collect during installPackageLI", e);              return;          }          //如果安装程序此前传入了一个清单文件,那么将解析到的清单文件与传入的进行对比。安装器的确传入了一个清单,PackageInstallerActivity中也解析了apk,那时记录了这个清单,并一并传入到这里了。这里又做了一步判断,判断两者是同一个apk.           if (args.manifestDigest != null) {              if (!args.manifestDigest.equals(pkg.manifestDigest)) {                  res.setError(INSTALL_FAILED_PACKAGE_CHANGED, "Manifest digest changed");                  return;              }          } else if (DEBUG_INSTALL) {              final String parsedManifest = pkg.manifestDigest == null                      ? "null" : pkg.manifestDigest.toString();              Slog.d(TAG, "manifestDigest was not present, but parser got: " + parsedManifest);          }            synchronized (mPackages) {              // Check if installing already existing package              if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {              ……//处理安装已经存在的应用,将replace置为true                }          // 如果ps不为null,同样说明,已经存在一个同包名的程序被安装,也就是还是处理覆盖安装的情况          // 这里主要是验证包名的签名,不一致的话,是不能覆盖安装的,另外版本号也不能比安装的低,否则也不能安装             PackageSetting ps = mSettings.mPackages.get(pkgName);              if (ps != null) {                  if (shouldCheckUpgradeKeySetLP(ps, scanFlags)) {                      if (!checkUpgradeKeySetLP(ps, pkg)) {                          res.setError(INSTALL_FAILED_UPDATE_INCOMPATIBLE, "Package " + pkg.packageName + " upgrade keys do not match the "+ "previously installed version");                          return;                      }                  } else {                      try {                          verifySignaturesLP(ps, pkg);                      } catch (PackageManagerException e) {                          res.setError(e.error, e.getMessage());                          return;                      }                  }             ……              }              //检查apk中定义的所有的权限是否已经被其他应用定义了,如果重定义的是系统应用定义的权限,那么忽略本app定义的这个权限。如果重定义的是非系统应用的权限,那么本次安装就以失败返回              int N = pkg.permissions.size();              for (int i = N-1; i >= 0; i--) {              }         }            if (args.move != null) {              // 移动app走该分支          } else if (!forwardLocked && !pkg.applicationInfo.isExternalAsec()) {              scanFlags |= SCAN_NO_DEX;              try {                  derivePackageAbi(pkg, new File(pkg.codePath), args.abiOverride, true);//设置apk的so库路径,以及主次abi的值              } catch (PackageManagerException pme) {                  return;              }           //实际为dex2oat操作,用来将apk中的dex文件转换为oat文件。需要注意的是:           //Android M中,/data/dalvik_cache/只存放系统内置应用的oat文件,           //用户安装的app的oat文件在,最终会在/data/app/包名/oat/<isa>/              int result = mPackageDexOptimizer.performDexOpt(pkg, null /* instruction sets */, false /* forceDex */,                              false /* defer */, false /* inclDependencies */);              if (result == PackageDexOptimizer.DEX_OPT_FAILED) {                  res.setError(INSTALL_FAILED_DEXOPT, "Dexopt failed for " + pkg.codePath);                  return;              }          }          //重命名,将/data/app/vmdl<安装会话id>.tmp重命名为/data/app/包名-suffix  suffix为1,2……          if (!args.doRename(res.returnCode, pkg, oldCodePath)) {              res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");              return;          }            startIntentFilterVerifications(args.user.getIdentifier(), replace, pkg);            if (replace) {//覆盖安装              replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user,                      installerPackageName, volumeUuid, res);          } else {//首次安装              installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,                      args.user, installerPackageName, volumeUuid, res);          }</code></pre>    <p>执行完installPackageLI之后,返回processPendingInstall方法中,继续发送POST_INSTALL消息继续处理。代码如下:</p>    <pre>  <code class="language-java">sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, extras, null, null, firstUsers);         final boolean update = res.removedInfo.removedPackage != null;         if (update) {              extras.putBoolean(Intent.EXTRA_REPLACING, true);         }         sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, extras, null, null, updateUsers);         if (update) {              sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED, packageName, extras, null, null, updateUsers);              sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED, null, null, packageName, null, updateUsers);         }</code></pre>    <p>该消息的处理主要就是在发送广播,应用安装完成之后要通知系统中的其他应用开始处理,比如在launcher需要增加app的图标等。等发完广播,安装也就结束了,最后通过最初安装是传入的安装观察者observer返回最初的调用者。</p>    <h2>4. PMS问题调试方法</h2>    <h2>4.1 如何打开PMS的log开关</h2>    <p>方法一</p>    <ul>     <li>adb shell dumpsys package log a on</li>     <li>只对当次开机有效</li>    </ul>    <p>方法二</p>    <ul>     <li>PackageManagerService.java中修改DEBUG_XXX为true</li>     <li>长久有效</li>    </ul>    <h2>4.2 pm指令的使用</h2>    <p>利用adb shell命令,进入Android设备的终端,pm工具在/system/bin中,所以可以直接使用:pm <cmd></p>    <h2>包名信息查询</h2>    <pre>  <code class="language-java">pm list packages [options] [FILTER]</code></pre>    <p>打印所有的已经安装的应用的包名,如果设置了文件过滤则值显示包含过滤文字的内容.</p>    <p>参数:</p>    <p>-f 显示每个包的文件位置</p>    <p>-d 使用过滤器,只显示禁用的应用的包名</p>    <p>-e 使用过滤器,只显示可用的应用的包名</p>    <p>-s 使用过滤器,只显示系统应用的包名</p>    <p>-3 使用过滤器,只显示第三方应用的包名</p>    <p>-i 查看应用的安装者</p>    <h2>权限信息查询</h2>    <p>打印所有已知的权限组</p>    <pre>  <code class="language-java">pm list permission-groups</code></pre>    <p>打印权限:</p>    <pre>  <code class="language-java">pm list permissions [options] [GROUP]</code></pre>    <p>参数:</p>    <p>g 按组进行列出权限</p>    <p>-f 打印所有信息</p>    <p>-s 简短的摘要</p>    <p>-d 只有危险的权限列表</p>    <p>-u 只有权限的用户将看到列表用户自定义权限</p>    <p>Android 6.0之后,允许授权和取消权限:</p>    <pre>  <code class="language-java">pm grant <package_name> <permission>   pm revoke <package_name> <permission></code></pre>    <p>授权和取消是针对APK中申请的权限的来说的。即APK中没有申请的权限,是没办法通过此命令添加的。</p>    <h2>包路径</h2>    <pre>  <code class="language-java">pm path package_name</code></pre>    <h2>系统硬件特性</h2>    <pre>  <code class="language-java">pm list features</code></pre>    <h2>设备依赖的java库</h2>    <pre>  <code class="language-java">pm list libraries</code></pre>    <h2>dump包信息</h2>    <pre>  <code class="language-java">pm dump package_name</code></pre>    <h2>安装与卸载apk</h2>    <p>安装apk</p>    <pre>  <code class="language-java">pm install [-lrtsfd] [-i PACKAGE] [PATH]</code></pre>    <p>adb install实际上就是对pm install的封装调用。</p>    <p>参数:</p>    <p>-l 锁定应用程序</p>    <p>-r 重新安装应用,且保留应用数据</p>    <p>-t 允许测试apk被安装</p>    <p>-i INSTALLER_PACKAGE_NAME 指定安装包的包名</p>    <p>-s 安装到sd卡</p>    <p>-f 安装到系统内置存储中(默认安装位置)</p>    <p>-d 允许降级安装(同一应用低级换高级)</p>    <p>-g 授予应用程序清单中列出的所有权限(只有6.0系统可用)</p>    <p>卸载apk:</p>    <pre>  <code class="language-java">pm uninstall [options] <PACKAGE></code></pre>    <p>参数:</p>    <p>-k 卸载应用且保留数据与缓存(如果不加-k则全部删除)</p>    <h2>清除应用数据</h2>    <pre>  <code class="language-java">pm clear package_name</code></pre>    <h2>禁用和启用系统应用</h2>    <pre>  <code class="language-java">pm enable <PACKAGE_OR_COMPONENT>   使package或component可用  pm disenable <PACKAGE_OR_COMPONENT>    使package或component不可用(直接就找不到应用了)  pm disenable-user [options] <PACKAGE_OR_COMPONENT> 使package或component不可用(会显示已停用)</code></pre>    <h2>隐藏与恢复应用</h2>    <pre>  <code class="language-java">pm hide PACKAGE_OR_COMPONENT  pm unhide PACKAGE_OR_COMPONENT</code></pre>    <p>被隐藏应用在应用管理中变得不可见,桌面图标也会消失</p>    <h2>设置和查看应用的安装位置</h2>    <pre>  <code class="language-java">pm set-install-location package_name  pm get-install-location package_name</code></pre>    <p>参数:</p>    <p>0:自动-让系统决定最好的位置</p>    <p>1:内部存储-安装在内部设备上的存储</p>    <p>2:外部存储-安装在外部媒体</p>    <h2>查看当前系统user信息</h2>    <pre>  <code class="language-java">pm list users</code></pre>    <p>可以指的apk安装在某个user下,这样只有切换到该user时,才能显示和使用该apk。</p>    <p> </p>    <p>来自:http://www.jianshu.com/p/6769d1a9f4ad</p>    <p> </p>