Android 6.0 PackageManagerService


PackageManagerService构造函数


Settings

下面我们主要分析PackageManagerService的构造函数:

  1. mSettings = new Settings(context);//新建Settings  
  2. mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,//往Settings中添加SharedUserSetting  
  3.         ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);  
  4. mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,  
  5.         ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);  
  6. mSettings.addSharedUserLPw("android.uid.log", LOG_UID,  
  7.         ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);  
  8. mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,  
  9.         ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);  
  10. mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,  
  11.         ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);  
  12. mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,  
  13.         ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED);  

我们还是挑主要的讲,在构造函数中新建了Setting类,然后又调用了addSharedUserLPw函数。

我们先看Settings类:

  1. Settings(Context context) {  
  2.     this(context, Environment.getDataDirectory());  
  3. }  
  4.   
  5. Settings(Context context, File dataDir) {  
  6.     mSystemDir = new File(dataDir, "system");  
  7.     mSystemDir.mkdirs();  
  8.     FileUtils.setPermissions(mSystemDir.toString(),  
  9.             FileUtils.S_IRWXU|FileUtils.S_IRWXG  
  10.             |FileUtils.S_IROTH|FileUtils.S_IXOTH,  
  11.             -1, -1);  
  12.     mSettingsFilename = new File(mSystemDir, "packages.xml");  
  13.     mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");  
  14.     mPackageListFilename = new File(mSystemDir, "packages.list");  
  15.     FileUtils.setPermissions(mPackageListFilename, 0640, SYSTEM_UID, PACKAGE_INFO_GID);  
  16.   
  17.     // Deprecated: Needed for migration  
  18.     mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");  
  19.     mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");  
  20. }  

Settings类在data目录下创建了system目录,然后分别保存了下面文件。

1.packages.xml:记录系统中所有安装的应用的信息, 我们来看下这个文件关于一个package

  1. <package name="com.android.providers.media" codePath="/system/priv-app/MediaProvider" nativeLibraryPath="/system/priv-app/MediaProvider/lib" publicFlags="944291397" privateFlags="8" ft="15659d595e8" it="15659d595e8" ut="15659d595e8" version="800" sharedUserId="10006">  
  2.     <sigs count="1">  
  3.         <cert index="2" key="308204a830820390a003020102020900f2b98e6123572c4e300d06092a864886f70d0101040500308194310b3009060355040613025553311330110603550408130a43616c69666f726e6961311630140603550407130d4d6f756e7461696e20566965773110300e060355040a1307416e64726f6964311  
  4.     </sigs>  
  5.     <perms>  
  6.         <item name="android.permission.ACCESS_CACHE_FILESYSTEM" granted="true" flags="0" />  
  7.         <item name="android.permission.WRITE_SETTINGS" granted="true" flags="0" />  
  8.         <item name="android.permission.SEND_DOWNLOAD_COMPLETED_INTENTS" granted="true" flags="0" />  
  9.         <item name="android.permission.RECEIVE_BOOT_COMPLETED" granted="true" flags="0" />  
  10.         <item name="android.permission.WRITE_MEDIA_STORAGE" granted="true" flags="0" />  
  11.         <item name="android.permission.INTERNET" granted="true" flags="0" />  
  12.         <item name="android.permission.UPDATE_DEVICE_STATS" granted="true" flags="0" />  
  13.         <item name="android.permission.ACCESS_ALL_DOWNLOADS" granted="true" flags="0" />  
  14.         <item name="android.permission.ACCESS_DOWNLOAD_MANAGER" granted="true" flags="0" />  
  15.         <item name="android.permission.MANAGE_USERS" granted="true" flags="0" />  
  16.         <item name="android.permission.ACCESS_NETWORK_STATE" granted="true" flags="0" />  
  17.         <item name="android.permission.ACCESS_MTP" granted="true" flags="0" />  
  18.         <item name="android.permission.INTERACT_ACROSS_USERS" granted="true" flags="0" />  
  19.         <item name="android.permission.CLEAR_APP_CACHE" granted="true" flags="0" />  
  20.         <item name="android.permission.CONNECTIVITY_INTERNAL" granted="true" flags="0" />  
  21.         <item name="android.permission.MODIFY_NETWORK_ACCOUNTING" granted="true" flags="0" />  
  22.         <item name="android.permission.WAKE_LOCK" granted="true" flags="0" />  
  23.         <item name="android.permission.UPDATE_APP_OPS_STATS" granted="true" flags="0" />  
  24.     </perms>  
  25.     <proper-signing-keyset identifier="4" />  
  26. </package>  

2.packages-backup.xml:上面文件的备份

3.packages-stopped.xml:被强制停止运行的应用信息

4.packages-stopped-backup.xml:上面文件的备份

5.packages.list:保存普通应用的数据目录和uid信息等

  1. ......  
  2. com.android.managedprovisioning 10009 0 /data/data/com.android.managedprovisioning platform 3003  
  3. com.android.gifviewer 10042 0 /data/data/com.android.gifviewer default none  
  4. com.android.dreams.phototable 10054 0 /data/data/com.android.dreams.phototable default none  
  5. com.leadcore.telassistant 1000 0 /data/data/com.leadcore.telassistant platform 3002,1023,1015,3003,3001  
  6. com.android.noisefield 10049 0 /data/data/com.android.noisefield default none  
  7. com.android.smspush 10064 0 /data/data/com.android.smspush default none  
  8. com.leadcore.codescan 10029 0 /data/data/com.leadcore.codescan platform 3003  
  9. com.android.wallpaper.livepicker 10046 0 /data/data/com.android.wallpaper.livepicker platform none  
  10. jp.co.omronsoft.openwnn 10051 0 /data/data/jp.co.omronsoft.openwnn default none  
  11. com.android.settings 1000 0 /data/data/com.android.settings platform 3002,1023,1015,3003,3001  
  12. com.leadcore.logservice 1000 0 /data/data/com.leadcore.logservice platform 3002,1023,1015,3003,3001  
  13. com.android.calculator2 10032 0 /data/data/com.android.calculator2 default none  
  14. com.leadcore.userfeedback 10062 0 /data/data/com.leadcore.userfeedback platform 3003  
  15. com.leadcore.filesearch 10035 0 /data/data/com.leadcore.filesearch default none  
  16. com.iflytek.inputmethod 10041 0 /data/data/com.iflytek.inputmethod default 3002,3003,3001  
  17. org.simalliance.openmobileapi.uiccterminal 10061 0 /data/data/org.simalliance.openmobileapi.uiccterminal platform none  
  18. com.android.wallpaper 10045 0 /data/data/com.android.wallpaper default none  
  19. com.android.vpndialogs 10018 0 /data/data/com.android.vpndialogs platform none  
  20. com.android.email 10031 0 /data/data/com.android.email default 3003  
  21. com.android.music 10048 0 /data/data/com.android.music platform 3002,1023,1015,3003  
  22. com.android.phone 1001 0 /data/data/com.android.phone platform 3002,3003,3001  
  23. com.android.shell 2000 0 /data/data/com.android.shell platform 3002,1023,1015,3008  
  24. com.android.video 10063 0 /data/data/com.android.video platform 1023,1015,3003  
  25. com.android.providers.userdictionary 10000 0 /data/data/com.android.providers.userdictionary default 3003  
  26. com.android.location.fused 1000 0 /data/data/com.android.location.fused platform 3002,1023,1015,3003,3001  
  27. com.android.deskclock 1000 0 /data/data/com.android.deskclock platform 3002,1023,1015,3003,3001  
  28. com.android.systemui 10017 0 /data/data/com.android.systemui platform 3002,1023,1015,3001,3006  
  29. ......  

Android对文件packages.xml和packages-stopped.xml写之前,会先把它们备份,如果写文件成功了,再把备份文件删除掉。如果写的时候,系统出问题重启了,重启后会读取这两个文件时,发现有备份文件,会使用备份文件的内容,因为这个时候原文件已经损坏了。

每个应用的信息会保存在PackageSetting中,而所有的PackageSetting对象会保存在Settings的成员变量mPackages中。


回到构造函数,在Settings添加了SharedUserSetting,6种系统的uid:system radio log nfc bluetooth shell。相同shareUserId的包可以运行在一个进程中。


SystemConfig

继续分析构造函数:

  1. SystemConfig systemConfig = SystemConfig.getInstance();  
  2. mGlobalGids = systemConfig.getGlobalGids();  
  3. mSystemPermissions = systemConfig.getSystemPermissions();  
  4. mAvailableFeatures = systemConfig.getAvailableFeatures();  

SystemConfig构造函数中会去读取etc下面的各个文件分析

  1. SystemConfig() {  
  2.     // Read configuration from system  
  3.     readPermissions(Environment.buildPath(  
  4.             Environment.getRootDirectory(), "etc""sysconfig"), false);  
  5.     // Read configuration from the old permissions dir  
  6.     readPermissions(Environment.buildPath(  
  7.             Environment.getRootDirectory(), "etc""permissions"), false);  
  8.     // Only read features from OEM config  
  9.     readPermissions(Environment.buildPath(  
  10.             Environment.getOemDirectory(), "etc""sysconfig"), true);  
  11.     readPermissions(Environment.buildPath(  
  12.             Environment.getOemDirectory(), "etc""permissions"), true);  
  13. }  

分析完了后,会保存在各个成员变量中。

我们看下system/etc/permissions/platform.xml的一部分内容

  1. ......  
  2.     <permission name="android.permission.MANAGE_VOICE_KEYPHRASES">  
  3.         <group gid="audio" />  
  4.     </permission>  
  5.   
  6.     <permission name="android.permission.ACCESS_FM_RADIO" >  
  7.         <group gid="media" />  
  8.     </permission>  
  9.   
  10.     <!-- ================================================================== -->  
  11.     <!-- ================================================================== -->  
  12.     <!-- ================================================================== -->  
  13.   
  14.     <!-- The following tags are assigning high-level permissions to specific  
  15.          user IDs.  These are used to allow specific core system users to  
  16.          perform the given operations with the higher-level framework.  For  
  17.          example, we give a wide variety of permissions to the shell user  
  18.          since that is the user the adb shell runs under and developers and  
  19.          others should have a fairly open environment in which to  
  20.          interact with the system. -->  
  21.   
  22.     <assign-permission name="android.permission.MODIFY_AUDIO_SETTINGS" uid="media" />  
  23.     <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="media" />  
  24.     <assign-permission name="android.permission.WAKE_LOCK" uid="media" />  
  25.     <assign-permission name="android.permission.UPDATE_DEVICE_STATS" uid="media" />  
  26.     <assign-permission name="android.permission.UPDATE_APP_OPS_STATS" uid="media" />  
  27.   
  28.     <assign-permission name="android.permission.ACCESS_SURFACE_FLINGER" uid="graphics" />  
  29. ......  


继续分析,会创建一个消息线程,会创建一些目录

  1. mHandlerThread = new ServiceThread(TAG,  
  2.         Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);  
  3. mHandlerThread.start();  
  4. mHandler = new PackageHandler(mHandlerThread.getLooper());  
  5. Watchdog.getInstance().addThread(mHandler, WATCHDOG_TIMEOUT);  
  6.   
  7. File dataDir = Environment.getDataDirectory();//存放应用数据目录  
  8. mAppDataDir = new File(dataDir, "data");  
  9. mAppInstallDir = new File(dataDir, "app");//放应用  
  10. mAppLib32InstallDir = new File(dataDir, "app-lib");//native库  
  11. mAsecInternalPath = new File(dataDir, "app-asec").getPath();  
  12. mUserAppDataDir = new File(dataDir, "user");//存放用户数据  
  13. mDrmAppPrivateInstallDir = new File(dataDir, "app-private");  

前面systemconfig解析etc下的文件,然后将Permissions放在mSettings的mPermission,以及共享库放在mSharedLibraries中

  1. ArrayMap<String, SystemConfig.PermissionEntry> permConfig  
  2.         = systemConfig.getPermissions();  
  3. for (int i=0; i<permConfig.size(); i++) {  
  4.     SystemConfig.PermissionEntry perm = permConfig.valueAt(i);  
  5.     BasePermission bp = mSettings.mPermissions.get(perm.name);  
  6.     if (bp == null) {  
  7.         bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);  
  8.         mSettings.mPermissions.put(perm.name, bp);  
  9.     }  
  10.     if (perm.gids != null) {  
  11.         bp.gids = appendInts(bp.gids, perm.gids);  
  12.     }  
  13. }  
  14.   
  15. ArrayMap<String, String> libConfig = systemConfig.getSharedLibraries();  
  16. for (int i=0; i<libConfig.size(); i++) {  
  17.     mSharedLibraries.put(libConfig.keyAt(i),  
  18.             new SharedLibraryEntry(libConfig.valueAt(i), null));  
  19. }  


readLPw函数

继续分析,调用了Settings的readLPw函数。

  1. mRestoredSettings = mSettings.readLPw(this, sUserManager.getUsers(false),  
  2.                     mSdkVersion, mOnlyCore);  

我们来详细分析,先会去看packages-backup.xml有没有,有这个文件说明在写packages.xml的时候系统出问题了,所以在系统启动的时候就要读备份的想xml文件内容。如果没有这个备份文件再去看packages.xml, 然后再去解析xml文件,把解析出来的内容封装在各个对象中保存在mSettings中各个变量中。

  1. boolean readLPw(PackageManagerService service, List<UserInfo> users, int sdkVersion,  
  2.             boolean onlyCore) {  
  3.         FileInputStream str = null;  
  4.         if (mBackupSettingsFilename.exists()) {//是否有备份文件  
  5.             try {  
  6.                 str = new FileInputStream(mBackupSettingsFilename);  
  7.                 mReadMessages.append("Reading from backup settings file\n");  
  8.                 PackageManagerService.reportSettingsProblem(Log.INFO,  
  9.                         "Need to read from backup settings file");  
  10.                 if (mSettingsFilename.exists()) {  
  11.                     // If both the backup and settings file exist, we  
  12.                     // ignore the settings since it might have been  
  13.                     // corrupted.  
  14.                     Slog.w(PackageManagerService.TAG, "Cleaning up settings file "  
  15.                             + mSettingsFilename);  
  16.                     mSettingsFilename.delete();  
  17.                 }  
  18.             } catch (java.io.IOException e) {  
  19.                 // We'll try for the normal settings file.  
  20.             }  
  21.         }  
  22.   
  23.         mPendingPackages.clear();  
  24.         mPastSignatures.clear();  
  25.   
  26.         try {  
  27.             if (str == null) {//这里为空,代表没有备份文件,就看packages.xml文件  
  28.                 if (!mSettingsFilename.exists()) {  
  29.                     mReadMessages.append("No settings file found\n");  
  30.                     PackageManagerService.reportSettingsProblem(Log.INFO,  
  31.                             "No settings file; creating initial state");  
  32.                     mInternalSdkPlatform = mExternalSdkPlatform = sdkVersion;  
  33.                     mFingerprint = Build.FINGERPRINT;  
  34.                     return false;  
  35.                 }  
  36.                 str = new FileInputStream(mSettingsFilename);  
  37.             }  
  38.             XmlPullParser parser = Xml.newPullParser();  
  39.             parser.setInput(str, null);  
  40.   
  41.             int type;  
  42.             while ((type = parser.next()) != XmlPullParser.START_TAG  
  43.                     && type != XmlPullParser.END_DOCUMENT) {  
  44.                 ;  
  45.             }  
  46.   
  47.             if (type != XmlPullParser.START_TAG) {  
  48.                 mReadMessages.append("No start tag found in settings file\n");  
  49.                 PackageManagerService.reportSettingsProblem(Log.WARN,  
  50.                         "No start tag found in package manager settings");  
  51.                 Slog.wtf(PackageManagerService.TAG,  
  52.                         "No start tag found in package manager settings");  
  53.                 return false;  
  54.             }  
  55.   
  56.             int outerDepth = parser.getDepth();  
  57.             while ((type = parser.next()) != XmlPullParser.END_DOCUMENT  
  58.                     && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {  
  59.                 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {  
  60.                     continue;  
  61.                 }  
  62.   
  63.                 String tagName = parser.getName();  
  64.                 if (tagName.equals("package")) {  
  65.                     readPackageLPw(parser);//解析packages.xml中的各内容  
  66.                 } else if (tagName.equals("permissions")) {  
  67.                     readPermissionsLPw(mPermissions, parser);  
  68.                 } else if (tagName.equals("permission-trees")) {  
  69.                     readPermissionsLPw(mPermissionTrees, parser);  
  70.                 } else if (tagName.equals("shared-user")) {  
  71.                     readSharedUserLPw(parser);  
  72.                 } else if (tagName.equals("preferred-packages")) {  
  73.                     // no longer used.  
  74.                 } else if (tagName.equals("preferred-activities")) {  
  75.                     // Upgrading from old single-user implementation;  
  76.                     // these are the preferred activities for user 0.  
  77.                     readPreferredActivitiesLPw(parser, 0);  
  78.                 } else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) {  
  79.                     // TODO: check whether this is okay! as it is very  
  80.                     // similar to how preferred-activities are treated  
  81.                     readPersistentPreferredActivitiesLPw(parser, 0);  
  82.                 } else if (tagName.equals(TAG_CROSS_PROFILE_INTENT_FILTERS)) {  
  83.                     // TODO: check whether this is okay! as it is very  
  84.                     // similar to how preferred-activities are treated  
  85.                     readCrossProfileIntentFiltersLPw(parser, 0);  
  86.                 } else if (tagName.equals("updated-package")) {  
  87.                     readDisabledSysPackageLPw(parser);  
  88.                 } else if (tagName.equals("cleaning-package")) {  
  89.                     String name = parser.getAttributeValue(null, ATTR_NAME);  
  90.                     String userStr = parser.getAttributeValue(null, ATTR_USER);  
  91.                     String codeStr = parser.getAttributeValue(null, ATTR_CODE);  
  92.                     if (name != null) {  
  93.                         int userId = 0;  
  94.                         boolean andCode = true;  
  95.                         try {  
  96.                             if (userStr != null) {  
  97.                                 userId = Integer.parseInt(userStr);  
  98.                             }  
  99.                         } catch (NumberFormatException e) {  
  100.                         }  
  101.                         if (codeStr != null) {  
  102.                             andCode = Boolean.parseBoolean(codeStr);  
  103.                         }  
  104.                         addPackageToCleanLPw(new PackageCleanItem(userId, name, andCode));  
  105.                     }  
  106.                 } else if (tagName.equals("renamed-package")) {  
  107.                     String nname = parser.getAttributeValue(null"new");  
  108.                     String oname = parser.getAttributeValue(null"old");  
  109.                     if (nname != null && oname != null) {  
  110.                         mRenamedPackages.put(nname, oname);  
  111.                     }  
  112.                 } else if (tagName.equals("last-platform-version")) {  
  113.                     mInternalSdkPlatform = mExternalSdkPlatform = 0;  
  114.                     try {  
  115.                         String internal = parser.getAttributeValue(null"internal");  
  116.                         if (internal != null) {  
  117.                             mInternalSdkPlatform = Integer.parseInt(internal);  
  118.                         }  
  119.                         String external = parser.getAttributeValue(null"external");  
  120.                         if (external != null) {  
  121.                             mExternalSdkPlatform = Integer.parseInt(external);  
  122.                         }  
  123.                     } catch (NumberFormatException e) {  
  124.                     }  
  125.                     mFingerprint = parser.getAttributeValue(null"fingerprint");  
  126.                 } else if (tagName.equals("database-version")) {  
  127.                     mInternalDatabaseVersion = mExternalDatabaseVersion = 0;  
  128.                     try {  
  129.                         String internalDbVersionString = parser.getAttributeValue(null"internal");  
  130.                         if (internalDbVersionString != null) {  
  131.                             mInternalDatabaseVersion = Integer.parseInt(internalDbVersionString);  
  132.                         }  
  133.                         String externalDbVersionString = parser.getAttributeValue(null"external");  
  134.                         if (externalDbVersionString != null) {  
  135.                             mExternalDatabaseVersion = Integer.parseInt(externalDbVersionString);  
  136.                         }  
  137.                     } catch (NumberFormatException ignored) {  
  138.                     }  
  139.                 } else if (tagName.equals("verifier")) {  
  140.                     final String deviceIdentity = parser.getAttributeValue(null"device");  
  141.                     try {  
  142.                         mVerifierDeviceIdentity = VerifierDev  
  143. ......  



scanDirLI函数

private void scanDirLI(File dir, int parseFlags, int scanFlags, long currentTime) {
        final File[] files = dir.listFiles();
        if (ArrayUtils.isEmpty(files)) {
            Log.d(TAG, "No files in app dir " + dir);
            return;
        }

        if (DEBUG_PACKAGE_SCANNING) {
            Log.d(TAG, "Scanning app dir " + dir + " scanFlags=" + scanFlags
                    + " flags=0x" + Integer.toHexString(parseFlags));
        }

        long startScan, endScan;
        for (File file : files) {
            final boolean isPackage = (isApkFile(file) || file.isDirectory())
                    && !PackageInstallerService.isStageName(file.getName());
            if (!isPackage) {
                // Ignore entries which are not packages
                continue;
            }
            try {
                startScan = SystemClock.uptimeMillis();
                Log.d(TAG," scan package: " + file.toString() + " , start at: " + startScan + "ms.");
                scanPackageLI(file, parseFlags | PackageParser.PARSE_MUST_BE_APK,
                        scanFlags, currentTime, null);
                endScan = SystemClock.uptimeMillis();
                Log.d(TAG," scan package: " + file.toString() + " , end at: " + endScan + "ms. elapsed time = " + (endScan - startScan) + "ms.");
            } catch (PackageManagerException e) {
                Slog.w(TAG, "Failed to parse " + file + ": " + e.getMessage());

                // Delete invalid userdata apps
                if ((parseFlags & PackageParser.PARSE_IS_SYSTEM) == 0 &&
                        e.error == PackageManager.INSTALL_FAILED_INVALID_APK) {
                    logCriticalInfo(Log.WARN, "Deleting invalid package at " + file);
                    if (file.isDirectory()) {
                        mInstaller.rmPackageDir(file.getAbsolutePath());
                    } else {
                        file.delete();
                    }
                }
            }
        }
    }


scanPackageLI函数

 /*
     *  Scan a package and return the newly parsed package.
     *  Returns null in case of errors and the error code is stored in mLastScanError
     */
    private PackageParser.Package scanPackageLI(File scanFile, int parseFlags, int scanFlags,
            long currentTime, UserHandle user) throws PackageManagerException {
        if (DEBUG_INSTALL) Slog.d(TAG, "Parsing: " + scanFile);
        parseFlags |= mDefParseFlags;
        PackageParser pp = new PackageParser();
        pp.setSeparateProcesses(mSeparateProcesses);
        pp.setOnlyCoreApps(mOnlyCore);
        pp.setDisplayMetrics(mMetrics);
        /* 
         * 各个参数的值如下: 
        mSeparateProcesses = null,因为前面初始化值的时候separateProcesses为null 
        mOnlyCore = false   正常情况下mOnlyCore为flase 
        mMetrics 为前面获取到的手机的分辨率 
        mDefParseFlags 的值为0 
        parseFlags的值会随着scanFile的变化而变化 
       */
        
        if ((scanFlags & SCAN_TRUSTED_OVERLAY) != 0) {
            parseFlags |= PackageParser.PARSE_TRUSTED_OVERLAY;
        }

        final PackageParser.Package pkg;
        try {
            pkg = pp.parsePackage(scanFile, parseFlags);
        } catch (PackageParserException e) {
            throw PackageManagerException.from(e);
        }

        PackageSetting ps = null;
        PackageSetting updatedPkg;
        // reader
        synchronized (mPackages) {
            // Look to see if we already know about this package.
            String oldName = mSettings.mRenamedPackages.get(pkg.packageName);
            if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName)) {
                // This package has been renamed to its original name.  Let's
                // use that.
                ps = mSettings.peekPackageLPr(oldName);
            }
            // If there was no original package, see one for the real package name.
            if (ps == null) {
                ps = mSettings.peekPackageLPr(pkg.packageName);
            }
            // 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 && (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
            // If new package is not located in "/system/priv-app" (e.g. due to an OTA),
            // it needs to drop FLAG_PRIVILEGED.
            if (locationIsPrivileged(scanFile)) {
                updatedPkg.pkgPrivateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
            } else {
                updatedPkg.pkgPrivateFlags &= ~ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
            }

            if (ps != null && !ps.codePath.equals(scanFile)) {
                // The path has changed from what was last scanned...  check the
                // version of the new path against what we have stored to determine
                // what to do.
                if (DEBUG_INSTALL) Slog.d(TAG, "Path changing from " + ps.codePath);
                if (pkg.mVersionCode <= ps.versionCode) {
                    // The system package has been updated and the code path does not match
                    // Ignore entry. Skip it.
                    if (DEBUG_INSTALL) Slog.i(TAG, "Package " + ps.name + " at " + scanFile
                            + " ignored: updated version " + ps.versionCode
                            + " better than this " + pkg.mVersionCode);
                    if (!updatedPkg.codePath.equals(scanFile)) {
                        Slog.w(PackageManagerService.TAG, "Code path for hidden system pkg : "
                                + ps.name + " changing from " + updatedPkg.codePathString
                                + " to " + scanFile);
                        updatedPkg.codePath = scanFile;
                        updatedPkg.codePathString = scanFile.toString();
                        updatedPkg.resourcePath = scanFile;
                        updatedPkg.resourcePathString = scanFile.toString();
                    }
                    updatedPkg.pkg = pkg;
                    throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE,
                            "Package " + ps.name + " at " + scanFile
                                    + " ignored: updated version " + ps.versionCode
                                    + " better than this " + pkg.mVersionCode);
                } else {
                    // The current app on the system partition is better than
                    // what we have updated to on the data partition; switch
                    // back to the system partition version.
                    // At this point, its safely assumed that package installation for
                    // apps in system partition will go through. If not there won't be a working
                    // version of the app
                    // writer
                    synchronized (mPackages) {
                        // Just remove the loaded entries from package lists.
                        mPackages.remove(ps.name);
                    }

                    logCriticalInfo(Log.WARN, "Package " + ps.name + " at " + scanFile
                            + " reverting from " + ps.codePathString
                            + ": new version " + pkg.mVersionCode
                            + " better than installed " + ps.versionCode);

                    InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
                            ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
                    synchronized (mInstallLock) {
                        args.cleanUpResourcesLI();
                    }
                    synchronized (mPackages) {
                        mSettings.enableSystemPackageLPw(ps.name);
                    }
                    updatedPkgBetter = true;
                }
            }
        }

        if (updatedPkg != null) {
            // An updated system app will not have the PARSE_IS_SYSTEM flag set
            // initially
            parseFlags |= PackageParser.PARSE_IS_SYSTEM;

            // An updated privileged app will not have the PARSE_IS_PRIVILEGED
            // flag set initially
            if ((updatedPkg.pkgPrivateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
                parseFlags |= PackageParser.PARSE_IS_PRIVILEGED;
            }
        }

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

        /*
         * A new system app appeared, but we already had a non-system one of the
         * same 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) {
                logCriticalInfo(Log.WARN, "Package " + ps.name + " appeared on system, but"
                        + " signatures don't match existing userdata copy; removing");
                deletePackageLI(pkg.packageName, null, true, null, null, 0, null, false);
                ps = null;
            } else {
                /*
                 * If the newly-added system app is an older version than the
                 * already installed version, hide it. It will be scanned later
                 * and re-added like an update.
                 */
                if (pkg.mVersionCode <= ps.versionCode) {
                    shouldHideSystemApp = true;
                    logCriticalInfo(Log.INFO, "Package " + ps.name + " appeared at " + scanFile
                            + " but new version " + pkg.mVersionCode + " better than installed "
                            + ps.versionCode + "; hiding system");
                } 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.
                     */
                    logCriticalInfo(Log.WARN, "Package " + ps.name + " at " + scanFile
                            + " reverting from " + ps.codePathString + ": new version "
                            + pkg.mVersionCode + " better than installed " + ps.versionCode);
                    InstallArgs args = createInstallArgsForExisting(packageFlagsToInstallFlags(ps),
                            ps.codePathString, ps.resourcePathString, getAppDexInstructionSets(ps));
                    synchronized (mInstallLock) {
                        args.cleanUpResourcesLI();
                    }
                }
            }
        }

        // The apk is forward locked (not public) if its code and resources
        // are kept in different files. (except for app in either system or
        // vendor path).
        // TODO grab this value from PackageSettings
        if ((parseFlags & PackageParser.PARSE_IS_SYSTEM_DIR) == 0) {
            if (ps != null && !ps.codePath.equals(ps.resourcePath)) {
                parseFlags |= PackageParser.PARSE_FORWARD_LOCK;
            }
        }

        // TODO: extend to support forward-locked splits
        String resourcePath = null;
        String baseResourcePath = null;
        if ((parseFlags & PackageParser.PARSE_FORWARD_LOCK) != 0 && !updatedPkgBetter) {
            if (ps != null && ps.resourcePathString != null) {
                resourcePath = ps.resourcePathString;
                baseResourcePath = ps.resourcePathString;
            } else {
                // Should not happen at all. Just log an error.
                Slog.e(TAG, "Resource path not set for pkg : " + pkg.packageName);
            }
        } else {
            resourcePath = pkg.codePath;
            baseResourcePath = pkg.baseCodePath;
        }

        // Set application objects path explicitly.
        pkg.applicationInfo.volumeUuid = pkg.volumeUuid;
        pkg.applicationInfo.setCodePath(pkg.codePath);
        pkg.applicationInfo.setBaseCodePath(pkg.baseCodePath);
        pkg.applicationInfo.setSplitCodePaths(pkg.splitCodePaths);
        pkg.applicationInfo.setResourcePath(resourcePath);
        pkg.applicationInfo.setBaseResourcePath(baseResourcePath);
        pkg.applicationInfo.setSplitResourcePaths(pkg.splitCodePaths);

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

        /*
         * If the system app should be overridden by a previously installed
         * data, hide the system app now and let the /data/app scan pick it up
         * again.
         */
        if (shouldHideSystemApp) {
            synchronized (mPackages) {
                mSettings.disableSystemPackageLPw(pkg.packageName);
            }
        }

        return scannedPkg;
    }

updatePermissionsLPw函数

 private void updatePermissionsLPw(String changingPkg, PackageParser.Package pkgInfo,
            int flags) {
        final String volumeUuid = (pkgInfo != null) ? getVolumeUuidForPackage(pkgInfo) : null;
        updatePermissionsLPw(changingPkg, pkgInfo, volumeUuid, flags);
    }

    private void updatePermissionsLPw(String changingPkg,
            PackageParser.Package pkgInfo, String replaceVolumeUuid, int flags) {
        // Make sure there are no dangling permission trees.
        Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator();
        while (it.hasNext()) {
            final BasePermission bp = it.next();
            if (bp.packageSetting == null) {
                // We may not yet have parsed the package, so just see if
                // we still know about its settings.
                bp.packageSetting = mSettings.mPackages.get(bp.sourcePackage);
            }
            if (bp.packageSetting == null) {
                Slog.w(TAG, "Removing dangling permission tree: " + bp.name
                        + " from package " + bp.sourcePackage);
                it.remove();
            } else if (changingPkg != null && changingPkg.equals(bp.sourcePackage)) {
                if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) {
                    Slog.i(TAG, "Removing old permission tree: " + bp.name
                            + " from package " + bp.sourcePackage);
                    flags |= UPDATE_PERMISSIONS_ALL;
                    it.remove();
                }
            }
        }

        // Make sure all dynamic permissions have been assigned to a package,
        // and make sure there are no dangling permissions.
        it = mSettings.mPermissions.values().iterator();
        while (it.hasNext()) {
            final BasePermission bp = it.next();
            if (bp.type == BasePermission.TYPE_DYNAMIC) {
                if (DEBUG_SETTINGS) Log.v(TAG, "Dynamic permission: name="
                        + bp.name + " pkg=" + bp.sourcePackage
                        + " info=" + bp.pendingInfo);
                if (bp.packageSetting == null && bp.pendingInfo != null) {
                    final BasePermission tree = findPermissionTreeLP(bp.name);
                    if (tree != null && tree.perm != null) {
                        bp.packageSetting = tree.packageSetting;
                        bp.perm = new PackageParser.Permission(tree.perm.owner,
                                new PermissionInfo(bp.pendingInfo));
                        bp.perm.info.packageName = tree.perm.info.packageName;
                        bp.perm.info.name = bp.name;
                        bp.uid = tree.uid;
                    }
                }
            }
            if (bp.packageSetting == null) {
                // We may not yet have parsed the package, so just see if
                // we still know about its settings.
                bp.packageSetting = mSettings.mPackages.get(bp.sourcePackage);
            }
            if (bp.packageSetting == null) {
                Slog.w(TAG, "Removing dangling permission: " + bp.name
                        + " from package " + bp.sourcePackage);
                it.remove();
            } else if (changingPkg != null && changingPkg.equals(bp.sourcePackage)) {
                if (pkgInfo == null || !hasPermission(pkgInfo, bp.name)) {
                    Slog.i(TAG, "Removing old permission: " + bp.name
                            + " from package " + bp.sourcePackage);
                    flags |= UPDATE_PERMISSIONS_ALL;
                    it.remove();
                }
            }
        }

        // Now update the permissions for all packages, in particular
        // replace the granted permissions of the system packages.
        if ((flags&UPDATE_PERMISSIONS_ALL) != 0) {
            for (PackageParser.Package pkg : mPackages.values()) {
                if (pkg != pkgInfo) {
                    // Only replace for packages on requested volume
                    final String volumeUuid = getVolumeUuidForPackage(pkg);
                    final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_ALL) != 0)
                            && Objects.equals(replaceVolumeUuid, volumeUuid);
                    grantPermissionsLPw(pkg, replace, changingPkg);
                }
            }
        }

        if (pkgInfo != null) {
            // Only replace for packages on requested volume
            final String volumeUuid = getVolumeUuidForPackage(pkgInfo);
            final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_PKG) != 0)
                    && Objects.equals(replaceVolumeUuid, volumeUuid);
            grantPermissionsLPw(pkgInfo, replace, changingPkg);
        }
    }

grantPermissionsLPw函数

private void grantPermissionsLPw(PackageParser.Package pkg, boolean replace,
            String packageOfInterest) {
        // IMPORTANT: There are two types of permissions: install and runtime.
        // Install time permissions are granted when the app is installed to
        // all device users and users added in the future. Runtime permissions
        // are granted at runtime explicitly to specific users. Normal and signature
        // protected permissions are install time permissions. Dangerous permissions
        // are install permissions if the app's target SDK is Lollipop MR1 or older,
        // otherwise they are runtime permissions. This function does not manage
        // runtime permissions except for the case an app targeting Lollipop MR1
        // being upgraded to target a newer SDK, in which case dangerous permissions
        // are transformed from install time to runtime ones.

        final PackageSetting ps = (PackageSetting) pkg.mExtras;
        if (ps == null) {
            return;
        }

        PermissionsState permissionsState = ps.getPermissionsState();
        PermissionsState origPermissions = permissionsState;

        final int[] currentUserIds = UserManagerService.getInstance().getUserIds();

        boolean runtimePermissionsRevoked = false;
        int[] changedRuntimePermissionUserIds = EMPTY_INT_ARRAY;

        boolean changedInstallPermission = false;

        if (replace) {
            ps.installPermissionsFixed = false;
            if (!ps.isSharedUser()) {
                origPermissions = new PermissionsState(permissionsState);
                permissionsState.reset();
            } else {
                // We need to know only about runtime permission changes since the
                // calling code always writes the install permissions state but
                // the runtime ones are written only if changed. The only cases of
                // changed runtime permissions here are promotion of an install to
                // runtime and revocation of a runtime from a shared user.
                changedRuntimePermissionUserIds = revokeUnusedSharedUserPermissionsLPw(
                        ps.sharedUser, UserManagerService.getInstance().getUserIds());
                if (!ArrayUtils.isEmpty(changedRuntimePermissionUserIds)) {
                    runtimePermissionsRevoked = true;
                }
            }
        }

        permissionsState.setGlobalGids(mGlobalGids);

        final int N = pkg.requestedPermissions.size();
        for (int i=0; i<N; i++) {
            final String name = pkg.requestedPermissions.get(i);
            final BasePermission bp = mSettings.mPermissions.get(name);

            if (DEBUG_INSTALL) {
                Log.i(TAG, "Package " + pkg.packageName + " checking " + name + ": " + bp);
            }

            if (bp == null || bp.packageSetting == null) {
                if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
                    Slog.w(TAG, "Unknown permission " + name
                            + " in package " + pkg.packageName);
                }
                continue;
            }

            final String perm = bp.name;
            boolean allowedSig = false;
            int grant = GRANT_DENIED;

            // Keep track of app op permissions.
            if ((bp.protectionLevel & PermissionInfo.PROTECTION_FLAG_APPOP) != 0) {
                ArraySet<String> pkgs = mAppOpPermissionPackages.get(bp.name);
                if (pkgs == null) {
                    pkgs = new ArraySet<>();
                    mAppOpPermissionPackages.put(bp.name, pkgs);
                }
                pkgs.add(pkg.packageName);
            }

            final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE;
            switch (level) {
                case PermissionInfo.PROTECTION_NORMAL: {
                    // For all apps normal permissions are install time ones.
                    grant = GRANT_INSTALL;
                } break;

                case PermissionInfo.PROTECTION_DANGEROUS: {
                    if (pkg.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1){
                        // For legacy apps dangerous permissions are install time ones.
                        grant = GRANT_INSTALL_LEGACY;
                    } else if (origPermissions.hasInstallPermission(bp.name)) {
                        // For legacy apps that became modern, install becomes runtime.
                        grant = GRANT_UPGRADE;
                    } else if (mPromoteSystemApps
                            && isSystemApp(ps)
                            && mExistingSystemPackages.contains(ps.name)) {
                        // For legacy system apps, install becomes runtime.
                        // We cannot check hasInstallPermission() for system apps since those
                        // permissions were granted implicitly and not persisted pre-M.
                        grant = GRANT_UPGRADE;
                    } else {
                        // For modern apps keep runtime permissions unchanged.
                        grant = GRANT_RUNTIME;
                    }
                } break;

                case PermissionInfo.PROTECTION_SIGNATURE: {
                    // For all apps signature permissions are install time ones.
                    allowedSig = grantSignaturePermission(perm, pkg, bp, origPermissions);
                    if (allowedSig) {
                        grant = GRANT_INSTALL;
                    }
                } break;
            }

            if (DEBUG_INSTALL) {
                Log.i(TAG, "Package " + pkg.packageName + " granting " + perm);
            }

            if (grant != GRANT_DENIED) {
                if (!isSystemApp(ps) && ps.installPermissionsFixed) {
                    // If this is an existing, non-system package, then
                    // we can't add any new permissions to it.
                    if (!allowedSig && !origPermissions.hasInstallPermission(perm)) {
                        // Except...  if this is a permission that was added
                        // to the platform (note: need to only do this when
                        // updating the platform).
                        if (!isNewPlatformPermissionForPackage(perm, pkg)) {
                            grant = GRANT_DENIED;
                        }
                    }
                }

                switch (grant) {
                    case GRANT_INSTALL: {
                        // Revoke this as runtime permission to handle the case of
                        // a runtime permission being downgraded to an install one.
                        for (int userId : UserManagerService.getInstance().getUserIds()) {
                            if (origPermissions.getRuntimePermissionState(
                                    bp.name, userId) != null) {
                                // Revoke the runtime permission and clear the flags.
                                origPermissions.revokeRuntimePermission(bp, userId);
                                origPermissions.updatePermissionFlags(bp, userId,
                                      PackageManager.MASK_PERMISSION_FLAGS, 0);
                                // If we revoked a permission permission, we have to write.
                                changedRuntimePermissionUserIds = ArrayUtils.appendInt(
                                        changedRuntimePermissionUserIds, userId);
                            }
                        }
                        // Grant an install permission.
                        if (permissionsState.grantInstallPermission(bp) !=
                                PermissionsState.PERMISSION_OPERATION_FAILURE) {
                            changedInstallPermission = true;
                        }
                    } break;

                    case GRANT_INSTALL_LEGACY: {
                        // Grant an install permission.
                        if (permissionsState.grantInstallPermission(bp) !=
                                PermissionsState.PERMISSION_OPERATION_FAILURE) {
                            changedInstallPermission = true;
                        }
                    } break;

                    case GRANT_RUNTIME: {
                        // Grant previously granted runtime permissions.
                        for (int userId : UserManagerService.getInstance().getUserIds()) {
                            PermissionState permissionState = origPermissions
                                    .getRuntimePermissionState(bp.name, userId);
                            final int flags = permissionState != null
                                    ? permissionState.getFlags() : 0;
                            if (origPermissions.hasRuntimePermission(bp.name, userId)) {
                                if (permissionsState.grantRuntimePermission(bp, userId) ==
                                        PermissionsState.PERMISSION_OPERATION_FAILURE) {
                                    // If we cannot put the permission as it was, we have to write.
                                    changedRuntimePermissionUserIds = ArrayUtils.appendInt(
                                            changedRuntimePermissionUserIds, userId);
                                }
                            }
                            // Propagate the permission flags.
                            permissionsState.updatePermissionFlags(bp, userId, flags, flags);
                        }
                    } break;

                    case GRANT_UPGRADE: {
                        // Grant runtime permissions for a previously held install permission.
                        PermissionState permissionState = origPermissions
                                .getInstallPermissionState(bp.name);
                        final int flags = permissionState != null ? permissionState.getFlags() : 0;

                        if (origPermissions.revokeInstallPermission(bp)
                                != PermissionsState.PERMISSION_OPERATION_FAILURE) {
                            // We will be transferring the permission flags, so clear them.
                            origPermissions.updatePermissionFlags(bp, UserHandle.USER_ALL,
                                    PackageManager.MASK_PERMISSION_FLAGS, 0);
                            changedInstallPermission = true;
                        }

                        // If the permission is not to be promoted to runtime we ignore it and
                        // also its other flags as they are not applicable to install permissions.
                        if ((flags & PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE) == 0) {
                            for (int userId : currentUserIds) {
                                if (permissionsState.grantRuntimePermission(bp, userId) !=
                                        PermissionsState.PERMISSION_OPERATION_FAILURE) {
                                    // Transfer the permission flags.
                                    permissionsState.updatePermissionFlags(bp, userId,
                                            flags, flags);
                                    // If we granted the permission, we have to write.
                                    changedRuntimePermissionUserIds = ArrayUtils.appendInt(
                                            changedRuntimePermissionUserIds, userId);
                                }
                            }
                        }
                    } break;

                    default: {
                        if (packageOfInterest == null
                                || packageOfInterest.equals(pkg.packageName)) {
                            Slog.w(TAG, "Not granting permission " + perm
                                    + " to package " + pkg.packageName
                                    + " because it was previously installed without");
                        }
                    } break;
                }
            } else {
                if (permissionsState.revokeInstallPermission(bp) !=
                        PermissionsState.PERMISSION_OPERATION_FAILURE) {
                    // Also drop the permission flags.
                    permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
                            PackageManager.MASK_PERMISSION_FLAGS, 0);
                    changedInstallPermission = true;
                    Slog.i(TAG, "Un-granting permission " + perm
                            + " from package " + pkg.packageName
                            + " (protectionLevel=" + bp.protectionLevel
                            + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
                            + ")");
                } else if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) == 0) {
                    // Don't print warning for app op permissions, since it is fine for them
                    // not to be granted, there is a UI for the user to decide.
                    if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
                        Slog.w(TAG, "Not granting permission " + perm
                                + " to package " + pkg.packageName
                                + " (protectionLevel=" + bp.protectionLevel
                                + " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
                                + ")");
                    }
                }
            }
        }

        String value = SystemProperties.get("persist.sys.vidoo.applist", null);
        if(null != value){
            String appList[] = value.split(",");
            //String[] appList = {"com.sharpnow.vidoo.vrplayer", "com.dk.startbootfirst"};
            if(Arrays.asList(appList).contains(pkg.packageName)) {
                final int permsSize = pkg.requestedPermissions.size();
                for (int i=0; i<permsSize; i++) {
                    final String name = pkg.requestedPermissions.get(i);
                    final BasePermission bp = mSettings.mPermissions.get(name);
                    if(null != bp && permissionsState.grantInstallPermission(bp) != PermissionsState.PERMISSION_OPERATION_FAILURE) {
                        Slog.d(TAG, "---perm&package grant permission " + name
                                + " to package " + pkg.packageName);
                        changedInstallPermission = true;
                    }
                }
            }
        }

        if ((changedInstallPermission || replace) && !ps.installPermissionsFixed &&
                !isSystemApp(ps) || isUpdatedSystemApp(ps)){
            // This is the first that we have heard about this package, so the
            // permissions we have now selected are fixed until explicitly
            // changed.
            ps.installPermissionsFixed = true;
        }

        // Persist the runtime permissions state for users with changes. If permissions
        // were revoked because no app in the shared user declares them we have to
        // write synchronously to avoid losing runtime permissions state.
        for (int userId : changedRuntimePermissionUserIds) {
            mSettings.writeRuntimePermissionsForUserLPr(userId, runtimePermissionsRevoked);
        }
    }


评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值