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

Android的应用管理主要是通过PackageManagerService来完成的。PackageManagerService服务负责各种APK包的安装、卸载、优化和查询。

PackageManagerService在启动时会扫描所有的APK文件和Jar包,然后把他们的信息读取出来,保存在内存中,这样系统在运行时就能迅速找到各种应用和组件的信息。扫描过程中如果遇到没有优化的文件,还要执行转换工作,将app文件从dex格式转换为oat格式(Android5.0之前是转换为odex格式)。

启动后,PackageManagerService将提供安装包的信息查询服务以及应用的安装和卸载服务。Android5.x,/data/app目录或者/system/app目录下存放的是以应用名称命名的目录,在这个目录下存放的是apk文件和一个lib目录,lib目录存放的是应用的so文件。

Android中的应用可以简单的分为两大类:系统应用和普通应用。

系统应用是指位于/system/app或者/system/priv-app目录下的应用。priv-app目录中存放的是一些系统底层应用,如setting,systemui等。/system/app存放的则是系统级的应用,如Phone、Contacts等。在PackageManagerService中,所有的system应用包括这两个目录下的应用,而所谓的private应用特指priv-app目录下的应用。

普通应用:用户安装的应用,位于目录/data/app下。普通应用还可以按照在SD卡上,系统应用不可以。

通常情况下,系统应用是不能删除的,但是可以升级。升级的方法是安装一个包名相同,但是具有更高版本号的应用在/data/app目录下。对于系统中的这种升级情况,Android会在/data/system/packages.xml文件中用标签<update-package>记录被覆盖的系统应用的信息。如果在升级过程中,如果出现与原始包名不同的应用,最后会改成原始包名,Android会在packages.xml文件中用标签<rename-package>记录这种改名的情况。

系统目录/data/dalvik-cache下保存的是大部分的apk文件和jar包的odex版本。odex是一种优化过的格式,执行速度比apk文件的dex格式更快。

每个应用都有保存数据的目录,位于/data/data/<包名>/目录下。其中数据目录下常见的两个子目录:shared_prefs目录中保存的是应用的设置文件,database保存的是应用的数据库文件。

一、了解PackageManagerService

在应用中如果使用PackageManagerService服务,通常调用的是Context的getPackageManager()方法,这个方法返回的是PackageManager对象,注意Context是一个抽象类,ContextImpl类继承了Context,方法如下:

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1. @Override  
  2. public PackageManager getPackageManager() {  
  3.     if (mPackageManager != null) {  
  4.         return mPackageManager;  
  5.     }  
  6.   
  7.     IPackageManager pm = ActivityThread.getPackageManager();  
  8.     if (pm != null) {  
  9.         // Doesn’t matter if we make more than one instance.  
  10.         return (mPackageManager = new ApplicationPackageManager(this, pm));  
  11.     }  
  12.   
  13.     return null;  
  14. }  


从getPackageManager()方法的代码中可以看到它返回的PackageManager对象实际上是一个ApplicationPackageManager对象,这个对象创建时使用了IPackageManager对象作为参数。IPackageManager对象是一个PackageManagerService的引用对象。因此,ApplicationPackageManager对象就是PackageManagerService的代理对象。ApplicationPackageManager类继承自PackageManager类。PackageManager类中定义了应用可以操作PackageManagerService的接口。

PackageManagerService的两个重要成员变量mInstallerService和mInstller与应用安装有密切关系。mInstallerService是类PackageInstallerService的实例对象,一个应用的安装过程比较长,Android5.0中新增加了PackageInstallerService来管理应用的安装过程。另一个成员变量mInstaller是类Installer的实例对象,它也有一个名为mInstaller的成员变量,其类型是InstallerConnection,而InstallerConnection中存在着和Deamon进程Installd通信的Socket命令通道。实际上系统中进行apk文件格式转换、建立数据目录等工作最后都是由installd进程来完成的。这些对象之间的关系如下图所示。

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

1、理解Packages.xml和Settings类

这里先看一下Settings这个类,这个类用来保存和PackageManagerService相关的一些设置,它所保存的内容在解析应用时会用到。那么Settings中保存的值是从哪里来的?作用又是什么呢?我们先看一下Settings的构造方法,代码如下:

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1. Settings(Context context) {  
  2.     this(context, Environment.getDataDirectory());//获取用户数据目录:/data  
  3. }  

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1. Settings(Context context, File dataDir) {  
  2.     mSystemDir = new File(dataDir, “system”);  
  3.     mSystemDir.mkdirs();//在data目录下创建system目录:/data/system  
  4.     FileUtils.setPermissions(mSystemDir.toString(),  
  5.             FileUtils.S_IRWXU|FileUtils.S_IRWXG  
  6.             |FileUtils.S_IROTH|FileUtils.S_IXOTH,  
  7.             –1, –1);//设置目录的属性为0775  
  8.     mSettingsFilename = new File(mSystemDir, “packages.xml”);//data/system/packages.xml  
  9.     mBackupSettingsFilename = new File(mSystemDir, “packages-backup.xml”);  
  10.     mPackageListFilename = new File(mSystemDir, “packages.list”);  
  11.     FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID);  
  12.   
  13.     // Deprecated: Needed for migration  
  14.     mStoppedPackagesFilename = new File(mSystemDir, “packages-stopped.xml”);  
  15.     mBackupStoppedPackagesFilename = new File(mSystemDir, “packages-stopped-backup.xml”);  
  16. }  


Settings的构造方法会创建data目录下的system目录,这个目录用来保存很多系统文件。主要的工作是创建了5个位于目录/data/system下的File对象,分别是:

  • packages.xml:记录系统中所有安装的应用信息,包括基本信息、签名和权限。
  • packages-backup.xml:packages.xml文件的备份。
  • packages.list:保存普通应用的数据目录和uid等信息。
  • packages-stopped.xml:记录系统中被强制停止运行的应用信息。系统在强制停止某个应用时,会讲应用的信息记录到该文件中。
  • packages-stopped-backup.xml:pacakges-stopped.xml文件的备份。


这5个文件中packages-backup.xml和packages-stopped-backup.xml是备份文件。当Android对文件packages.xml和packages-stopped.xml写之前,会先把他们备份,如果写文件成功了,再把备份文件删除。如果写的时候出问题了,重启后再需要读取这两个文件时,如果发现备份文件存在,会使用备份文件的内容,因为原文件可能损坏了。

packages.xml是PackageManagerService启动时需要用的文件,先看下文件的内容:

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1. <package name=“com.dianping.v1”  
  2.        codePath=“/system/presetapp/BY_3RD_Dianping.apk”  
  3.        nativeLibraryPath=“/data/app-lib/BY_3RD_Dianping”   
  4.        primaryCpuAbi=“armeabi-v7a”   
  5.        flags=“1621572” ft=“14de3458a58” it=“14de3458a58” ut=“14de3458a58”   
  6.        version=“711” userId=“10091”>  
  7.     <sigs count=“1”>  
  8.         <cert index=“5” key=“3082025f308201c8a00……” />  
  9.     </sigs>  
  10.     <perms>  
  11.         <item name=“android.permission.WRITE_SETTINGS” />  
  12.         <item name=“android.permission.READ_SMS” />  
  13.         <item name=“android.permission.ACCESS_FINE_LOCATION” />  
  14.         ……  
  15.     </perms>  
  16.     <proper-signing-keyset identifier=“9” />  
  17.     <signing-keyset identifier=“9” />  
  18. </package>  


上面是文件的一个片段,通过标签<package>记录了一个应用的基本信息,签名和申明的权限。

标签<package>中的属性name表示应用的包名,codePath表示apk文件的位置,nativeLibraryPath表示应用的native库的存储路径;flags表示应用的属性,如FLAG_SYSTEM等;it表示应用安装的时间;ut表示应用最后一次修改的时间;version表示应用的版本号;userId表示应用所属的用户ID。

便签<sigs>表示应用的签名,属性count表示标签中包含有多少个证书。cert表示具体的证书的值。

标签<perms>表示应用声明使用的权限,每个子标签<item>代表一项权限。

<package>标签解析后将保存在PackageSetting对象中。系统中实际运行的应用信息会通过标签<package>记录,被升级包覆盖的系统应用不通过<package>标签记录,而是通过<update-packages>标签记录。这两个标签的记录基本一致。这两个标签的应用信息都将保存在PackageSetting对象中。类PackageSetting的继承关系如图。

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

PackageSetting类继承了PackageSettingBase类,PackageSettingBase类又继承了GrantedPermissions类,应用的基本信息保存在PackageSettingBase类的成员变量中,申明的权限保存在GrantedPremissions类,签名则保持在SharedUserSetting类的成员变量signatures中。

类SharedUserSetting用来描述具有相同sharedUserId的应用信息,它的成员变量packages保存了所有具有相同sharedUserId的应用信息的引用。这些应用的签名是相同的,所以只需在成员变量signatures中保存一份。通过这个对象,Android运行时很容易检索到和某个应用拥有相同sharedUserId的其他应用。

标签<package>所标识的应用的PackageSetting对象都保存在Settings的成员变量mPackages中,标签<update-packages>所标识的系统应用的PackageSetting对象都保存在Settings的成员变量mDisabledSysPackages中,定义如下:

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1. private final ArrayMap<String, PackageSetting> mPackages = new ArrayMap<String, PackageSetting>();  

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1. private final ArrayMap<String, PackageSetting> mDisabledSysPackages = new ArrayMap<String, PackageSetting>();  

package.xml文件中出了标签<package>、<updated-package>,还有两个标签<cleaning-package>和<renamed-package>。Settings中解析这些标签的代码如下:

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1. if (tagName.equals(“package”)) {  
  2.     readPackageLPw(parser);  
  3. else if (tagName.equals(“permissions”)) {  
  4.     readPermissionsLPw(mPermissions, parser);  
  5. else if (tagName.equals(“updated-package”)) {  
  6.     readDisabledSysPackageLPw(parser);  
  7. else if (tagName.equals(“cleaning-package”)) {  
  8.     ……  
  9.         addPackageToCleanLPw(new PackageCleanItem(userId, name, andCode));  
  10.     }  
  11. else if (tagName.equals(“renamed-package”)) {  
  12.    ……  
  13.         mRenamedPackages.put(nname, oname);  


标签<cleaning-package>用来记录哪些已经删除,但是数据目录还暂时保留的应用的信息。扫描后生成的对象是PackageCleanItem,放入mSettings的mPackagesToBeCleaned数组中管理。

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1. // Packages that have been uninstalled and still need their external  
  2. // storage data deleted.  
  3. final ArrayList<PackageCleanItem> mPackagesToBeCleaned = new ArrayList<PackageCleanItem>();  


标签<renamed-package>记录了系统中改名应用的新旧包名,它记录的信息都保存到mSettings的mRenamedPackaged对象中,定义如下:

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1. final ArrayMap<String, String> mRenamedPackages = new ArrayMap<String, String>();  

2、服务的初始化过程

PackageManagerService也是在SystemServer中开始初始化的,代码如下:

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1. mPackageManagerService = PackageManagerService.main(mSystemContext, installer,  
  2.                 mFactoryTestMode != FactoryTest.FACTORY_TEST_OFF, mOnlyCore);  
  3. ……  
  4. try {  
  5.             mPackageManagerService.performBootDexOpt();  
  6.         } catch (Throwable e) {  
  7.             reportWtf(“performing boot dexopt”, e);  
  8.         }  
  9. ……  
  10. try {  
  11.             mPackageManagerService.systemReady();  
  12.         } catch (Throwable e) {  
  13.             reportWtf(“making Package Manager Service ready”, e);  
  14.         }  


SystemServer对PMS的初始化主要是通过上面代码完成的。首先看下PMS对象的创建,main()函数的代码如下:

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1. public static final PackageManagerService main(Context context, Installer installer,  
  2.         boolean factoryTest, boolean onlyCore) {  
  3.     PackageManagerService m = new PackageManagerService(context, installer,  
  4.             factoryTest, onlyCore);  
  5.     ServiceManager.addService(“package”, m);  
  6.     return m;  
  7. }  


main函数比较简单,只是创建PMS对象并在ServiceManager中注册。

PMS的构造方法主要完成两件事,第一是把系统中的apk文件和jar包从dex格式转换成ART的oat格式;第二是扫描系统中所有安装的应用,把他们的信息提取出来。

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1. public PackageManagerService(Context context, Installer installer,  
  2.         boolean factoryTest, boolean onlyCore) {  
  3.     EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_START,  
  4.             SystemClock.uptimeMillis());  
  5.   
  6.     if (mSdkVersion <= 0) {//检查SDK版本  
  7.         Slog.w(TAG, “**** ro.build.version.sdk not set!”);  
  8.     }  
  9.   
  10.     Object bridgeObject;  
  11.   
  12.     try {  
  13.   
  14.         /* 
  15.          * load and create the security bridge:com.android.services.SecurityBridge.core.PackageManagerSB 
  16.          */  
  17.         bridgeObject = getClass().getClassLoader().loadClass(SECURITY_BRIDGE_NAME).newInstance();  
  18.         mSecurityBridge = (PackageManagerMonitor)bridgeObject;  
  19.   
  20.     } catch (Exception e){  
  21.         Slog.w(TAG, “No security bridge jar found, using default”);  
  22.         mSecurityBridge = new PackageManagerMonitor();  
  23.     }  
  24.   
  25.     mContext = context;  
  26.     mFactoryTest = factoryTest;//设置运行模式。工厂模式是一种测试模式  
  27.     mOnlyCore = onlyCore;//onlyCore为true表示只处理系统的应用,通常为false  
  28.     mLazyDexOpt = “eng”.equals(SystemProperties.get(“ro.build.type”));  
  29.     mMetrics = new DisplayMetrics();//DisplayMetrics对象存储屏幕的显示信息  
  30.     mSettings = new Settings(context);//新创建一个settings对象  
  31.     mSettings.addSharedUserLPw(“android.uid.system”, Process.SYSTEM_UID,  
  32.             ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);  
  33.     mSettings.addSharedUserLPw(“android.uid.phone”, RADIO_UID,  
  34.             ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);  
  35.     mSettings.addSharedUserLPw(“android.uid.log”, LOG_UID,  
  36.             ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);  
  37.     mSettings.addSharedUserLPw(“android.uid.nfc”, NFC_UID,  
  38.             ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);  
  39.     mSettings.addSharedUserLPw(“android.uid.bluetooth”, BLUETOOTH_UID,  
  40.             ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);  
  41.     mSettings.addSharedUserLPw(“android.uid.shell”, SHELL_UID,  
  42.             ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);  
  43.     //上面添加SharedUserSetting对象到mSettings中,sharedUserId属性相同的包可以运行在同一个进程中,或者相互读取资源。这里添加了6中系统的uid:system、phone、log、nfc、Bluetooth、shell。  
  44.     // TODO: add a property to control this?  
  45.     long dexOptLRUThresholdInMinutes;  
  46.     if (mLazyDexOpt) {  
  47.         dexOptLRUThresholdInMinutes = 30// only last 30 minutes of apps for eng builds.  
  48.     } else {  
  49.         dexOptLRUThresholdInMinutes = 7 * 24 * 60// apps used in the 7 days for users.  
  50.     }  
  51.     mDexOptLRUThresholdInMills = dexOptLRUThresholdInMinutes * 60 * 1000;  
  52.   
  53.     String separateProcesses = SystemProperties.get(“debug.separate_processes”);  
  54.     if (separateProcesses != null && separateProcesses.length() > 0) {  
  55.         if (“*”.equals(separateProcesses)) {  
  56.             mDefParseFlags = PackageParser.PARSE_IGNORE_PROCESSES;  
  57.             mSeparateProcesses = null;  
  58.             Slog.w(TAG, “Running with debug.separate_processes: * (ALL)”);  
  59.         } else {  
  60.             mDefParseFlags = 0;  
  61.             mSeparateProcesses = separateProcesses.split(“,”);  
  62.             Slog.w(TAG, “Running with debug.separate_processes: “  
  63.                     + separateProcesses);  
  64.         }  
  65.     } else {  
  66.         mDefParseFlags = 0;  
  67.         mSeparateProcesses = null;  
  68.     }  
  69.   
  70.     mInstaller = installer;//应用安装器  
  71.   
  72.     getDefaultDisplayMetrics(context, mMetrics);//设置DisplayMetrics对象  
  73.     //读取系统配置来初始化mGlobalGids、mSystemPermissions、mAvailableFeatures  
  74.     SystemConfig systemConfig = SystemConfig.getInstance();  
  75.     mGlobalGids = systemConfig.getGlobalGids();  
  76.     mSystemPermissions = systemConfig.getSystemPermissions();//保存在对象内部的变量列表  
  77.     mAvailableFeatures = systemConfig.getAvailableFeatures();  
  78.   
  79.     synchronized (mInstallLock) {  
  80.     // writer  
  81.     synchronized (mPackages) {  
  82.         mHandlerThread = new ServiceThread(TAG,//创建用来处理消息的线程,并加入到Watchdog的监控中  
  83.                 Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);  
  84.         mHandlerThread.start();  
  85.         mHandler = new PackageHandler(mHandlerThread.getLooper());  
  86.         Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);  
  87.         //为/data目录下的子目录生成文件对象  
  88.         File dataDir = Environment.getDataDirectory();  
  89.         mAppDataDir = new File(dataDir, “data”);///data/data存放应用数据的目录  
  90.         mAppInstallDir = new File(dataDir, “app”);///data/app存放安装的应用  
  91.         mAppLib32InstallDir = new File(dataDir, “app-lib”);//存放应用自带的native库  
  92.         mAsecInternalPath = new File(dataDir, “app-asec”).getPath();  
  93.         mUserAppDataDir = new File(dataDir, “user”);//存放用户的数据文件  
  94.         mDrmAppPrivateInstallDir = new File(dataDir, “app-private”);//存放drm保护的应用  
  95.   
  96.         sUserManager = new UserManagerService(context, this,  
  97.                 mInstallLock, mPackages);//创建用户管理服务  
  98.   
  99.         // Propagate permission configuration in to package manager.通过systemConfig得到系统中定义的permission。这些permission保存在/etc/permissions目录下的文件中。  
  100.         ArrayMap<String, SystemConfig.PermissionEntry> permConfig  
  101.                 = systemConfig.getPermissions();  
  102.         for (int i=0; i<permConfig.size(); i++) {  
  103.             SystemConfig.PermissionEntry perm = permConfig.valueAt(i);  
  104.             BasePermission bp = mSettings.mPermissions.get(perm.name);  
  105.             if (bp == null) {  
  106.                 bp = new BasePermission(perm.name, “android”, BasePermission.TYPE_BUILTIN);  
  107.                 mSettings.mPermissions.put(perm.name, bp);  
  108.             }  
  109.             if (perm.gids != null) {  
  110.                 bp.gids = appendInts(bp.gids, perm.gids);  
  111.             }  
  112.         }  
  113.         //通过systemConfig得到系统中的共享库列表  
  114.         ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();  
  115.         for (int i=0; i<libConfig.size(); i++) {  
  116.             mSharedLibraries.put(libConfig.keyAt(i),  
  117.                     new SharedLibraryEntry(libConfig.valueAt(i), null));  
  118.         }  
  119.         //打开SELinux的policy文件  
  120.         mFoundPolicyFile = SELinuxMMAC.readInstallPolicy();  
  121.         //读取packages.xml文件的内容,解析后插到mSettings的mPackages等变量中  
  122.         mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),  
  123.                 mSdkVersion, mOnlyCore);  
  124.         //设置模块来代替framework-res.apk中的缺省ResolverActivity  
  125.         String customResolverActivity = Resources.getSystem().getString(  
  126.                 R.string.config_customResolverActivity);  
  127.         if (TextUtils.isEmpty(customResolverActivity)) {  
  128.             customResolverActivity = null;  
  129.         } else {  
  130.             mCustomResolverComponentName = ComponentName.unflattenFromString(  
  131.                     customResolverActivity);  
  132.         }  
  133.   
  134.         long startTime = SystemClock.uptimeMillis();//记录开始扫描的时间  
  135.   
  136.         EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SYSTEM_SCAN_START,  
  137.                 startTime);  
  138.   
  139.         // Set flag to monitor and not change apk file paths when  
  140.         // scanning install directories.设置扫描模式,以监测和扫描安装目录时不会改变apk文件路径  
  141.         final int scanFlags = SCAN_NO_PATHS | SCAN_DEFER_DEX | SCAN_BOOTING;  
  142.         //定义已经优化的文件集合,已经优化过的文件或不需要优化的文件将加到这个集合中  
  143.         final ArraySet<String> alreadyDexOpted = new ArraySet<String>();  
  144.   
  145.         /** 
  146.          * Add everything in the in the boot class path to the 
  147.          * list of process files because dexopt will have been run 
  148.          * if necessary during zygote startup. 
  149.          */  
  150.         final String bootClassPath = System.getenv(“BOOTCLASSPATH”);  
  151.         final String systemServerClassPath = System.getenv(“SYSTEMSERVERCLASSPATH”);  
  152.         //把环境变量BOOTCALSSPATH中定义的包加入到已优化的集合alreadyDexOpted  
  153.         if (bootClassPath != null) {  
  154.             String[] bootClassPathElements = splitString(bootClassPath, ‘:’);  
  155.             for (String element : bootClassPathElements) {  
  156.                 alreadyDexOpted.add(element);  
  157.             }  
  158.         } else {  
  159.             Slog.w(TAG, “No BOOTCLASSPATH found!”);  
  160.         }  

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1. if (systemServerClassPath != null) {  
  2.     String[] systemServerClassPathElements = splitString(systemServerClassPath, ‘:’);  
  3.     for (String element : systemServerClassPathElements) {  
  4.         alreadyDexOpted.add(element);  
  5.     }  
  6. else {  
  7.     Slog.w(TAG, “No SYSTEMSERVERCLASSPATH found!”);  
  8. }  
  9.   
  10. final List<String> allInstructionSets = getAllInstructionSets();  
  11. final String[] dexCodeInstructionSets =  
  12.     getDexCodeInstructionSets(allInstructionSets.toArray(new String[allInstructionSets.size()]));  
  13.   
  14. /** 
  15.  * Ensure all external libraries have had dexopt run on them. 
  16.  */  
  17. if (mSharedLibraries.size() > 0) {  
  18.     // NOTE: For now, we’re compiling these system “shared libraries”  
  19.     // (and framework jars) into all available architectures. It’s possible  
  20.     // to compile them only when we come across an app that uses them (there’s  
  21.     // already logic for that in scanPackageLI) but that adds some complexity.  
  22.     for (String dexCodeInstructionSet : dexCodeInstructionSets) {  
  23.         for (SharedLibraryEntry libEntry : mSharedLibraries.values()) {  
  24.             final String lib = libEntry.path;  
  25.             if (lib == null) {  
  26.                 continue;  
  27.             }  
  28.   
  29.             try {  
  30.                 byte dexoptRequired = DexFile.isDexOptNeededInternal(lib, null,  
  31.                                                                      dexCodeInstructionSet,  
  32.                                                                      false);  
  33.                 if (dexoptRequired != DexFile.UP_TO_DATE) {  
  34.                     alreadyDexOpted.add(lib);  
  35.   
  36.                     // The list of “shared libraries” we have at this point is  
  37.                     if (dexoptRequired == DexFile.DEXOPT_NEEDED) {  
  38.                         mInstaller.dexopt(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet);  
  39.                     } else {  
  40.                         mInstaller.patchoat(lib, Process.SYSTEM_UID, true, dexCodeInstructionSet);  
  41.                     }  
  42.                 }  
  43.             } catch (FileNotFoundException e) {  
  44.                 Slog.w(TAG, “Library not found: “ + lib);  
  45.             } catch (IOException e) {  
  46.                 Slog.w(TAG, “Cannot dexopt “ + lib + “; is it an APK or JAR? “  
  47.                         + e.getMessage());  
  48.             }  
  49.         }  
  50.     }  
  51. }  
  52.   
  53. File frameworkDir = new File(Environment.getRootDirectory(), “framework”);  
  54.   
  55. // Gross hack for now: we know this file doesn’t contain any  
  56. // code, so don’t dexopt it to avoid the resulting log spew.  
  57. alreadyDexOpted.add(frameworkDir.getPath() + “/framework-res.apk”);  
  58.   
  59. // Gross hack for now: we know this file is only part of  
  60. // the boot class path for art, so don’t dexopt it to  
  61. // avoid the resulting log spew.  
  62. alreadyDexOpted.add(frameworkDir.getPath() + “/core-libart.jar”);  
  63.   
  64. /** 
  65.  * And there are a number of commands implemented in Java, which 
  66.  * we currently need to do the dexopt on so that they can be 
  67.  * run from a non-root shell. 
  68.  */  
  69. String[] frameworkFiles = frameworkDir.list();  
  70. if (frameworkFiles != null) {  
  71.     // TODO: We could compile these only for the most preferred ABI. We should  
  72.     // first double check that the dex files for these commands are not referenced  
  73.     // by other system apps.  
  74.     for (String dexCodeInstructionSet : dexCodeInstructionSets) {  
  75.         for (int i=0; i<frameworkFiles.length; i++) {  
  76.             File libPath = new File(frameworkDir, frameworkFiles[i]);  
  77.             String path = libPath.getPath();  
  78.             // Skip the file if we already did it.  
  79.             if (alreadyDexOpted.contains(path)) {  
  80.                 continue;  
  81.             }  
  82.             // Skip the file if it is not a type we want to dexopt.  
  83.             if (!path.endsWith(“.apk”) && !path.endsWith(“.jar”)) {  
  84.                 continue;  
  85.             }  
  86.             try {  
  87.                 byte dexoptRequired = DexFile.isDexOptNeededInternal(path, null,  
  88.                                                                      dexCodeInstructionSet,  
  89.                                                                      false);  
  90.                 if (dexoptRequired == DexFile.DEXOPT_NEEDED) {  
  91.                     mInstaller.dexopt(path, Process.SYSTEM_UID, true, dexCodeInstructionSet);  
  92.                 } else if (dexoptRequired == DexFile.PATCHOAT_NEEDED) {  
  93.                     mInstaller.patchoat(path, Process.SYSTEM_UID, true, dexCodeInstructionSet);  
  94.                 }  
  95.             } catch (FileNotFoundException e) {  
  96.                 Slog.w(TAG, “Jar not found: “ + path);  
  97.             } catch (IOException e) {  
  98.                 Slog.w(TAG, “Exception reading jar: “ + path, e);  
  99.             }  
  100.         }  
  101.     }  
  102. }  

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1.         // Collect vendor overlay packages.  
  2.         // (Do this before scanning any apps.)  
  3.         // For security and version matching reason, only consider  
  4.         // overlay packages if they reside in VENDOR_OVERLAY_DIR.  
  5.         File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR);  
  6.         scanDirLI(vendorOverlayDir, PackageParser.PARSE_IS_SYSTEM  
  7.                 | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_TRUSTED_OVERLAY, 0);  
  8.   
  9.         // Find base frameworks (resource packages without code).  
  10.         scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM  
  11.                 | PackageParser.PARSE_IS_SYSTEM_DIR  
  12.                 | PackageParser.PARSE_IS_PRIVILEGED,  
  13.                 scanFlags | SCAN_NO_DEX, 0);  
  14.   
  15.         // Collected privileged system packages./system/priv-app  
  16.         final File privilegedAppDir = new File(Environment.getRootDirectory(), “priv-app”);  
  17.         scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM  
  18.                 | PackageParser.PARSE_IS_SYSTEM_DIR  
  19.                 | PackageParser.PARSE_IS_PRIVILEGED, scanFlags, 0);  
  20.   
  21.         // Collect ordinary system packages./system/app  
  22.         final File systemAppDir = new File(Environment.getRootDirectory(), “app”);  
  23.         scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM  
  24.                 | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);  
  25.   
  26.         // Collect all vendor packages.  
  27.         File vendorAppDir = new File(“/vendor/app”);  
  28.         try {  
  29.             vendorAppDir = vendorAppDir.getCanonicalFile();  
  30.         } catch (IOException e) {  
  31.             // failed to look up canonical path, continue with original one  
  32.         }  
  33.         scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM  
  34.                 | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);  
  35.   
  36.         // Collect all OEM packages.  
  37.         final File oemAppDir = new File(Environment.getOemDirectory(), “app”);  
  38.         scanDirLI(oemAppDir, PackageParser.PARSE_IS_SYSTEM  
  39.                 | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags, 0);  
  40.   
  41.         if (DEBUG_UPGRADE) Log.v(TAG, “Running installd update commands”);  
  42.         mInstaller.moveFiles();  
  43.   
  44.         // Prune any system packages that no longer exist.  
  45.         final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<String>();  
  46.         final ArrayMap<String, File> expectingBetter = new ArrayMap<>();  
  47.         if (!mOnlyCore) {  
  48.             Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();  
  49.             while (psit.hasNext()) {  
  50.                 PackageSetting ps = psit.next();  
  51.   
  52.                 /* 
  53.                  * If this is not a system app, it can’t be a 
  54.                  * disable system app. 
  55.                  */  
  56.                 if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) {  
  57.                     continue;  
  58.                 }  
  59.   
  60.                 /* 
  61.                  * If the package is scanned, it’s not erased. 
  62.                  */  
  63.                 final PackageParser.Package scannedPkg = mPackages.get(ps.name);  
  64.                 if (scannedPkg != null) {  
  65.                     /* 
  66.                      * If the system app is both scanned and in the 
  67.                      * disabled packages list, then it must have been 
  68.                      * added via OTA. Remove it from the currently 
  69.                      * scanned package so the previously user-installed 
  70.                      * application can be scanned. 
  71.                      */  
  72.                     if (mSettings.isDisabledSystemPackageLPr(ps.name)) {  
  73.                         logCriticalInfo(Log.WARN, “Expecting better updated system app for “  
  74.                                 + ps.name + “; removing system app.  Last known codePath=”  
  75.                                 + ps.codePathString + “, installStatus=” + ps.installStatus  
  76.                                 + “, versionCode=” + ps.versionCode + “; scanned versionCode=”  
  77.                                 + scannedPkg.mVersionCode);  
  78.                         removePackageLI(ps, true);  
  79.                         expectingBetter.put(ps.name, ps.codePath);  
  80.                     }  
  81.   
  82.                     continue;  
  83.                 }  
  84.   
  85.                 if (!mSettings.isDisabledSystemPackageLPr(ps.name)) {  
  86.                     psit.remove();  
  87.                     logCriticalInfo(Log.WARN, “System package “ + ps.name  
  88.                             + ” no longer exists; wiping its data”);  
  89.                     removeDataDirsLI(ps.name);  
  90.                 } else {  
  91.                     final PackageSetting disabledPs = mSettings.getDisabledSystemPkgLPr(ps.name);  
  92.                     if (disabledPs.codePath == null || !disabledPs.codePath.exists()) {  
  93.                         possiblyDeletedUpdatedSystemApps.add(ps.name);  
  94.                     }  
  95.                 }  
  96.             }  
  97.         }  
  98.   
  99.         //look for any incomplete package installations  
  100.         ArrayList<PackageSetting> deletePkgsList = mSettings.getListOfIncompleteInstallPackagesLPr();  
  101.         //clean up list  
  102.         for(int i = 0; i < deletePkgsList.size(); i++) {  
  103.             //clean up here  
  104.             cleanupInstallFailedPackage(deletePkgsList.get(i));  
  105.         }  
  106.         //delete tmp files  
  107.         deleteTempPackageFiles();  
  108.   
  109.         // Remove any shared userIDs that have no associated packages  
  110.         mSettings.pruneSharedUsersLPw();  
  111.   
  112.         if (!mOnlyCore) {  
  113.   
  114.             EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,  
  115.                     SystemClock.uptimeMillis());  
  116.             scanDirLI(mAppInstallDir, 0, scanFlags | SCAN_REQUIRE_KNOWN, 0);  
  117.   
  118.             scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,  
  119.                     scanFlags | SCAN_REQUIRE_KNOWN, 0);  
  120.   
  121.             /** 
  122.              * Remove disable package settings for any updated system 
  123.              * apps that were removed via an OTA. If they’re not a 
  124.              * previously-updated app, remove them completely. 
  125.              * Otherwise, just revoke their system-level permissions. 
  126.              */  
  127.             for (String deletedAppName : possiblyDeletedUpdatedSystemApps) {  
  128.                 PackageParser.Package deletedPkg = mPackages.get(deletedAppName);  
  129.                 mSettings.removeDisabledSystemPackageLPw(deletedAppName);  
  130.   
  131.                 String msg;  
  132.                 if (deletedPkg == null) {  
  133.                     msg = “Updated system package “ + deletedAppName  
  134.                             + ” no longer exists; wiping its data”;  
  135.                     removeDataDirsLI(deletedAppName);  
  136.                 } else {  
  137.                     msg = “Updated system app + “ + deletedAppName  
  138.                             + ” no longer present; removing system privileges for “  
  139.                             + deletedAppName;  
  140.   
  141.                     deletedPkg.applicationInfo.flags &= ~ApplicationInfo.FLAG_SYSTEM;  
  142.   
  143.                     PackageSetting deletedPs = mSettings.mPackages.get(deletedAppName);  
  144.                     deletedPs.pkgFlags &= ~ApplicationInfo.FLAG_SYSTEM;  
  145.                 }  
  146.                 logCriticalInfo(Log.WARN, msg);  
  147.             }  
  148.   
  149.             /** 
  150.              * Make sure all system apps that we expected to appear on 
  151.              * the userdata partition actually showed up. If they never 
  152.              * appeared, crawl back and revive the system version. 
  153.              */  
  154.             for (int i = 0; i < expectingBetter.size(); i++) {  
  155.                 final String packageName = expectingBetter.keyAt(i);  
  156.                 if (!mPackages.containsKey(packageName)) {  
  157.                     final File scanFile = expectingBetter.valueAt(i);  
  158.   
  159.                     logCriticalInfo(Log.WARN, “Expected better “ + packageName  
  160.                             + ” but never showed up; reverting to system”);  
  161.   
  162.                     final int reparseFlags;  
  163.                     if (FileUtils.contains(privilegedAppDir, scanFile)) {  
  164.                         reparseFlags = PackageParser.PARSE_IS_SYSTEM  
  165.                                 | PackageParser.PARSE_IS_SYSTEM_DIR  
  166.                                 | PackageParser.PARSE_IS_PRIVILEGED;  
  167.                     } else if (FileUtils.contains(systemAppDir, scanFile)) {  
  168.                         reparseFlags = PackageParser.PARSE_IS_SYSTEM  
  169.                                 | PackageParser.PARSE_IS_SYSTEM_DIR;  
  170.                     } else if (FileUtils.contains(vendorAppDir, scanFile)) {  
  171.                         reparseFlags = PackageParser.PARSE_IS_SYSTEM  
  172.                                 | PackageParser.PARSE_IS_SYSTEM_DIR;  
  173.                     } else if (FileUtils.contains(oemAppDir, scanFile)) {  
  174.                         reparseFlags = PackageParser.PARSE_IS_SYSTEM  
  175.                                 | PackageParser.PARSE_IS_SYSTEM_DIR;  
  176.                     } else {  
  177.                         Slog.e(TAG, “Ignoring unexpected fallback path “ + scanFile);  
  178.                         continue;  
  179.                     }  
  180.   
  181.                     mSettings.enableSystemPackageLPw(packageName);  
  182.   
  183.                     try {  
  184.                         scanPackageLI(scanFile, reparseFlags, scanFlags, 0null);  
  185.                     } catch (PackageManagerException e) {  
  186.                         Slog.e(TAG, “Failed to parse original system package: “  
  187.                                 + e.getMessage());  
  188.                     }  
  189.                 }  
  190.             }  
  191.         }  
  192.   
  193.         // Now that we know all of the shared libraries, update all clients to have  
  194.         // the correct library paths.更新所有应用的动态库路径  
  195.         updateAllSharedLibrariesLPw();  
  196.   
  197.         for (SharedUserSetting setting : mSettings.getAllSharedUsersLPw()) {  
  198.             // NOTE: We ignore potential failures here during a system scan (like  
  199.             // the rest of the commands above) because there’s precious little we  
  200.             // can do about it. A settings error is reported, though.  
  201.             adjustCpuAbisForSharedUserLPw(setting.packages, null /* scanned package */,  
  202.                     false /* force dexopt */false /* defer dexopt */);  
  203.         }  
  204.   
  205.         // Now that we know all the packages we are keeping,  
  206.         // read and update their last usage times.  
  207.         mPackageUsage.readLP();  
  208.   
  209.         EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_SCAN_END,  
  210.                 SystemClock.uptimeMillis());  
  211.         Slog.i(TAG, “Time to scan packages: “  
  212.                 + ((SystemClock.uptimeMillis()-startTime)/1000f)  
  213.                 + ” seconds”);  
  214.   
  215.         // If the platform SDK has changed since the last time we booted,  
  216.         // we need to re-grant app permission to catch any new ones that  
  217.         // appear.  This is really a hack, and means that apps can in some  
  218.         // cases get permissions that the user didn’t initially explicitly  
  219.         // allow…  it would be nice to have some better way to handle  
  220.         // this situation.如果平台的SDK版本和上次启动时相比发生了变化,可能permission的定义也改变了,因此需要重新赋予应用权限  
  221.         final boolean regrantPermissions = mSettings.mInternalSdkPlatform  
  222.                 != mSdkVersion;  
  223.         if (regrantPermissions) Slog.i(TAG, “Platform changed from “  
  224.                 + mSettings.mInternalSdkPlatform + ” to “ + mSdkVersion  
  225.                 + “; regranting permissions for internal storage”);  
  226.         mSettings.mInternalSdkPlatform = mSdkVersion;  
  227.   
  228.         updatePermissionsLPw(nullnull, UPDATE_PERMISSIONS_ALL  
  229.                 | (regrantPermissions  
  230.                         ? (UPDATE_PERMISSIONS_REPLACE_PKG|UPDATE_PERMISSIONS_REPLACE_ALL)  
  231.                         : 0));  
  232.   
  233.         // If this is the first boot, and it is a normal boot, then  
  234.         // we need to initialize the default preferred apps.  
  235.         if (!mRestoredSettings && !onlyCore) {  
  236.             mSettings.readDefaultPreferredAppsLPw(this0);  
  237.         }  
  238.   
  239.         // If this is first boot after an OTA, and a normal boot, then  
  240.         // we need to clear code cache directories.如果这是执行OTA后的第一次启动,需要清除cache  
  241.         mIsUpgrade = !Build.FINGERPRINT.equals(mSettings.mFingerprint);  
  242.         if (mIsUpgrade && !onlyCore) {  
  243.             Slog.i(TAG, “Build fingerprint changed; clearing code caches”);  
  244.             for (String pkgName : mSettings.mPackages.keySet()) {  
  245.                 deleteCodeCacheDirsLI(pkgName);  
  246.             }  
  247.             mSettings.mFingerprint = Build.FINGERPRINT;  
  248.         }  
  249.   
  250.         // All the changes are done during package scanning.所有操作执行完成,更新数据库版本  
  251.         mSettings.updateInternalDatabaseVersion();  
  252.   
  253.         // can downgrade to reader  
  254.         mSettings.writeLPr();把mSettings中的内容保存到packages.xml  
  255.   
  256.         EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_READY,  
  257.                 SystemClock.uptimeMillis());  
  258.   
  259.   
  260.         mRequiredVerifierPackage = getRequiredVerifierLPr();  
  261.     } // synchronized (mPackages)  
  262.     } // synchronized (mInstallLock)  
  263.     //创建PackageInstallerService./data/app  
  264.     mInstallerService = new PackageInstallerService(context, this, mAppInstallDir);  
  265.   
  266.     // Now after opening every single application zip, make sure they  
  267.     // are all flushed.  Not really needed, but keeps things nice and  
  268.     // tidy.启动内存垃圾回收  
  269.     Runtime.getRuntime().gc();  
  270. }  

PackageManagerService构造方法的执行过程就是先读取保存在packages.xml文件中的上次扫描的结果,保存在mSettings的成员变量中,然后扫描设备中几个应用目录下的应用文件,并把扫描的结果保存在PackageManagerService的成员变量mPackages中,通过对比上次扫描的结果来检查本次扫描到的应用中是否有被升级包覆盖的系统应用,如果有,则从mPackages中去除掉。这样mPackages的记录和mSettings的记录就一致了,最后,将本次扫描的结果写回packages.xml文件中。

PackageManagerService的初始化工作都是在它的构造方法中完成的,主要完成以下任务:

(1)、添加一些用户id,如shell、phone等;

(2)、建立并启动PackageHandler消息循环,用于处理apk安装请求,如adb install,packageInstaller安装过程中就会发送消息。

3、添加用户

添加用户调用的是Settings类的addSharedUserLPw方法。

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1. /** 
  2.  * 检查mSharedUsers中是否有这个用户,没有则创建一个SharedUserSetting对象, 
  3.  * 保存到mUserIds或mOtherUserIds中,并添加到mSharedUsers中 
  4.  * @param name 
  5.  * @param uid 
  6.  * @param pkgFlags 
  7.  * @return 
  8.  */  
  9. SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags) {  
  10.     SharedUserSetting s = mSharedUsers.get(name);  
  11.     if (s != null) {  
  12.         if (s.userId == uid) {  
  13.             return s;  
  14.         }  
  15.         PackageManagerService.reportSettingsProblem(Log.ERROR,  
  16.                 “Adding duplicate shared user, keeping first: “ + name);  
  17.         return null;  
  18.     }  
  19.     s = new SharedUserSetting(name, pkgFlags);//创建SharedUserSetting对象  
  20.     s.userId = uid;//将uid赋给SharedUserSetting对象的userId属性  
  21.     if (addUserIdLPw(uid, s, name)) {//保存到mUserIds或mOtherUserIds中  
  22.         mSharedUsers.put(name, s);//添加到mSharedUsers中  
  23.         return s;  
  24.     }  
  25.     return null;  
  26. }  

首先检查mSharedusers数组中是否含有对应name的SharedUserSetting对象,如果没有则创建SharedUserSetting对象,调用addUserIdLPw方法添加到mUserIds或mOtherUserIds数组中,并添加到mSharedUsers中,返回查询的对象。

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1. /** 
  2.  * 将uid及对应的SharedUserSetting对象添加到mUserIds或mOtherUserIds 
  3.  * @param uid  
  4.  * @param obj SharedUserSetting对象 
  5.  * @param name 
  6.  * @return 
  7.  */  
  8. private boolean addUserIdLPw(int uid, Object obj, Object name) {  
  9.     if (uid > Process.LAST_APPLICATION_UID) {//大于应用程序最大的id,19999  
  10.         return false;  
  11.     }  
  12.   
  13.     if (uid >= Process.FIRST_APPLICATION_UID) {// 10000  
  14.         int N = mUserIds.size();  
  15.         final int index = uid – Process.FIRST_APPLICATION_UID;  
  16.         while (index >= N) {//先添加元素,后设置值  
  17.             mUserIds.add(null);  
  18.             N++;  
  19.         }  
  20.         if (mUserIds.get(index) != null) {  
  21.             PackageManagerService.reportSettingsProblem(Log.ERROR,  
  22.                     “Adding duplicate user id: “ + uid  
  23.                     + ” name=” + name);  
  24.             return false;  
  25.         }  
  26.         mUserIds.set(index, obj);//把该uid和对应的SharedUserSetting保存  
  27.     } else {//系统用户  
  28.         if (mOtherUserIds.get(uid) != null) {  
  29.             PackageManagerService.reportSettingsProblem(Log.ERROR,  
  30.                     “Adding duplicate shared id: “ + uid  
  31.                     + ” name=” + name);  
  32.             return false;  
  33.         }  
  34.         mOtherUserIds.put(uid, obj);//保存到mOtherUserIds中  
  35.     }  
  36.     return true;  
  37. }  

在构造方法中添加了6个系统用户:android.uid.system、android.uid.phone、android.uid.log、android.uid.nfc、android.uid.bluetooth、android.uid.shell。

4、建立并启动PackageHandler消息循环机制

PackageHandler用来处理安装apk等过程中的各种消息。这里比较简单,这要是建立一个PackageHandler,来处理消息。

5、处理permission文件

在PackageManagerService的构造方法中,调用了SystemConfig的getSystemPermissions()方法来获取系统的Permission列表。getSystemPermisssions()方法返回的是保存在对象内部的变量列表,如下:

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1. public SparseArray<ArraySet<String>> getSystemPermissions() {  
  2.     return mSystemPermissions;  
  3. }  


这个列表的建立是在SystemConfig的构造方法中完成的,如下:

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1. SystemConfig() {  
  2.     // Read configuration from system /system/etc/sysconfig  
  3.     readPermissions(Environment.buildPath(  
  4.             Environment.getRootDirectory(), “etc”“sysconfig”), false);  
  5.     // Read configuration from the old permissions dir <span style=”font-size: 11.8181819915771px; font-family: Arial, Helvetica, sans-serif;”>/system/etc/permissions</span>  
  6.     readPermissions(Environment.buildPath(  
  7.             Environment.getRootDirectory(), “etc”“permissions”), false);  
  8.     // Only read features from OEM config /oem/etc/sysconfig  
  9.     readPermissions(Environment.buildPath(  
  10.             Environment.getOemDirectory(), “etc”“sysconfig”), true);  
  11.     readPermissions(Environment.buildPath(// /oem/etc/permissions  
  12.             Environment.getOemDirectory(), “etc”“permissions”), true);  
  13. }  


Permission的读取是通过readPermission()方法完成的,代码如下:

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1. void readPermissions(File libraryDir, boolean onlyFeatures) {  
  2.     // Read permissions from given directory.  
  3.     if (!libraryDir.exists() || !libraryDir.isDirectory()) {  
  4.         if (!onlyFeatures) {  
  5.             Slog.w(TAG, “No directory “ + libraryDir + “, skipping”);  
  6.         }  
  7.         return;//如果目录不存在,或libraryDir不是一个目录,退出  
  8.     }  
  9.     if (!libraryDir.canRead()) {  
  10.         Slog.w(TAG, “Directory “ + libraryDir + ” cannot be read”);  
  11.         return;//如果目录是不可读的,退出  
  12.     }  
  13.   
  14.     // Iterate over the files in the directory and scan .xml files  
  15.     File platformFile = null;  
  16.     for (File f : libraryDir.listFiles()) {  
  17.         // We’ll read platform.xml last  
  18.         if (f.getPath().endsWith(“etc/permissions/platform.xml”)) {  
  19.             platformFile = f;  
  20.             continue;//先不处理platform.xml  
  21.         }  
  22.   
  23.         if (!f.getPath().endsWith(“.xml”)) {  
  24.             Slog.i(TAG, “Non-xml file “ + f + ” in “ + libraryDir + ” directory, ignoring”);  
  25.             continue;//仅处理xml文件,其余文件忽略  
  26.         }  
  27.         if (!f.canRead()) {  
  28.             Slog.w(TAG, “Permissions library file “ + f + ” cannot be read”);  
  29.             continue;//如果文件不可读,忽略  
  30.         }  
  31.   
  32.         readPermissionsFromXml(f, onlyFeatures);//解析xml文件  
  33.     }  
  34.   
  35.     // Read platform permissions last so it will take precedence  
  36.     if (platformFile != null) {//单独处理/etc/permissions/platform.xml文件  
  37.         readPermissionsFromXml(platformFile, onlyFeatures);  
  38.     }  
  39. }  


readPermissions()方法的工作就是读取指定目录下的xml文件,然后调用readPermissionsFromXml()方法来处理文件。目录下存放的是和permission相关的文件,其中platform.xml文件的内容和其他文件的内容不同。

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1. <?xml version=“1.0” encoding=“utf-8”?>  
  2. <permissions>  
  3.     <permission name=“android.permission.BLUETOOTH_ADMIN” >  
  4.         <group gid=“net_bt_admin” />  
  5.     </permission>  
  6.   
  7.     <permission name=“android.permission.BLUETOOTH” >  
  8.         <group gid=“net_bt” />  
  9.     </permission>  
  10.   
  11.     ……  
  12.   
  13.     <assign-permission name=“android.permission.MODIFY_AUDIO_SETTINGS” uid=”medi  
  14. a” />  
  15.     <assign-permission name=“android.permission.ACCESS_SURFACE_FLINGER” uid=”med  
  16. ia” />  
  17.     ……  
  18.   
  19.     <library name=“android.test.runner”  
  20.             file=“/system/framework/android.test.runner.jar” />  
  21.     <library name=“javax.obex”  
  22.             file=“/system/framework/javax.obex.jar”/>  
  23.     ……  
  24.     <allow-in-power-save package=“com.android.providers.downloads” />  
  25.   
  26. </permissions>  


platform.xml文件主要有3块内容组成。

  • 标签<permission>表示把属性name中的字符串表示的权限赋予<group>标签中的属性gid中的用户组。
  • 标签<assign-permission>表示把属性name中的字符串表示的权限赋予属性uid的用户。
  • 标签<library>表示除了framework中的动态库以外,系统将为应用自动加载的动态库。

readPermissionsFromXml()方法主要的工作就是解析上面的标签,看下解析结果。
(1)标签<permission>中的属性name字符串和<group>标签中的gid都放到了变量mSettings的mPermissions中,而所有gid也被收集起来放到了PackageManagerService的成员变量mGlobalGids中。
(2)标签<assign-permission>的内容放到了PMS的成员变量mSystemPermissions中,定义:
[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1. final SparseArray<ArraySet<String>> mSystemPermissions;  

(3)标签<library>的动态库字符串放到了PMS的成员变量mSharedLibraries中。
(4)其他xml文件的标签<feature>定义的字符串放到了PMS的成员变量mAvailableFeatures中。 Android的很多功能是通过所谓的permission字符串来保护的,应用中如果需要使用某项受保护的功能,需要在它的AndroidManifest.xml文件中显示的声明该功能对应的permission字符串。但是,有些功能只能由特定用户组的应用使用,在platform.xml文件中定义的就是这种限制规则。

4、扫描应用目录的过程

PMS的构造方法中调用了scanDirLI()方法来扫描某个目录中apk文件,如下:

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1. private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {  
  2.     final File[] files = dir.listFiles();  
  3.     if (ArrayUtils.isEmpty(files)) {  
  4.         Log.d(TAG, “No files in app dir “ + dir);  
  5.         return;//如果扫描的目录是空的,返回  
  6.     }  
  7.   
  8.     if (DEBUG_PACKAGE_SCANNING) {  
  9.         Log.d(TAG, “Scanning app dir “ + dir + ” scanFlags=” + scanFlags  
  10.                 + ” flags=0x” + Integer.toHexString(parseFlags));  
  11.     }  
  12.   
  13.     for (File file : files) {//判断文件是否是应用文件  
  14.         final boolean isPackage = (isApkFile(file) || file.isDirectory())  
  15.                 && !PackageInstallerService.isStageName(file.getName());  
  16.         if (!isPackage) {  
  17.             // Ignore entries which are not packages忽略非应用文件  
  18.             continue;  
  19.         }  
  20.         try {//调用scanPackageLI扫描文件  
  21.             scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,  
  22.                     scanFlags, currentTime, null);  
  23.         } catch (PackageManagerException e) {  
  24.             Slog.w(TAG, “Failed to parse “ + file + “: “ + e.getMessage());  
  25.   
  26.             // Delete invalid userdata apps删除安装失败的文件和数据目录  
  27.             if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&  
  28.                     e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {  
  29.                 logCriticalInfo(Log.WARN, “Deleting invalid package at “ + file);  
  30.                 if (file.isDirectory()) {  
  31.                     FileUtils.deleteContents(file);  
  32.                 }  
  33.                 file.delete();  
  34.             }  
  35.         }  
  36.     }  
  37. }  


scanDirLI()只扫描指定目录下的文件,不会去扫描子目录,同时只扫描后缀为“apk”的文件。调用scanPackageLI()方法来继续扫描过程。在scanDirLI方法中调用的参数是File对象,因此scanPackageLI调用第一个参数为File对象的方法,作用是把File对象解析成PackageParser.Package对象,这个结果会传递给scanPackageLI方法的第二个实现,用来创建PackageManagerService中和应用相关的各种内部数据结构。如下:

(1)解析文件:

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1. /* 
  2.      *  Scan a package and return the newly parsed package. 
  3.      *  Returns null in case of errors and the error code is stored in mLastScanError 
  4.      */  
  5.     private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,  
  6.             long currentTime, UserHandle user) throws PackageManagerException {  
  7.         if (DEBUG_INSTALL) Slog.d(TAG, “Parsing: “ + scanFile);  
  8.         parseFlags |= mDefParseFlags;  
  9.         PackageParser pp = new PackageParser();//创建文件解析器对象  
  10.         pp.setSeparateProcesses(mSeparateProcesses);  
  11.         pp.setOnlyCoreApps(mOnlyCore);  
  12.         pp.setDisplayMetrics(mMetrics);  
  13.   
  14.         if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) {  
  15.             parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;  
  16.         }  
  17.   
  18.         final PackageParser.Package pkg;  
  19.         try {  
  20.             pkg = pp.parsePackage(scanFile, parseFlags);//创建PackageParser.Package对象  
  21.         } catch (PackageParserException e) {  
  22.             throw PackageManagerException.from(e);  
  23.         }  


scanPackageLI方法创建一个文件解析器PackageParser对象,然后调用它的parsePackage()方法来解析参数中传递的文件对象scanFile,得到一个PackageParser.Package对象。

(2)处理更改了包名的应用。

这里是在检查应用是否属于<renamed-package>标签标记的改了包名的应用,如果是,则使用原始的包的信息:

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1. PackageSetting ps = null;  
  2. PackageSetting updatedPkg;  
  3. // reader  
  4. synchronized (mPackages) {  
  5.     // Look to see if we already know about this package.获取旧的包名  
  6.     String oldName = mSettings.mRenamedPackages.get(pkg.packageName);  
  7.     if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName)) {  
  8.         // This package has been renamed to its original name.  Let’s  
  9.         // use that.  
  10.         ps = mSettings.peekPackageLPr(oldName);  
  11.     }  
  12.     // If there was no original package, see one for the real package name.  
  13.     if (ps == null) {  
  14.         ps = mSettings.peekPackageLPr(pkg.packageName);  
  15.     }  


(3)处理安装了升级包的系统应用。

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1.     // Check to see if this package could be hiding/updating a system  
  2.     // package.  Must look for it either under the original or real  
  3.     // package name depending on our state.检查应用是否存在升级包  
  4.     updatedPkg = mSettings.getDisabledSystemPkgLPr(ps != null ? ps.name : pkg.packageName);  
  5.     if (DEBUG_INSTALL && updatedPkg != null) Slog.d(TAG, “updatedPkg = “ + updatedPkg);  
  6. }  
  7. boolean updatedPkgBetter = false;  
  8. // First check if this is a system package that may involve an update如果扫描的是系统应用并且有升级包  
  9. if (updatedPkg != null && (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {  
  10.     // If new package is not located in “/system/priv-app” (e.g. due to an OTA),  
  11.     // it needs to drop FLAG_PRIVILEGED.  
  12.     if (locationIsPrivileged(scanFile)) {  
  13.         updatedPkg.pkgFlags |= ApplicationInfo.FLAG_PRIVILEGED;  
  14.     } else {  
  15.         updatedPkg.pkgFlags &= ~ApplicationInfo.FLAG_PRIVILEGED;  
  16.     }  
  17.   
  18.     if (ps != null && !ps.codePath.equals(scanFile)) {  
  19.         // The path has changed from what was last scanned…  check the  
  20.         // version of the new path against what we have stored to determine  
  21.         // what to do.如果包名和上次扫描的结果不相同了,根据版本号来决定如何处理  
  22.         if (DEBUG_INSTALL) Slog.d(TAG, “Path changing from “ + ps.codePath);  
  23.         if (pkg.mVersionCode <= ps.versionCode) {//如果扫描文件的版本号低于升级包的版本,忽略这个文件  
  24.             // The system package has been updated and the code path does not match  
  25.             // Ignore entry. Skip it.  
  26.             Slog.i(TAG, “Package “ + ps.name + ” at “ + scanFile  
  27.                     + ” ignored: updated version “ + ps.versionCode  
  28.                     + ” better than this “ + pkg.mVersionCode);  
  29.             if (!updatedPkg.codePath.equals(scanFile)) {  
  30.                 Slog.w(PackageManagerService.TAG, “Code path for hidden system pkg : “  
  31.                         + ps.name + ” changing from “ + updatedPkg.codePathString  
  32.                         + ” to “ + scanFile);  
  33.                 updatedPkg.codePath = scanFile;  
  34.                 updatedPkg.codePathString = scanFile.toString();  
  35.             }  
  36.             updatedPkg.pkg = pkg;  
  37.             throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE, null);  
  38.         } else {  
  39.             // The current app on the system partition is better than  
  40.             // what we have updated to on the data partition; switch  
  41.             // back to the system partition version.  
  42.             // At this point, its safely assumed that package installation for  
  43.             // apps in system partition will go through. If not there won’t be a working  
  44.             // version of the app  
  45.             // writer如果扫描文件的版本号高于升级包的版本,则把升级包删除掉  
  46.             synchronized (mPackages) {  
  47.                 // Just remove the loaded entries from package lists.  
  48.                 mPackages.remove(ps.name);  
  49.             }  
  50.   
  51.             logCriticalInfo(Log.WARN, “Package “ + ps.name + ” at “ + scanFile  
  52.                     + ” reverting from “ + ps.codePathString  
  53.                     + “: new version “ + pkg.mVersionCode  
  54.                     + ” better than installed “ + ps.versionCode);  
  55.   
  56.             InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),  
  57.                     ps.codePathString, ps.resourcePathString, ps.legacyNativeLibraryPathString,  
  58.                     getAppDexInstructionSets(ps));  
  59.             synchronized (mInstallLock) {  
  60.                 args.cleanUpResourcesLI();  
  61.             }  
  62.             synchronized (mPackages) {  
  63.                 mSettings.enableSystemPackageLPw(ps.name);  
  64.             }  
  65.             updatedPkgBetter = true;  
  66.         }  
  67.     }  
  68. }  
  69.   
  70. if (updatedPkg != null) {//如果升级包存在,把属性设为系统应用  
  71.     // An updated system app will not have the PARSE_IS_SYSTEM flag set  
  72.     // initially  
  73.     parseFlags |= PackageParser.PARSE_IS_SYSTEM;  
  74.   
  75.     // An updated privileged app will not have the PARSE_IS_PRIVILEGED  
  76.     // flag set initially  
  77.     if ((updatedPkg.pkgFlags & ApplicationInfo.FLAG_PRIVILEGED) != 0) {  
  78.         parseFlags |= PackageParser.PARSE_IS_PRIVILEGED;  
  79.     }  
  80. }  


这段代码的作用就是处理带有升级包的应用。

(4)扫描文件的签名。

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1. // Verify certificates against what was last scanned  
  2. collectCertificatesLI(pp, ps, pkg, scanFile, parseFlags);  


(5)处理应用的包名有冲突的情况。

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1. /* 
  2.  * A new system app appeared, but we already had a non-system one of the 
  3.  * same name installed earlier. 
  4.  */  
  5. boolean shouldHideSystemApp = false;  
  6. if (updatedPkg == null && ps != null//如果扫描的文件是系统应用,但是也存在一个同名的普通应用  
  7.         && (parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) != 0 && !isSystemApp(ps)) {  
  8.     /* 
  9.      * Check to make sure the signatures match first. If they don’t, 
  10.      * wipe the installed application and its data. 
  11.      */  
  12.     if (compareSignatures(ps.signatures.mSignatures, pkg.mSignatures)  
  13.             != PackageManager.SIGNATURE_MATCH) {//比较它们的签名,如果不相同就删除扫描到的系统应用  
  14.         logCriticalInfo(Log.WARN, “Package “ + ps.name + ” appeared on system, but”  
  15.                 + ” signatures don’t match existing userdata copy; removing”);  
  16.         deletePackageLI(pkg.packageName, nulltruenullnull0nullfalse);  
  17.         ps = null;  
  18.     } else {  
  19.         /* 
  20.          * If the newly-added system app is an older version than the 
  21.          * already installed version, hide it. It will be scanned later 
  22.          * and re-added like an update.如果扫描的系统应用和安装的应用签名相同,则比较他们的版本; 
  23.          */  
  24.         if (pkg.mVersionCode <= ps.versionCode) {//如果安装的应用版本高,则构成了升级关系,把系统应用隐藏。  
  25.             shouldHideSystemApp = true;  
  26.             logCriticalInfo(Log.INFO, “Package “ + ps.name + ” appeared at “ + scanFile  
  27.                     + ” but new version “ + pkg.mVersionCode + ” better than installed “  
  28.                     + ps.versionCode + “; hiding system”);  
  29.         } else {//删除安装的应用  
  30.             /* 
  31.              * The newly found system app is a newer version that the 
  32.              * one previously installed. Simply remove the 
  33.              * already-installed application and replace it with our own 
  34.              * while keeping the application data. 
  35.              */  
  36.             logCriticalInfo(Log.WARN, “Package “ + ps.name + ” at “ + scanFile  
  37.                     + ” reverting from “ + ps.codePathString + “: new version “  
  38.                     + pkg.mVersionCode + ” better than installed “ + ps.versionCode);  
  39.             InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),  
  40.                     ps.codePathString, ps.resourcePathString, ps.legacyNativeLibraryPathString,  
  41.                     getAppDexInstructionSets(ps));  
  42.             synchronized (mInstallLock) {  
  43.                 args.cleanUpResourcesLI();  
  44.             }  
  45.         }  
  46.     }  
  47. }  


(6)处理应用的代码路径和资源路径:

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1. // The apk is forward locked (not public) if its code and resources  
  2. // are kept in different files. (except for app in either system or  
  3. // vendor path).  
  4. // 如果一个应用的代码路径和资源路径不相同,则加上PARSE_FORWARD_LOCK标记,这类应用是forward locked类应用  
  5. if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {  
  6.     if (ps != null && !ps.codePath.equals(ps.resourcePath)) {  
  7.         parseFlags |= PackageParser.PARSE_FORWARD_LOCK;  
  8.     }  
  9. }  
  10.   
  11. // TODO: extend to support forward-locked splits  
  12. String resourcePath = null;  
  13. String baseResourcePath = null;  
  14. if ((parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0 && !updatedPkgBetter) {  
  15.     if (ps != null && ps.resourcePathString != null) {//如果是forward locked类应用,且他们的资源路径不为空,单独设置他们的资源路径  
  16.         resourcePath = ps.resourcePathString;  
  17.         baseResourcePath = ps.resourcePathString;  
  18.     } else {  
  19.         // Should not happen at all. Just log an error.  
  20.         Slog.e(TAG, “Resource path not set for pkg : “ + pkg.packageName);  
  21.     }  
  22. else {//如果是普通应用,设置他们的资源路径和代码路径一致  
  23.     resourcePath = pkg.codePath;  
  24.     baseResourcePath = pkg.baseCodePath;  
  25. }  
  26.   
  27. // Set application objects path explicitly.  
  28. pkg.applicationInfo.setCodePath(pkg.codePath);  
  29. pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);  
  30. pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);  
  31. pkg.applicationInfo.setResourcePath(resourcePath);  
  32. pkg.applicationInfo.setBaseResourcePath(baseResourcePath);  
  33. pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);  


(7)调用参数为PackageParser.Package对象的scanPackageLI方法。

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1. // Note that we invoke the following method only if we are about to unpack an application  
  2.  PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanFlags  
  3.          | SCAN_UPDATE_SIGNATURE, currentTime, user);  


(8)结束:

[java] 
view plain
copy
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》
《Android5.1--APK包的安装、卸载和优化(PackageManagerService)》

  1. /* 
  2.   * If the system app should be overridden by a previously installed 
  3.   * data, hide the system app now and let the /data/app scan pick it up 
  4.   * again. 
  5.   */  
  6.  if (shouldHideSystemApp) {  
  7.      synchronized (mPackages) {  
  8.          /* 
  9.           * We have to grant systems permissions before we hide, because 
  10.           * grantPermissions will assume the package update is trying to 
  11.           * expand its permissions.如果扫描的应用带有升级包,把他们的关系保存到mSettings中 
  12.           */  
  13.          grantPermissionsLPw(pkg, true, pkg.packageName);  
  14.          mSettings.disableSystemPackageLPw(pkg.packageName);  
  15.      }  
  16.  }  
  17.   
  18.  return scannedPkg;  


scanPackageLI方法在解析出了apk文件的信息后,主要是通过对比上次启动的结果来检查系统中是否存在和扫描的文件相冲突的应用包存在,如果有scanPackageLI方法会设法纠正错误,保证系统启动后的一致性。完成错误检查后,将进入第二阶段,调用PackageParser.Package对象的scanPackageLI方法来构建PackageManagerService中的数据结构。

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