diff options
Diffstat (limited to 'services/core/java/com/android/server')
-rw-r--r-- | services/core/java/com/android/server/am/ActivityManagerService.java | 7 | ||||
-rw-r--r-- | services/core/java/com/android/server/am/BroadcastQueue.java | 2 | ||||
-rw-r--r-- | services/core/java/com/android/server/pm/BasePermission.java | 5 | ||||
-rw-r--r-- | services/core/java/com/android/server/pm/PackageManagerService.java | 500 | ||||
-rw-r--r-- | services/core/java/com/android/server/pm/PackageSetting.java | 6 | ||||
-rw-r--r-- | services/core/java/com/android/server/pm/PackageSettingBase.java | 10 | ||||
-rw-r--r-- | services/core/java/com/android/server/pm/PermissionsState.java | 523 | ||||
-rw-r--r-- | services/core/java/com/android/server/pm/SettingBase.java (renamed from services/core/java/com/android/server/pm/GrantedPermissions.java) | 21 | ||||
-rw-r--r-- | services/core/java/com/android/server/pm/Settings.java | 696 | ||||
-rw-r--r-- | services/core/java/com/android/server/pm/SharedUserSetting.java | 2 |
10 files changed, 1421 insertions, 351 deletions
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java index 11c3ea6..fa1a1f1 100644 --- a/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/services/core/java/com/android/server/am/ActivityManagerService.java @@ -76,6 +76,7 @@ import com.android.internal.os.IResultReceiver; import com.android.internal.os.ProcessCpuTracker; import com.android.internal.os.TransferPipe; import com.android.internal.os.Zygote; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastPrintWriter; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.MemInfoReader; @@ -3066,7 +3067,7 @@ public final class ActivityManagerService extends ActivityManagerNative * Add shared application and profile GIDs so applications can share some * resources like shared libraries and access user-wide resources */ - if (permGids == null) { + if (ArrayUtils.isEmpty(permGids)) { gids = new int[2]; } else { gids = new int[permGids.length + 2]; @@ -6530,7 +6531,7 @@ public final class ActivityManagerService extends ActivityManagerNative if (permission == null) { return PackageManager.PERMISSION_DENIED; } - return checkComponentPermission(permission, pid, UserHandle.getAppId(uid), -1, true); + return checkComponentPermission(permission, pid, uid, -1, true); } @Override @@ -6550,7 +6551,7 @@ public final class ActivityManagerService extends ActivityManagerNative pid = tlsIdentity.pid; } - return checkComponentPermission(permission, pid, UserHandle.getAppId(uid), -1, true); + return checkComponentPermission(permission, pid, uid, -1, true); } /** diff --git a/services/core/java/com/android/server/am/BroadcastQueue.java b/services/core/java/com/android/server/am/BroadcastQueue.java index 8fe1238..34c1c53 100644 --- a/services/core/java/com/android/server/am/BroadcastQueue.java +++ b/services/core/java/com/android/server/am/BroadcastQueue.java @@ -765,7 +765,7 @@ public final class BroadcastQueue { try { perm = AppGlobals.getPackageManager(). checkPermission(r.requiredPermission, - info.activityInfo.applicationInfo.packageName); + info.activityInfo.applicationInfo.packageName, r.userId); } catch (RemoteException e) { perm = PackageManager.PERMISSION_DENIED; } diff --git a/services/core/java/com/android/server/pm/BasePermission.java b/services/core/java/com/android/server/pm/BasePermission.java index 4f27408..138a146 100644 --- a/services/core/java/com/android/server/pm/BasePermission.java +++ b/services/core/java/com/android/server/pm/BasePermission.java @@ -56,4 +56,9 @@ final class BasePermission { return "BasePermission{" + Integer.toHexString(System.identityHashCode(this)) + " " + name + "}"; } + + public boolean isRuntime() { + return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) + == PermissionInfo.PROTECTION_DANGEROUS; + } } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 52411bf..5d20528 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -54,7 +54,6 @@ import static com.android.internal.app.IntentForwarderActivity.FORWARD_INTENT_TO import static com.android.internal.content.NativeLibraryHelper.LIB64_DIR_NAME; import static com.android.internal.content.NativeLibraryHelper.LIB_DIR_NAME; import static com.android.internal.util.ArrayUtils.appendInt; -import static com.android.internal.util.ArrayUtils.removeInt; import static com.android.server.pm.InstructionSets.getAppDexInstructionSets; import static com.android.server.pm.InstructionSets.getDexCodeInstructionSet; import static com.android.server.pm.InstructionSets.getDexCodeInstructionSets; @@ -249,6 +248,9 @@ public class PackageManagerService extends IPackageManager.Stub { private static final boolean DEBUG_DEXOPT = false; private static final boolean DEBUG_ABI_SELECTION = false; + private static final boolean RUNTIME_PERMISSIONS_ENABLED = + SystemProperties.getInt("ro.runtime.premissions.enabled", 0) == 1; + private static final int RADIO_UID = Process.PHONE_UID; private static final int LOG_UID = Process.LOG_UID; private static final int NFC_UID = Process.NFC_UID; @@ -321,10 +323,28 @@ public class PackageManagerService extends IPackageManager.Stub { DEFAULT_CONTAINER_PACKAGE, "com.android.defcontainer.DefaultContainerService"); + private static final String KILL_APP_REASON_GIDS_CHANGED = + "permission grant or revoke changed gids"; + + private static final String KILL_APP_REASON_PERMISSIONS_REVOKED = + "permissions revoked"; + private static final String PACKAGE_MIME_TYPE = "application/vnd.android.package-archive"; private static final String VENDOR_OVERLAY_DIR = "/vendor/overlay"; + /** Permission grant: not grant the permission. */ + private static final int GRANT_DENIED = 1; + + /** Permission grant: grant the permission as an install permission. */ + private static final int GRANT_INSTALL = 2; + + /** Permission grant: grant the permission as a runtime permission. */ + private static final int GRANT_RUNTIME = 3; + + /** Permission grant: grant as runtime a permission that was granted as an install time one. */ + private static final int GRANT_UPGRADE = 4; + final ServiceThread mHandlerThread; final PackageHandler mHandler; @@ -1243,7 +1263,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } - public static final PackageManagerService main(Context context, Installer installer, + public static PackageManagerService main(Context context, Installer installer, boolean factoryTest, boolean onlyCore) { PackageManagerService m = new PackageManagerService(context, installer, factoryTest, onlyCore); @@ -1293,7 +1313,7 @@ public class PackageManagerService extends IPackageManager.Stub { mOnlyCore = onlyCore; mLazyDexOpt = "eng".equals(SystemProperties.get("ro.build.type")); mMetrics = new DisplayMetrics(); - mSettings = new Settings(context); + mSettings = new Settings(mContext, mPackages); mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID, ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED); mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID, @@ -1832,14 +1852,8 @@ public class PackageManagerService extends IPackageManager.Stub { final String packageName = info.activityInfo.packageName; - final PackageSetting ps = mSettings.mPackages.get(packageName); - if (ps == null) { - continue; - } - - final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps; - if (!gp.grantedPermissions - .contains(android.Manifest.permission.PACKAGE_VERIFICATION_AGENT)) { + if (checkPermission(android.Manifest.permission.PACKAGE_VERIFICATION_AGENT, + packageName, UserHandle.USER_OWNER) != PackageManager.PERMISSION_GRANTED) { continue; } @@ -1895,26 +1909,21 @@ public class PackageManagerService extends IPackageManager.Stub { return cur; } - static int[] removeInts(int[] cur, int[] rem) { - if (rem == null) return cur; - if (cur == null) return cur; - final int N = rem.length; - for (int i=0; i<N; i++) { - cur = removeInt(cur, rem[i]); - } - return cur; - } - PackageInfo generatePackageInfo(PackageParser.Package p, int flags, int userId) { if (!sUserManager.exists(userId)) return null; final PackageSetting ps = (PackageSetting) p.mExtras; if (ps == null) { return null; } - final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps; + + PermissionsState permissionsState = ps.getPermissionsState(); + + final int[] gids = permissionsState.computeGids(userId); + Set<String> permissions = permissionsState.getPermissions(userId); + final PackageUserState state = ps.readUserState(userId); - return PackageParser.generatePackageInfo(p, gp.gids, flags, - ps.firstInstallTime, ps.lastUpdateTime, gp.grantedPermissions, + return PackageParser.generatePackageInfo(p, gids, flags, + ps.firstInstallTime, ps.lastUpdateTime, permissions, state, userId); } @@ -1986,6 +1995,7 @@ public class PackageManagerService extends IPackageManager.Stub { public int getPackageUid(String packageName, int userId) { if (!sUserManager.exists(userId)) return -1; enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, "get package uid"); + // reader synchronized (mPackages) { PackageParser.Package p = mPackages.get(packageName); @@ -2002,22 +2012,30 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override - public int[] getPackageGids(String packageName) { + public int[] getPackageGids(String packageName, int userId) throws RemoteException { + if (!sUserManager.exists(userId)) { + return null; + } + + enforceCrossUserPermission(Binder.getCallingUid(), userId, false, false, + "getPackageGids"); + // reader synchronized (mPackages) { PackageParser.Package p = mPackages.get(packageName); - if (DEBUG_PACKAGE_INFO) + if (DEBUG_PACKAGE_INFO) { Log.v(TAG, "getPackageGids" + packageName + ": " + p); + } if (p != null) { - final PackageSetting ps = (PackageSetting)p.mExtras; - return ps.getGids(); + PackageSetting ps = (PackageSetting) p.mExtras; + return ps.getPermissionsState().computeGids(userId); } } - // stupid thing to indicate an error. - return new int[0]; + + return null; } - static final PermissionInfo generatePermissionInfo( + static PermissionInfo generatePermissionInfo( BasePermission bp, int flags) { if (bp.perm != null) { return PackageParser.generatePermissionInfo(bp.perm, flags); @@ -2381,30 +2399,37 @@ public class PackageManagerService extends IPackageManager.Stub { } @Override - public int checkPermission(String permName, String pkgName) { + public int checkPermission(String permName, String pkgName, int userId) { + if (!sUserManager.exists(userId)) { + return PackageManager.PERMISSION_DENIED; + } + synchronized (mPackages) { - PackageParser.Package p = mPackages.get(pkgName); + final PackageParser.Package p = mPackages.get(pkgName); if (p != null && p.mExtras != null) { - PackageSetting ps = (PackageSetting)p.mExtras; - if (ps.sharedUser != null) { - if (ps.sharedUser.grantedPermissions.contains(permName)) { - return PackageManager.PERMISSION_GRANTED; - } - } else if (ps.grantedPermissions.contains(permName)) { + final PackageSetting ps = (PackageSetting) p.mExtras; + if (ps.getPermissionsState().hasPermission(permName, userId)) { return PackageManager.PERMISSION_GRANTED; } } } + return PackageManager.PERMISSION_DENIED; } @Override public int checkUidPermission(String permName, int uid) { + final int userId = UserHandle.getUserId(uid); + + if (!sUserManager.exists(userId)) { + return PackageManager.PERMISSION_DENIED; + } + synchronized (mPackages) { Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid)); if (obj != null) { - GrantedPermissions gp = (GrantedPermissions)obj; - if (gp.grantedPermissions.contains(permName)) { + final SettingBase ps = (SettingBase) obj; + if (ps.getPermissionsState().hasPermission(permName, userId)) { return PackageManager.PERMISSION_GRANTED; } } else { @@ -2414,6 +2439,7 @@ public class PackageManagerService extends IPackageManager.Stub { } } } + return PackageManager.PERMISSION_DENIED; } @@ -2620,120 +2646,114 @@ public class PackageManagerService extends IPackageManager.Stub { } } - private static void checkGrantRevokePermissions(PackageParser.Package pkg, BasePermission bp) { + private static void enforceDeclaredAsUsedAndRuntimePermission(PackageParser.Package pkg, + BasePermission bp) { int index = pkg.requestedPermissions.indexOf(bp.name); if (index == -1) { throw new SecurityException("Package " + pkg.packageName + " has not requested permission " + bp.name); } - boolean isNormal = - ((bp.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE) - == PermissionInfo.PROTECTION_NORMAL); - boolean isDangerous = - ((bp.protectionLevel&PermissionInfo.PROTECTION_MASK_BASE) - == PermissionInfo.PROTECTION_DANGEROUS); - boolean isDevelopment = - ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0); - - if (!isNormal && !isDangerous && !isDevelopment) { + if (!bp.isRuntime()) { throw new SecurityException("Permission " + bp.name + " is not a changeable permission type"); } - - if (isNormal || isDangerous) { - if (pkg.requestedPermissionsRequired.get(index)) { - throw new SecurityException("Can't change " + bp.name - + ". It is required by the application"); - } - } } @Override - public void grantPermission(String packageName, String permissionName) { + public boolean grantPermission(String packageName, String name, int userId) { + if (!sUserManager.exists(userId)) { + return false; + } + mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, null); + android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, + "grantPermission"); + + enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, + "grantPermission"); + synchronized (mPackages) { final PackageParser.Package pkg = mPackages.get(packageName); if (pkg == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } - final BasePermission bp = mSettings.mPermissions.get(permissionName); + + final BasePermission bp = mSettings.mPermissions.get(name); if (bp == null) { - throw new IllegalArgumentException("Unknown permission: " + permissionName); + throw new IllegalArgumentException("Unknown permission: " + name); } - checkGrantRevokePermissions(pkg, bp); + enforceDeclaredAsUsedAndRuntimePermission(pkg, bp); - final PackageSetting ps = (PackageSetting) pkg.mExtras; - if (ps == null) { - return; + final SettingBase sb = (SettingBase) pkg.mExtras; + if (sb == null) { + throw new IllegalArgumentException("Unknown package: " + packageName); } - final GrantedPermissions gp = (ps.sharedUser != null) ? ps.sharedUser : ps; - if (gp.grantedPermissions.add(permissionName)) { - if (ps.haveGids) { - gp.gids = appendInts(gp.gids, bp.gids); + + final PermissionsState permissionsState = sb.getPermissionsState(); + + final int result = permissionsState.grantRuntimePermission(bp, userId); + switch (result) { + case PermissionsState.PERMISSION_OPERATION_FAILURE: { + return false; } - mSettings.writeLPr(); + + case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: { + killSettingPackagesForUser(sb, userId, KILL_APP_REASON_GIDS_CHANGED); + } break; } + + // Not critical if that is lost - app has to request again. + mSettings.writeRuntimePermissionsForUserLPr(userId, false); + + return true; } } @Override - public void revokePermission(String packageName, String permissionName) { - int changedAppId = -1; + public boolean revokePermission(String packageName, String name, int userId) { + if (!sUserManager.exists(userId)) { + return false; + } + + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, + "revokePermission"); + + enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, + "revokePermission"); synchronized (mPackages) { final PackageParser.Package pkg = mPackages.get(packageName); if (pkg == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } - if (pkg.applicationInfo.uid != Binder.getCallingUid()) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.GRANT_REVOKE_PERMISSIONS, null); - } - final BasePermission bp = mSettings.mPermissions.get(permissionName); + + final BasePermission bp = mSettings.mPermissions.get(name); if (bp == null) { - throw new IllegalArgumentException("Unknown permission: " + permissionName); + throw new IllegalArgumentException("Unknown permission: " + name); } - checkGrantRevokePermissions(pkg, bp); + enforceDeclaredAsUsedAndRuntimePermission(pkg, bp); - final PackageSetting ps = (PackageSetting) pkg.mExtras; - if (ps == null) { - return; - } - final GrantedPermissions gp = (ps.sharedUser != null) ? ps.sharedUser : ps; - if (gp.grantedPermissions.remove(permissionName)) { - gp.grantedPermissions.remove(permissionName); - if (ps.haveGids) { - gp.gids = removeInts(gp.gids, bp.gids); - } - mSettings.writeLPr(); - changedAppId = ps.appId; + final SettingBase sb = (SettingBase) pkg.mExtras; + if (sb == null) { + throw new IllegalArgumentException("Unknown package: " + packageName); } - } - if (changedAppId >= 0) { - // We changed the perm on someone, kill its processes. - IActivityManager am = ActivityManagerNative.getDefault(); - if (am != null) { - final int callingUserId = UserHandle.getCallingUserId(); - final long ident = Binder.clearCallingIdentity(); - try { - //XXX we should only revoke for the calling user's app permissions, - // but for now we impact all users. - //am.killUid(UserHandle.getUid(callingUserId, changedAppId), - // "revoke " + permissionName); - int[] users = sUserManager.getUserIds(); - for (int user : users) { - am.killUid(UserHandle.getUid(user, changedAppId), - "revoke " + permissionName); - } - } catch (RemoteException e) { - } finally { - Binder.restoreCallingIdentity(ident); - } + final PermissionsState permissionsState = sb.getPermissionsState(); + + if (permissionsState.revokeRuntimePermission(bp, userId) == + PermissionsState.PERMISSION_OPERATION_FAILURE) { + return false; } + + killSettingPackagesForUser(sb, userId, KILL_APP_REASON_PERMISSIONS_REVOKED); + + // Critical, after this call all should never have the permission. + mSettings.writeRuntimePermissionsForUserLPr(userId, true); + + return true; } } @@ -2794,6 +2814,46 @@ public class PackageManagerService extends IPackageManager.Stub { } } + private void killSettingPackagesForUser(SettingBase sb, int userId, String reason) { + final long identity = Binder.clearCallingIdentity(); + try { + if (sb instanceof SharedUserSetting) { + SharedUserSetting sus = (SharedUserSetting) sb; + final int packageCount = sus.packages.size(); + for (int i = 0; i < packageCount; i++) { + PackageSetting susPs = sus.packages.valueAt(i); + if (userId == UserHandle.USER_ALL) { + killApplication(susPs.pkg.packageName, susPs.appId, reason); + } else { + final int uid = UserHandle.getUid(userId, susPs.appId); + killUid(uid, reason); + } + } + } else if (sb instanceof PackageSetting) { + PackageSetting ps = (PackageSetting) sb; + if (userId == UserHandle.USER_ALL) { + killApplication(ps.pkg.packageName, ps.appId, reason); + } else { + final int uid = UserHandle.getUid(userId, ps.appId); + killUid(uid, reason); + } + } + } finally { + Binder.restoreCallingIdentity(identity); + } + } + + private static void killUid(int uid, String reason) { + IActivityManager am = ActivityManagerNative.getDefault(); + if (am != null) { + try { + am.killUid(uid, reason); + } catch (RemoteException e) { + /* ignore - same process */ + } + } + } + /** * Compares two sets of signatures. Returns: * <br /> @@ -3875,9 +3935,10 @@ public class PackageManagerService extends IPackageManager.Stub { private void addPackageHoldingPermissions(ArrayList<PackageInfo> list, PackageSetting ps, String[] permissions, boolean[] tmp, int flags, int userId) { int numMatch = 0; - final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps; + final PermissionsState permissionsState = ps.getPermissionsState(); for (int i=0; i<permissions.length; i++) { - if (gp.grantedPermissions.contains(permissions[i])) { + final String permission = permissions[i]; + if (permissionsState.hasPermission(permission, userId)) { tmp[i] = true; numMatch++; } else { @@ -6853,36 +6914,42 @@ public class PackageManagerService extends IPackageManager.Stub { 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; } - final GrantedPermissions gp = ps.sharedUser != null ? ps.sharedUser : ps; - ArraySet<String> origPermissions = gp.grantedPermissions; + + PermissionsState permissionsState = ps.getPermissionsState(); + PermissionsState origPermissions = permissionsState; + boolean changedPermission = false; if (replace) { ps.permissionsFixed = false; - if (gp == ps) { - origPermissions = new ArraySet<String>(gp.grantedPermissions); - gp.grantedPermissions.clear(); - gp.gids = mGlobalGids; - } + origPermissions = new PermissionsState(permissionsState); + permissionsState.reset(); } - if (gp.gids == null) { - gp.gids = mGlobalGids; - } + permissionsState.setGlobalGids(mGlobalGids); final int N = pkg.requestedPermissions.size(); for (int i=0; i<N; i++) { final String name = pkg.requestedPermissions.get(i); - final boolean required = pkg.requestedPermissionsRequired.get(i); final BasePermission bp = mSettings.mPermissions.get(name); + if (DEBUG_INSTALL) { - if (gp != ps) { - Log.i(TAG, "Package " + pkg.packageName + " checking " + name + ": " + bp); - } + Log.i(TAG, "Package " + pkg.packageName + " checking " + name + ": " + bp); } if (bp == null || bp.packageSetting == null) { @@ -6894,10 +6961,11 @@ public class PackageManagerService extends IPackageManager.Stub { } final String perm = bp.name; - boolean allowed; boolean allowedSig = false; - if ((bp.protectionLevel&PermissionInfo.PROTECTION_FLAG_APPOP) != 0) { - // Keep track of app op permissions. + 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<>(); @@ -6905,65 +6973,108 @@ public class PackageManagerService extends IPackageManager.Stub { } pkgs.add(pkg.packageName); } + final int level = bp.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE; - if (level == PermissionInfo.PROTECTION_NORMAL - || level == PermissionInfo.PROTECTION_DANGEROUS) { - // We grant a normal or dangerous permission if any of the following - // are true: - // 1) The permission is required - // 2) The permission is optional, but was granted in the past - // 3) The permission is optional, but was requested by an - // app in /system (not /data) - // - // Otherwise, reject the permission. - allowed = (required || origPermissions.contains(perm) - || (isSystemApp(ps) && !isUpdatedSystemApp(ps))); - } else if (bp.packageSetting == null) { - // This permission is invalid; skip it. - allowed = false; - } else if (level == PermissionInfo.PROTECTION_SIGNATURE) { - allowed = grantSignaturePermission(perm, pkg, bp, origPermissions); - if (allowed) { - allowedSig = true; - } - } else { - allowed = false; + switch (level) { + case PermissionInfo.PROTECTION_NORMAL: { + // For all apps normal permissions are install time ones. + grant = GRANT_INSTALL; + } break; + + case PermissionInfo.PROTECTION_DANGEROUS: { + if (!RUNTIME_PERMISSIONS_ENABLED + || pkg.applicationInfo.targetSdkVersion + <= Build.VERSION_CODES.LOLLIPOP_MR1) { + // For legacy apps dangerous permissions are install time ones. + grant = GRANT_INSTALL; + } else if ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) { + // For modern system apps dangerous permissions are install time ones. + grant = GRANT_INSTALL; + } else { + if (origPermissions.hasInstallPermission(bp.name)) { + // For legacy apps that became modern, install becomes runtime. + grant = GRANT_UPGRADE; + } else if (replace) { + // For upgraded 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) { - if (gp != ps) { - Log.i(TAG, "Package " + pkg.packageName + " granting " + perm); - } + Log.i(TAG, "Package " + pkg.packageName + " granting " + perm); } - if (allowed) { + + if (grant != GRANT_DENIED) { if (!isSystemApp(ps) && ps.permissionsFixed) { // If this is an existing, non-system package, then // we can't add any new permissions to it. - if (!allowedSig && !gp.grantedPermissions.contains(perm)) { + 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). - allowed = isNewPlatformPermissionForPackage(perm, pkg); + if (!isNewPlatformPermissionForPackage(perm, pkg)) { + grant = GRANT_DENIED; + } } } - if (allowed) { - if (!gp.grantedPermissions.contains(perm)) { - changedPermission = true; - gp.grantedPermissions.add(perm); - gp.gids = appendInts(gp.gids, bp.gids); - } else if (!ps.haveGids) { - gp.gids = appendInts(gp.gids, bp.gids); - } - } else { - if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) { - Slog.w(TAG, "Not granting permission " + perm - + " to package " + pkg.packageName - + " because it was previously installed without"); - } + + switch (grant) { + case GRANT_INSTALL: { + // Grant an install permission. + if (permissionsState.grantInstallPermission(bp) != + PermissionsState.PERMISSION_OPERATION_FAILURE) { + changedPermission = true; + } + } break; + + case GRANT_RUNTIME: { + // Grant previously granted runtime permissions. + for (int userId : UserManagerService.getInstance().getUserIds()) { + // Make sure runtime permissions are loaded. + if (origPermissions.hasRuntimePermission(bp.name, userId)) { + if (permissionsState.grantRuntimePermission(bp, userId) != + PermissionsState.PERMISSION_OPERATION_FAILURE) { + changedPermission = true; + } + } + } + } break; + + case GRANT_UPGRADE: { + // Grant runtime permissions for a previously held install permission. + permissionsState.revokeInstallPermission(bp); + for (int userId : UserManagerService.getInstance().getUserIds()) { + // Make sure runtime permissions are loaded. + if (permissionsState.grantRuntimePermission(bp, userId) != + PermissionsState.PERMISSION_OPERATION_FAILURE) { + changedPermission = true; + } + } + } 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 (gp.grantedPermissions.remove(perm)) { + if (permissionsState.revokeInstallPermission(bp) != + PermissionsState.PERMISSION_OPERATION_FAILURE) { changedPermission = true; - gp.gids = removeInts(gp.gids, bp.gids); Slog.i(TAG, "Un-granting permission " + perm + " from package " + pkg.packageName + " (protectionLevel=" + bp.protectionLevel @@ -6990,7 +7101,6 @@ public class PackageManagerService extends IPackageManager.Stub { // changed. ps.permissionsFixed = true; } - ps.haveGids = true; } private boolean isNewPlatformPermissionForPackage(String perm, PackageParser.Package pkg) { @@ -7011,7 +7121,7 @@ public class PackageManagerService extends IPackageManager.Stub { } private boolean grantSignaturePermission(String perm, PackageParser.Package pkg, - BasePermission bp, ArraySet<String> origPermissions) { + BasePermission bp, PermissionsState origPermissions) { boolean allowed; allowed = (compareSignatures( bp.packageSetting.signatures.mSignatures, pkg.mSignatures) @@ -7026,10 +7136,7 @@ public class PackageManagerService extends IPackageManager.Stub { if (isUpdatedSystemApp(pkg)) { final PackageSetting sysPs = mSettings .getDisabledSystemPkgLPr(pkg.packageName); - final GrantedPermissions origGp = sysPs.sharedUser != null - ? sysPs.sharedUser : sysPs; - - if (origGp.grantedPermissions.contains(perm)) { + if (sysPs.getPermissionsState().hasInstallPermission(perm)) { // If the original was granted this permission, we take // that grant decision as read and propagate it to the // update. @@ -7063,7 +7170,7 @@ public class PackageManagerService extends IPackageManager.Stub { & PermissionInfo.PROTECTION_FLAG_DEVELOPMENT) != 0) { // For development permissions, a development permission // is granted only if it was already granted. - allowed = origPermissions.contains(perm); + allowed = origPermissions.hasInstallPermission(perm); } return allowed; } @@ -10821,11 +10928,26 @@ public class PackageManagerService extends IPackageManager.Stub { mSettings.mKeySetManagerService.removeAppKeySetDataLPw(packageName); outInfo.removedAppId = mSettings.removePackageLPw(packageName); } - if (deletedPs != null) { - updatePermissionsLPw(deletedPs.name, null, 0); - if (deletedPs.sharedUser != null) { - // remove permissions associated with package - mSettings.updateSharedUserPermsLPw(deletedPs, mGlobalGids); + updatePermissionsLPw(deletedPs.name, null, 0); + if (deletedPs.sharedUser != null) { + // Remove permissions associated with package. Since runtime + // permissions are per user we have to kill the removed package + // or packages running under the shared user of the removed + // package if revoking the permissions requested only by the removed + // package is successful and this causes a change in gids. + for (int userId : UserManagerService.getInstance().getUserIds()) { + final int userIdToKill = mSettings.updateSharedUserPermsLPw(deletedPs, + userId); + if (userIdToKill == userId) { + // If gids changed for this user, kill all affected packages. + killSettingPackagesForUser(deletedPs, userIdToKill, + KILL_APP_REASON_GIDS_CHANGED); + } else if (userIdToKill == UserHandle.USER_ALL) { + // If gids changed for all users, kill them all - done. + killSettingPackagesForUser(deletedPs, userIdToKill, + KILL_APP_REASON_GIDS_CHANGED); + break; + } } } clearPackagePreferredActivitiesLPw(deletedPs.name, UserHandle.USER_ALL); diff --git a/services/core/java/com/android/server/pm/PackageSetting.java b/services/core/java/com/android/server/pm/PackageSetting.java index 06d842a..889164c 100644 --- a/services/core/java/com/android/server/pm/PackageSetting.java +++ b/services/core/java/com/android/server/pm/PackageSetting.java @@ -57,8 +57,10 @@ final class PackageSetting extends PackageSettingBase { + " " + name + "/" + appId + "}"; } - public int[] getGids() { - return sharedUser != null ? sharedUser.gids : gids; + public PermissionsState getPermissionsState() { + return (sharedUser != null) + ? sharedUser.getPermissionsState() + : super.getPermissionsState(); } public boolean isPrivileged() { diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index 4b8ca42..9e8b3df 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -29,7 +29,7 @@ import java.io.File; /** * Settings base class for pending and resolved classes. */ -class PackageSettingBase extends GrantedPermissions { +abstract class PackageSettingBase extends SettingBase { /** * Indicates the state of installation. Used by PackageManager to figure out * incomplete installations. Say a package is being installed (the state is @@ -93,7 +93,6 @@ class PackageSettingBase extends GrantedPermissions { PackageSignatures signatures = new PackageSignatures(); boolean permissionsFixed; - boolean haveGids; PackageKeySetData keySetData = new PackageKeySetData(); @@ -147,7 +146,6 @@ class PackageSettingBase extends GrantedPermissions { signatures = new PackageSignatures(base.signatures); permissionsFixed = base.permissionsFixed; - haveGids = base.haveGids; userState.clear(); for (int i=0; i<base.userState.size(); i++) { userState.put(base.userState.keyAt(i), @@ -160,7 +158,6 @@ class PackageSettingBase extends GrantedPermissions { installerPackageName = base.installerPackageName; keySetData = new PackageKeySetData(base.keySetData); - } void init(File codePath, File resourcePath, String legacyNativeLibraryPathString, @@ -201,9 +198,7 @@ class PackageSettingBase extends GrantedPermissions { * Make a shallow copy of this package settings. */ public void copyFrom(PackageSettingBase base) { - grantedPermissions = base.grantedPermissions; - gids = base.gids; - + getPermissionsState().copyFrom(base.getPermissionsState()); primaryCpuAbiString = base.primaryCpuAbiString; secondaryCpuAbiString = base.secondaryCpuAbiString; cpuAbiOverrideString = base.cpuAbiOverrideString; @@ -212,7 +207,6 @@ class PackageSettingBase extends GrantedPermissions { lastUpdateTime = base.lastUpdateTime; signatures = base.signatures; permissionsFixed = base.permissionsFixed; - haveGids = base.haveGids; userState.clear(); for (int i=0; i<base.userState.size(); i++) { userState.put(base.userState.keyAt(i), base.userState.valueAt(i)); diff --git a/services/core/java/com/android/server/pm/PermissionsState.java b/services/core/java/com/android/server/pm/PermissionsState.java new file mode 100644 index 0000000..f6417ce --- /dev/null +++ b/services/core/java/com/android/server/pm/PermissionsState.java @@ -0,0 +1,523 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm; + +import android.os.UserHandle; +import android.util.ArrayMap; +import android.util.ArraySet; +import com.android.internal.util.ArrayUtils; + +import java.util.Arrays; +import java.util.Collections; +import java.util.Set; + +/** + * This class encapsulates the permissions for a package or a shared user. + * <p> + * There are two types of permissions: install (granted at installation) + * and runtime (granted at runtime). Install permissions are granted to + * all device users while runtime permissions are granted explicitly to + * specific users. + * </p> + * <p> + * The permissions are kept on a per device user basis. For example, an + * application may have some runtime permissions granted under the device + * owner but not granted under the secondary user. + * <p> + * This class is also responsible for keeping track of the Linux gids per + * user for a package or a shared user. The gids are computed as a set of + * the gids for all granted permissions' gids on a per user basis. + * </p> + */ +public final class PermissionsState { + + /** The permission operation succeeded and no gids changed. */ + public static final int PERMISSION_OPERATION_SUCCESS = 1; + + /** The permission operation succeeded and gids changed. */ + public static final int PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED = 2; + + /** The permission operation failed. */ + public static final int PERMISSION_OPERATION_FAILURE = 3; + + private static final int[] USERS_ALL = {UserHandle.USER_ALL}; + + private static final int[] USERS_NONE = {}; + + private static final int[] NO_GIDS = {}; + + private ArrayMap<String, PermissionData> mPermissions; + + private int[] mGlobalGids = NO_GIDS; + + public PermissionsState() { + /* do nothing */ + } + + public PermissionsState(PermissionsState prototype) { + copyFrom(prototype); + } + + /** + * Sets the global gids, applicable to all users. + * + * @param globalGids The global gids. + */ + public void setGlobalGids(int[] globalGids) { + if (!ArrayUtils.isEmpty(globalGids)) { + mGlobalGids = Arrays.copyOf(globalGids, globalGids.length); + } + } + + /** + * Initialized this instance from another one. + * + * @param other The other instance. + */ + public void copyFrom(PermissionsState other) { + if (mPermissions != null) { + if (other.mPermissions == null) { + mPermissions = null; + } else { + mPermissions.clear(); + } + } + if (other.mPermissions != null) { + if (mPermissions == null) { + mPermissions = new ArrayMap<>(); + } + final int permissionCount = other.mPermissions.size(); + for (int i = 0; i < permissionCount; i++) { + String name = other.mPermissions.keyAt(i); + PermissionData permissionData = other.mPermissions.valueAt(i); + mPermissions.put(name, new PermissionData(permissionData)); + } + } + + mGlobalGids = NO_GIDS; + if (other.mGlobalGids != NO_GIDS) { + mGlobalGids = Arrays.copyOf(other.mGlobalGids, + other.mGlobalGids.length); + } + } + + /** + * Grant an install permission. + * + * @param permission The permission to grant. + * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS}, + * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link + * #PERMISSION_OPERATION_FAILURE}. + */ + public int grantInstallPermission(BasePermission permission) { + return grantPermission(permission, UserHandle.USER_ALL); + } + + /** + * Revoke an install permission. + * + * @param permission The permission to revoke. + * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS}, + * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link + * #PERMISSION_OPERATION_FAILURE}. + */ + public int revokeInstallPermission(BasePermission permission) { + return revokePermission(permission, UserHandle.USER_ALL); + } + + /** + * Grant a runtime permission. + * + * @param permission The permission to grant. + * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS}, + * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link + * #PERMISSION_OPERATION_FAILURE}. + */ + public int grantRuntimePermission(BasePermission permission, int userId) { + return grantPermission(permission, userId); + } + + /** + * Revoke a runtime permission for a given device user. + * + * @param permission The permission to revoke. + * @param userId The device user id. + * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS}, + * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link + * #PERMISSION_OPERATION_FAILURE}. + */ + public int revokeRuntimePermission(BasePermission permission, int userId) { + return revokePermission(permission, userId); + } + + /** + * Gets whether this state has a given permission, regardless if + * it is install time or runtime one. + * + * @param name The permission name. + * @return Whether this state has the permission. + */ + public boolean hasPermission(String name) { + return mPermissions != null && mPermissions.get(name) != null; + } + + /** + * Gets whether this state has a given runtime permission for a + * given device user id. + * + * @param name The permission name. + * @param userId The device user id. + * @return Whether this state has the permission. + */ + public boolean hasRuntimePermission(String name, int userId) { + return !hasInstallPermission(name) && hasPermission(name, userId); + } + + /** + * Gets whether this state has a given install permission. + * + * @param name The permission name. + * @return Whether this state has the permission. + */ + public boolean hasInstallPermission(String name) { + return hasPermission(name, UserHandle.USER_ALL); + } + + /** + * Revokes a permission for all users regardless if it is an install or + * a runtime permission. + * + * @param permission The permission to revoke. + * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS}, + * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link + * #PERMISSION_OPERATION_FAILURE}. + */ + public int revokePermission(BasePermission permission) { + if (!hasPermission(permission.name)) { + return PERMISSION_OPERATION_FAILURE; + } + + int result = PERMISSION_OPERATION_SUCCESS; + + PermissionData permissionData = mPermissions.get(permission.name); + if (permissionData.getGids() != NO_GIDS) { + for (int userId : permissionData.getUserIds()) { + if (revokePermission(permission, userId) + == PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) { + result = PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED; + break; + } + } + } + + mPermissions.remove(permission.name); + + return result; + } + + /** + * Gets whether the state has a given permission for the specified + * user, regardless if this is an install or a runtime permission. + * + * @param name The permission name. + * @param userId The device user id. + * @return Whether the user has the permission. + */ + public boolean hasPermission(String name, int userId) { + enforceValidUserId(userId); + + if (mPermissions == null) { + return false; + } + + PermissionData permissionData = mPermissions.get(name); + return permissionData != null && permissionData.hasUserId(userId); + } + + /** + * Gets all permissions regardless if they are install or runtime. + * + * @return The permissions or an empty set. + */ + public Set<String> getPermissions() { + if (mPermissions != null) { + return mPermissions.keySet(); + } + + return Collections.emptySet(); + } + + /** + * Gets all permissions for a given device user id regardless if they + * are install time or runtime permissions. + * + * @param userId The device user id. + * @return The permissions or an empty set. + */ + public Set<String> getPermissions(int userId) { + enforceValidUserId(userId); + + if (mPermissions == null) { + return Collections.emptySet(); + } + + Set<String> permissions = new ArraySet<>(); + + final int permissionCount = mPermissions.size(); + for (int i = 0; i < permissionCount; i++) { + String permission = mPermissions.keyAt(i); + if (userId == UserHandle.USER_ALL) { + if (hasInstallPermission(permission)) { + permissions.add(permission); + } + } else { + if (hasRuntimePermission(permission, userId)) { + permissions.add(permission); + } + } + } + + return permissions; + } + + /** + * Gets all runtime permissions. + * + * @return The permissions or an empty set. + */ + public Set<String> getRuntimePermissions(int userId) { + return getPermissions(userId); + } + + /** + * Gets all install permissions. + * + * @return The permissions or an empty set. + */ + public Set<String> getInstallPermissions() { + return getPermissions(UserHandle.USER_ALL); + } + + /** + * Compute the Linux gids for a given device user from the permissions + * granted to this user. Note that these are computed to avoid additional + * state as they are rarely accessed. + * + * @param userId The device user id. + * @return The gids for the device user. + */ + public int[] computeGids(int userId) { + enforceValidUserId(userId); + + int[] gids = mGlobalGids; + + if (mPermissions != null) { + final int permissionCount = mPermissions.size(); + for (int i = 0; i < permissionCount; i++) { + String permission = mPermissions.keyAt(i); + if (!hasPermission(permission, userId)) { + continue; + } + PermissionData permissionData = mPermissions.valueAt(i); + final int[] permGids = permissionData.getGids(); + if (permGids != NO_GIDS) { + gids = appendInts(gids, permGids); + } + } + } + + return gids; + } + + /** + * Compute the Linux gids for all device users from the permissions + * granted to these users. + * + * @return The gids for all device users. + */ + public int[] computeGids() { + int[] gids = mGlobalGids; + + for (int userId : UserManagerService.getInstance().getUserIds()) { + final int[] userGids = computeGids(userId); + gids = appendInts(gids, userGids); + } + + return gids; + } + + /** + * Resets the internal state of this object. + */ + public void reset() { + mGlobalGids = NO_GIDS; + mPermissions = null; + } + + private int grantPermission(BasePermission permission, int userId) { + if (hasPermission(permission.name, userId)) { + return PERMISSION_OPERATION_FAILURE; + } + + final boolean hasGids = permission.gids != NO_GIDS; + final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS; + + if (mPermissions == null) { + mPermissions = new ArrayMap<>(); + } + + PermissionData permissionData = mPermissions.get(permission.name); + if (permissionData == null) { + permissionData = new PermissionData(permission.gids); + mPermissions.put(permission.name, permissionData); + } + + if (!permissionData.addUserId(userId)) { + return PERMISSION_OPERATION_FAILURE; + } + + if (hasGids) { + final int[] newGids = computeGids(userId); + if (oldGids.length != newGids.length) { + return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED; + } + } + + return PERMISSION_OPERATION_SUCCESS; + } + + private int revokePermission(BasePermission permission, int userId) { + if (!hasPermission(permission.name, userId)) { + return PERMISSION_OPERATION_FAILURE; + } + + final boolean hasGids = permission.gids != NO_GIDS; + final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS; + + PermissionData permissionData = mPermissions.get(permission.name); + + if (!permissionData.removeUserId(userId)) { + return PERMISSION_OPERATION_FAILURE; + } + + if (permissionData.getUserIds() == USERS_NONE) { + mPermissions.remove(permission.name); + } + + if (mPermissions.isEmpty()) { + mPermissions = null; + } + + if (hasGids) { + final int[] newGids = computeGids(userId); + if (oldGids.length != newGids.length) { + return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED; + } + } + + return PERMISSION_OPERATION_SUCCESS; + } + + private static int[] appendInts(int[] current, int[] added) { + if (current != null && added != null) { + for (int guid : added) { + current = ArrayUtils.appendInt(current, guid); + } + } + return current; + } + + private static void enforceValidUserId(int userId) { + if (userId != UserHandle.USER_ALL && userId < 0) { + throw new IllegalArgumentException("Invalid userId:" + userId); + } + } + + private static final class PermissionData { + private final int[] mGids; + private int[] mUserIds = USERS_NONE; + + public PermissionData(int[] gids) { + mGids = !ArrayUtils.isEmpty(gids) + ? Arrays.copyOf(gids, gids.length) + : NO_GIDS; + } + + public PermissionData(PermissionData other) { + this(other.mGids); + + if (other.mUserIds == USERS_ALL || other.mUserIds == USERS_NONE) { + mUserIds = other.mUserIds; + } else { + mUserIds = Arrays.copyOf(other.mUserIds, other.mUserIds.length); + } + } + + public int[] getGids() { + return mGids; + } + + public int[] getUserIds() { + return mUserIds; + } + + public boolean hasUserId(int userId) { + if (mUserIds == USERS_ALL) { + return true; + } + + if (userId != UserHandle.USER_ALL) { + return ArrayUtils.contains(mUserIds, userId); + } + + return false; + } + + public boolean addUserId(int userId) { + if (hasUserId(userId)) { + return false; + } + + if (userId == UserHandle.USER_ALL) { + mUserIds = USERS_ALL; + return true; + } + + mUserIds = ArrayUtils.appendInt(mUserIds, userId); + + return true; + } + + public boolean removeUserId(int userId) { + if (!hasUserId(userId)) { + return false; + } + + if (mUserIds == USERS_ALL) { + mUserIds = UserManagerService.getInstance().getUserIds(); + } + + mUserIds = ArrayUtils.removeInt(mUserIds, userId); + + if (mUserIds.length == 0) { + mUserIds = USERS_NONE; + } + + return true; + } + } +} diff --git a/services/core/java/com/android/server/pm/GrantedPermissions.java b/services/core/java/com/android/server/pm/SettingBase.java index e87546c..d350c09 100644 --- a/services/core/java/com/android/server/pm/GrantedPermissions.java +++ b/services/core/java/com/android/server/pm/SettingBase.java @@ -19,27 +19,26 @@ package com.android.server.pm; import android.content.pm.ApplicationInfo; import android.util.ArraySet; -class GrantedPermissions { +abstract class SettingBase { int pkgFlags; int pkgPrivateFlags; - ArraySet<String> grantedPermissions = new ArraySet<String>(); + private final PermissionsState mPermissionsState; - int[] gids; - - GrantedPermissions(int pkgFlags, int pkgPrivateFlags) { + SettingBase(int pkgFlags, int pkgPrivateFlags) { setFlags(pkgFlags); setPrivateFlags(pkgPrivateFlags); + mPermissionsState = new PermissionsState(); } - @SuppressWarnings("unchecked") - GrantedPermissions(GrantedPermissions base) { + SettingBase(SettingBase base) { pkgFlags = base.pkgFlags; - grantedPermissions = new ArraySet<>(base.grantedPermissions); + pkgPrivateFlags = base.pkgPrivateFlags; + mPermissionsState = new PermissionsState(base.mPermissionsState); + } - if (base.gids != null) { - gids = base.gids.clone(); - } + public PermissionsState getPermissionsState() { + return mPermissionsState; } void setFlags(int pkgFlags) { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index b820d7e..1e0a49e 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -25,6 +25,7 @@ import static android.Manifest.permission.READ_EXTERNAL_STORAGE; import static android.os.Process.SYSTEM_UID; import static android.os.Process.PACKAGE_INFO_GID; +import android.content.Context; import android.content.IntentFilter; import android.content.pm.ActivityInfo; import android.content.pm.ResolveInfo; @@ -33,17 +34,27 @@ import android.os.Binder; import android.os.Build; import android.os.Environment; import android.os.FileUtils; +import android.os.Handler; +import android.os.Message; import android.os.PatternMatcher; import android.os.Process; +import android.os.SystemClock; import android.os.UserHandle; import android.os.UserManager; +import android.util.AtomicFile; import android.util.LogPrinter; +import android.util.SparseBooleanArray; +import android.util.SparseLongArray; +import com.android.internal.annotations.GuardedBy; +import com.android.internal.os.BackgroundThread; +import com.android.internal.util.ArrayUtils; import com.android.internal.util.FastXmlSerializer; import com.android.internal.util.JournaledFile; import com.android.internal.util.XmlUtils; import com.android.server.pm.PackageManagerService.DumpState; +import java.io.FileNotFoundException; import java.util.Collection; import org.xmlpull.v1.XmlPullParser; @@ -51,7 +62,6 @@ import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlSerializer; import android.content.ComponentName; -import android.content.Context; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.ComponentInfo; @@ -134,6 +144,8 @@ final class Settings { private static final boolean DEBUG_STOPPED = false; private static final boolean DEBUG_MU = false; + private static final String RUNTIME_PERMISSIONS_FILE_NAME = "runtime-permissions.xml"; + private static final String TAG_READ_EXTERNAL_STORAGE = "read-external-storage"; private static final String ATTR_ENFORCEMENT = "enforcement"; @@ -142,6 +154,9 @@ final class Settings { private static final String TAG_ENABLED_COMPONENTS = "enabled-components"; private static final String TAG_PACKAGE_RESTRICTIONS = "package-restrictions"; private static final String TAG_PACKAGE = "pkg"; + private static final String TAG_SHARED_USER = "shared-user"; + private static final String TAG_RUNTIME_PERMISSIONS = "runtime-permissions"; + private static final String TAG_PERMISSIONS = "perms"; private static final String TAG_PERSISTENT_PREFERRED_ACTIVITIES = "persistent-preferred-activities"; static final String TAG_CROSS_PROFILE_INTENT_FILTERS = @@ -161,6 +176,11 @@ final class Settings { private static final String ATTR_INSTALLED = "inst"; private static final String ATTR_BLOCK_UNINSTALL = "blockUninstall"; + private final Object mLock; + private final Context mContext; + + private final RuntimePermissionPersistence mRuntimePermissionsPersistence; + private final File mSettingsFilename; private final File mBackupSettingsFilename; private final File mPackageListFilename; @@ -257,11 +277,16 @@ final class Settings { public final KeySetManagerService mKeySetManagerService = new KeySetManagerService(mPackages); - Settings(Context context) { - this(context, Environment.getDataDirectory()); + Settings(Context context, Object lock) { + this(context, Environment.getDataDirectory(), lock); } - Settings(Context context, File dataDir) { + Settings(Context context, File dataDir, Object lock) { + mContext = context; + mLock = lock; + + mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock); + mSystemDir = new File(dataDir, "system"); mSystemDir.mkdirs(); FileUtils.setPermissions(mSystemDir.toString(), @@ -468,9 +493,9 @@ final class Settings { private PackageSetting getPackageLPw(String name, PackageSetting origPackage, String realName, SharedUserSetting sharedUser, File codePath, File resourcePath, - String legacyNativeLibraryPathString, String primaryCpuAbiString, String secondaryCpuAbiString, - int vc, int pkgFlags, int pkgPrivateFlags, UserHandle installUser, boolean add, - boolean allowInstall) { + String legacyNativeLibraryPathString, String primaryCpuAbiString, + String secondaryCpuAbiString, int vc, int pkgFlags, int pkgPrivateFlags, + UserHandle installUser, boolean add, boolean allowInstall) { PackageSetting p = mPackages.get(name); UserManagerService userManager = UserManagerService.getInstance(); if (p != null) { @@ -589,7 +614,7 @@ final class Settings { } p.appId = dis.appId; // Clone permissions - p.grantedPermissions = new ArraySet<String>(dis.grantedPermissions); + p.getPermissionsState().copyFrom(dis.getPermissionsState()); // Clone component info List<UserInfo> users = getAllUsers(); if (users != null) { @@ -732,45 +757,60 @@ final class Settings { * not in use by other permissions of packages in the * shared user setting. */ - void updateSharedUserPermsLPw(PackageSetting deletedPs, int[] globalGids) { + int updateSharedUserPermsLPw(PackageSetting deletedPs, int userId) { if ((deletedPs == null) || (deletedPs.pkg == null)) { Slog.i(PackageManagerService.TAG, "Trying to update info for null package. Just ignoring"); - return; + return UserHandle.USER_NULL; } + // No sharedUserId if (deletedPs.sharedUser == null) { - return; + return UserHandle.USER_NULL; } + SharedUserSetting sus = deletedPs.sharedUser; + // Update permissions for (String eachPerm : deletedPs.pkg.requestedPermissions) { - boolean used = false; - if (!sus.grantedPermissions.contains(eachPerm)) { + BasePermission bp = mPermissions.get(eachPerm); + if (bp == null) { continue; } - for (PackageSetting pkg:sus.packages) { - if (pkg.pkg != null && - !pkg.pkg.packageName.equals(deletedPs.pkg.packageName) && - pkg.pkg.requestedPermissions.contains(eachPerm)) { + + // If no user has the permission, nothing to remove. + if (!sus.getPermissionsState().hasPermission(bp.name, userId)) { + continue; + } + + boolean used = false; + + // Check if another package in the shared user needs the permission. + for (PackageSetting pkg : sus.packages) { + if (pkg.pkg != null + && !pkg.pkg.packageName.equals(deletedPs.pkg.packageName) + && pkg.pkg.requestedPermissions.contains(eachPerm)) { used = true; break; } } + if (!used) { - // can safely delete this permission from list - sus.grantedPermissions.remove(eachPerm); - } - } - // Update gids - int newGids[] = globalGids; - for (String eachPerm : sus.grantedPermissions) { - BasePermission bp = mPermissions.get(eachPerm); - if (bp != null) { - newGids = PackageManagerService.appendInts(newGids, bp.gids); + // Try to revoke as an install permission which is for all users. + if (sus.getPermissionsState().revokeInstallPermission(bp) == + PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) { + return UserHandle.USER_ALL; + } + + // Try to revoke as an install permission which is per user. + if (sus.getPermissionsState().revokeRuntimePermission(bp, userId) == + PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) { + return userId; + } } } - sus.gids = newGids; + + return UserHandle.USER_NULL; } int removePackageLPw(String name) { @@ -895,7 +935,17 @@ final class Settings { } private File getUserPackagesStateFile(int userId) { - return new File(Environment.getUserSystemDirectory(userId), "package-restrictions.xml"); + // TODO: Implement a cleaner solution when adding tests. + // This instead of Environment.getUserSystemDirectory(userId) to support testing. + File userDir = new File(new File(mSystemDir, "users"), Integer.toString(userId)); + return new File(userDir, "package-restrictions.xml"); + } + + private File getUserRuntimePermissionsFile(int userId) { + // TODO: Implement a cleaner solution when adding tests. + // This instead of Environment.getUserSystemDirectory(userId) to support testing. + File userDir = new File(new File(mSystemDir, "users"), Integer.toString(userId)); + return new File(userDir, RUNTIME_PERMISSIONS_FILE_NAME); } private File getUserPackagesStateBackupFile(int userId) { @@ -912,15 +962,9 @@ final class Settings { } } - void readAllUsersPackageRestrictionsLPr() { - List<UserInfo> users = getAllUsers(); - if (users == null) { - readPackageRestrictionsLPr(0); - return; - } - - for (UserInfo user : users) { - readPackageRestrictionsLPr(user.id); + void writeAllRuntimePermissionsLPr() { + for (int userId : UserManagerService.getInstance().getUserIds()) { + mRuntimePermissionsPersistence.writePermissionsForUserAsyncLPr(userId); } } @@ -1360,6 +1404,7 @@ final class Settings { } serializer.endTag(null, TAG_DISABLED_COMPONENTS); } + serializer.endTag(null, TAG_PACKAGE); } } @@ -1403,6 +1448,58 @@ final class Settings { } } + void readInstallPermissionsLPr(XmlPullParser parser, + PermissionsState permissionsState) throws IOException, XmlPullParserException { + int outerDepth = parser.getDepth(); + int type; + while ((type=parser.next()) != XmlPullParser.END_DOCUMENT + && (type != XmlPullParser.END_TAG + || parser.getDepth() > outerDepth)) { + if (type == XmlPullParser.END_TAG + || type == XmlPullParser.TEXT) { + continue; + } + String tagName = parser.getName(); + if (tagName.equals(TAG_ITEM)) { + String name = parser.getAttributeValue(null, ATTR_NAME); + + BasePermission bp = mPermissions.get(name); + if (bp == null) { + Slog.w(PackageManagerService.TAG, "Unknown permission: " + name); + XmlUtils.skipCurrentTag(parser); + continue; + } + + if (permissionsState.grantInstallPermission(bp) == + PermissionsState.PERMISSION_OPERATION_FAILURE) { + Slog.w(PackageManagerService.TAG, "Permission already added: " + name); + XmlUtils.skipCurrentTag(parser); + } + } else { + Slog.w(PackageManagerService.TAG, "Unknown element under <permissions>: " + + parser.getName()); + XmlUtils.skipCurrentTag(parser); + } + } + } + + void writePermissionsLPr(XmlSerializer serializer, Set<String> permissions) + throws IOException { + if (permissions.isEmpty()) { + return; + } + + serializer.startTag(null, TAG_PERMISSIONS); + + for (String permission : permissions) { + serializer.startTag(null, TAG_ITEM); + serializer.attribute(null, ATTR_NAME, permission); + serializer.endTag(null, TAG_ITEM); + } + + serializer.endTag(null, TAG_PERMISSIONS); + } + // Note: assumed "stopped" field is already cleared in all packages. // Legacy reader, used to read in the old file format after an upgrade. Not used after that. void readStoppedLPw() { @@ -1594,13 +1691,7 @@ final class Settings { serializer.attribute(null, "userId", Integer.toString(usr.userId)); usr.signatures.writeXml(serializer, "sigs", mPastSignatures); - serializer.startTag(null, "perms"); - for (String name : usr.grantedPermissions) { - serializer.startTag(null, TAG_ITEM); - serializer.attribute(null, ATTR_NAME, name); - serializer.endTag(null, TAG_ITEM); - } - serializer.endTag(null, "perms"); + writePermissionsLPr(serializer, usr.getPermissionsState().getInstallPermissions()); serializer.endTag(null, "shared-user"); } @@ -1614,7 +1705,7 @@ final class Settings { serializer.endTag(null, "cleaning-package"); } } - + if (mRenamedPackages.size() > 0) { for (Map.Entry<String, String> e : mRenamedPackages.entrySet()) { serializer.startTag(null, "renamed-package"); @@ -1623,7 +1714,7 @@ final class Settings { serializer.endTag(null, "renamed-package"); } } - + mKeySetManagerService.writeKeySetManagerServiceLPr(serializer); serializer.endTag(null, "packages"); @@ -1662,7 +1753,7 @@ final class Settings { final ApplicationInfo ai = pkg.pkg.applicationInfo; final String dataPath = ai.dataDir; final boolean isDebug = (ai.flags & ApplicationInfo.FLAG_DEBUGGABLE) != 0; - final int[] gids = pkg.getGids(); + final int[] gids = pkg.getPermissionsState().computeGids(); // Avoid any application that has a space in its path. if (dataPath.indexOf(" ") >= 0) @@ -1718,6 +1809,8 @@ final class Settings { } writeAllUsersPackageRestrictionsLPr(); + + writeAllRuntimePermissionsLPr(); return; } catch(XmlPullParserException e) { @@ -1770,26 +1863,12 @@ final class Settings { } else { serializer.attribute(null, "sharedUserId", Integer.toString(pkg.appId)); } - serializer.startTag(null, "perms"); + + // If this is a shared user, the permissions will be written there. if (pkg.sharedUser == null) { - // If this is a shared user, the permissions will - // be written there. We still need to write an - // empty permissions list so permissionsFixed will - // be set. - for (final String name : pkg.grantedPermissions) { - BasePermission bp = mPermissions.get(name); - if (bp != null) { - // We only need to write signature or system permissions but - // this wont - // match the semantics of grantedPermissions. So write all - // permissions. - serializer.startTag(null, TAG_ITEM); - serializer.attribute(null, ATTR_NAME, name); - serializer.endTag(null, TAG_ITEM); - } - } + writePermissionsLPr(serializer, pkg.getPermissionsState().getInstallPermissions()); } - serializer.endTag(null, "perms"); + serializer.endTag(null, "updated-package"); } @@ -1840,19 +1919,7 @@ final class Settings { } pkg.signatures.writeXml(serializer, "sigs", mPastSignatures); if ((pkg.pkgFlags & ApplicationInfo.FLAG_SYSTEM) == 0) { - serializer.startTag(null, "perms"); - if (pkg.sharedUser == null) { - // If this is a shared user, the permissions will - // be written there. We still need to write an - // empty permissions list so permissionsFixed will - // be set. - for (final String name : pkg.grantedPermissions) { - serializer.startTag(null, TAG_ITEM); - serializer.attribute(null, ATTR_NAME, name); - serializer.endTag(null, TAG_ITEM); - } - } - serializer.endTag(null, "perms"); + writePermissionsLPr(serializer, pkg.getPermissionsState().getInstallPermissions()); } writeSigningKeySetsLPr(serializer, pkg.keySetData); @@ -2161,9 +2228,11 @@ final class Settings { } else { if (users == null) { readPackageRestrictionsLPr(0); + mRuntimePermissionsPersistence.readStateForUserSyncLPr(UserHandle.USER_OWNER); } else { for (UserInfo user : users) { readPackageRestrictionsLPr(user.id); + mRuntimePermissionsPersistence.readStateForUserSyncLPr(user.id); } } } @@ -2537,7 +2606,7 @@ final class Settings { final String ptype = parser.getAttributeValue(null, "type"); if (name != null && sourcePackage != null) { final boolean dynamic = "dynamic".equals(ptype); - final BasePermission bp = new BasePermission(name, sourcePackage, + final BasePermission bp = new BasePermission(name.intern(), sourcePackage, dynamic ? BasePermission.TYPE_DYNAMIC : BasePermission.TYPE_NORMAL); bp.protectionLevel = readInt(parser, null, "protection", PermissionInfo.PROTECTION_NORMAL); @@ -2643,6 +2712,7 @@ final class Settings { String sharedIdStr = parser.getAttributeValue(null, "sharedUserId"); ps.appId = sharedIdStr != null ? Integer.parseInt(sharedIdStr) : 0; } + int outerDepth = parser.getDepth(); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT @@ -2651,9 +2721,8 @@ final class Settings { continue; } - String tagName = parser.getName(); - if (tagName.equals("perms")) { - readGrantedPermissionsLPw(parser, ps.grantedPermissions); + if (parser.getName().equals(TAG_PERMISSIONS)) { + readInstallPermissionsLPr(parser, ps.getPermissionsState()); } else { PackageManagerService.reportSettingsProblem(Log.WARN, "Unknown element under <updated-package>: " + parser.getName()); @@ -2711,7 +2780,7 @@ final class Settings { if (primaryCpuAbiString == null && legacyCpuAbiString != null) { primaryCpuAbiString = legacyCpuAbiString; } -; + version = parser.getAttributeValue(null, "version"); if (version != null) { try { @@ -2902,7 +2971,6 @@ final class Settings { packageSetting.installStatus = PackageSettingBase.PKG_INSTALL_COMPLETE; } } - int outerDepth = parser.getDepth(); int type; while ((type = parser.next()) != XmlPullParser.END_DOCUMENT @@ -2919,8 +2987,9 @@ final class Settings { readEnabledComponentsLPw(packageSetting, parser, 0); } else if (tagName.equals("sigs")) { packageSetting.signatures.readXml(parser, mPastSignatures); - } else if (tagName.equals("perms")) { - readGrantedPermissionsLPw(parser, packageSetting.grantedPermissions); + } else if (tagName.equals(TAG_PERMISSIONS)) { + readInstallPermissionsLPr(parser, + packageSetting.getPermissionsState()); packageSetting.permissionsFixed = true; } else if (tagName.equals("proper-signing-keyset")) { long id = Long.parseLong(parser.getAttributeValue(null, "identifier")); @@ -3039,7 +3108,6 @@ final class Settings { "Error in package manager settings: package " + name + " has bad userId " + idStr + " at " + parser.getPositionDescription()); } - ; if (su != null) { int outerDepth = parser.getDepth(); @@ -3054,47 +3122,18 @@ final class Settings { if (tagName.equals("sigs")) { su.signatures.readXml(parser, mPastSignatures); } else if (tagName.equals("perms")) { - readGrantedPermissionsLPw(parser, su.grantedPermissions); + readInstallPermissionsLPr(parser, su.getPermissionsState()); } else { PackageManagerService.reportSettingsProblem(Log.WARN, "Unknown element under <shared-user>: " + parser.getName()); XmlUtils.skipCurrentTag(parser); } } - } else { XmlUtils.skipCurrentTag(parser); } } - private void readGrantedPermissionsLPw(XmlPullParser parser, ArraySet<String> outPerms) - throws IOException, XmlPullParserException { - int outerDepth = parser.getDepth(); - int type; - while ((type = parser.next()) != XmlPullParser.END_DOCUMENT - && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { - if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { - continue; - } - - String tagName = parser.getName(); - if (tagName.equals(TAG_ITEM)) { - String name = parser.getAttributeValue(null, ATTR_NAME); - if (name != null) { - outPerms.add(name.intern()); - } else { - PackageManagerService.reportSettingsProblem(Log.WARN, - "Error in package manager settings: <perms> has" + " no name at " - + parser.getPositionDescription()); - } - } else { - PackageManagerService.reportSettingsProblem(Log.WARN, - "Unknown element under <perms>: " + parser.getName()); - } - XmlUtils.skipCurrentTag(parser); - } - } - void createNewUserLILPw(PackageManagerService service, Installer installer, int userHandle, File path) { path.mkdir(); @@ -3126,6 +3165,8 @@ final class Settings { file = getUserPackagesStateBackupFile(userId); file.delete(); removeCrossProfileIntentFiltersLPw(userId); + + mRuntimePermissionsPersistence.onUserRemoved(userId); } void removeCrossProfileIntentFiltersLPw(int userId) { @@ -3317,7 +3358,7 @@ final class Settings { return null; } - static final void printFlags(PrintWriter pw, int val, Object[] spec) { + static void printFlags(PrintWriter pw, int val, Object[] spec) { pw.print("[ "); for (int i=0; i<spec.length; i+=2) { int mask = (Integer)spec[i]; @@ -3414,8 +3455,8 @@ final class Settings { pw.println(ps.name); } - pw.print(prefix); pw.print(" userId="); pw.print(ps.appId); - pw.print(" gids="); pw.println(PackageManagerService.arrayToString(ps.gids)); + pw.print(prefix); pw.print(" userId="); pw.println(ps.appId); + if (ps.sharedUser != null) { pw.print(prefix); pw.print(" sharedUser="); pw.println(ps.sharedUser); } @@ -3525,10 +3566,15 @@ final class Settings { } pw.print(prefix); pw.print(" signatures="); pw.println(ps.signatures); pw.print(prefix); pw.print(" permissionsFixed="); pw.print(ps.permissionsFixed); - pw.print(" haveGids="); pw.print(ps.haveGids); pw.print(" installStatus="); pw.println(ps.installStatus); pw.print(prefix); pw.print(" pkgFlags="); printFlags(pw, ps.pkgFlags, FLAG_DUMP_SPEC); pw.println(); + + if (ps.sharedUser == null) { + PermissionsState permissionsState = ps.getPermissionsState(); + dumpInstallPermissionsLPr(pw, prefix + " ", permissionsState); + } + for (UserInfo user : users) { pw.print(prefix); pw.print(" User "); pw.print(user.id); pw.print(": "); pw.print(" installed="); @@ -3546,6 +3592,14 @@ final class Settings { pw.print(prefix); pw.print(" lastDisabledCaller: "); pw.println(lastDisabledAppCaller); } + + if (ps.sharedUser == null) { + PermissionsState permissionsState = ps.getPermissionsState(); + dumpGidsLPr(pw, prefix + " ", permissionsState.computeGids(user.id)); + dumpRuntimePermissionsLPr(pw, prefix + " ", permissionsState + .getRuntimePermissions(user.id)); + } + ArraySet<String> cmp = ps.getDisabledComponents(user.id); if (cmp != null && cmp.size() > 0) { pw.print(prefix); pw.println(" disabledComponents:"); @@ -3561,12 +3615,6 @@ final class Settings { } } } - if (ps.grantedPermissions.size() > 0) { - pw.print(prefix); pw.println(" grantedPermissions:"); - for (String s : ps.grantedPermissions) { - pw.print(prefix); pw.print(" "); pw.println(s); - } - } } void dumpPackagesLPr(PrintWriter pw, String packageName, DumpState dumpState, boolean checkin) { @@ -3688,14 +3736,21 @@ final class Settings { pw.print("] ("); pw.print(Integer.toHexString(System.identityHashCode(su))); pw.println("):"); - pw.print(" userId="); - pw.print(su.userId); - pw.print(" gids="); - pw.println(PackageManagerService.arrayToString(su.gids)); - pw.println(" grantedPermissions:"); - for (String s : su.grantedPermissions) { - pw.print(" "); - pw.println(s); + + String prefix = " "; + pw.print(prefix); pw.print("userId="); pw.println(su.userId); + + PermissionsState permissionsState = su.getPermissionsState(); + dumpInstallPermissionsLPr(pw, prefix, permissionsState); + + for (int userId : UserManagerService.getInstance().getUserIds()) { + final int[] gids = permissionsState.computeGids(userId); + Set<String> permissions = permissionsState.getRuntimePermissions(userId); + if (!ArrayUtils.isEmpty(gids) || !permissions.isEmpty()) { + pw.print(prefix); pw.print("User "); pw.print(userId); pw.println(": "); + dumpGidsLPr(pw, prefix + " ", gids); + dumpRuntimePermissionsLPr(pw, prefix + " ", permissions); + } } } else { pw.print("suid,"); pw.print(su.userId); pw.print(","); pw.println(su.name); @@ -3730,4 +3785,373 @@ final class Settings { pw.print("]"); } } + + void dumpGidsLPr(PrintWriter pw, String prefix, int[] gids) { + if (!ArrayUtils.isEmpty(gids)) { + pw.print(prefix); pw.print("gids="); pw.println( + PackageManagerService.arrayToString(gids)); + } + } + + void dumpRuntimePermissionsLPr(PrintWriter pw, String prefix, Set<String> permissions) { + if (!permissions.isEmpty()) { + pw.print(prefix); pw.println("runtime permissions:"); + for (String permission : permissions) { + pw.print(prefix); pw.print(" "); pw.println(permission); + } + } + } + + void dumpInstallPermissionsLPr(PrintWriter pw, String prefix, + PermissionsState permissionsState) { + Set<String> permissions = permissionsState.getInstallPermissions(); + if (!permissions.isEmpty()) { + pw.print(prefix); pw.println("install permissions:"); + for (String permission : permissions) { + pw.print(prefix); pw.print(" "); pw.println(permission); + } + } + } + + public void writeRuntimePermissionsForUserLPr(int userId, boolean sync) { + if (sync) { + mRuntimePermissionsPersistence.writePermissionsForUserSyncLPr(userId); + } else { + mRuntimePermissionsPersistence.writePermissionsForUserAsyncLPr(userId); + } + } + + private final class RuntimePermissionPersistence { + private static final long WRITE_PERMISSIONS_DELAY_MILLIS = 200; + + private static final long MAX_WRITE_PERMISSIONS_DELAY_MILLIS = 2000; + + private final Handler mHandler = new MyHandler(); + + private final Object mLock; + + @GuardedBy("mLock") + private SparseBooleanArray mWriteScheduled = new SparseBooleanArray(); + + @GuardedBy("mLock") + private SparseLongArray mLastNotWrittenMutationTimesMillis = new SparseLongArray(); + + public RuntimePermissionPersistence(Object lock) { + mLock = lock; + } + + public void writePermissionsForUserSyncLPr(int userId) { + mHandler.removeMessages(userId); + writePermissionsSync(userId); + } + + public void writePermissionsForUserAsyncLPr(int userId) { + final long currentTimeMillis = SystemClock.uptimeMillis(); + + if (mWriteScheduled.get(userId)) { + mHandler.removeMessages(userId); + + // If enough time passed, write without holding off anymore. + final long lastNotWrittenMutationTimeMillis = mLastNotWrittenMutationTimesMillis + .get(userId); + final long timeSinceLastNotWrittenMutationMillis = currentTimeMillis + - lastNotWrittenMutationTimeMillis; + if (timeSinceLastNotWrittenMutationMillis >= MAX_WRITE_PERMISSIONS_DELAY_MILLIS) { + mHandler.obtainMessage(userId).sendToTarget(); + return; + } + + // Hold off a bit more as settings are frequently changing. + final long maxDelayMillis = Math.max(lastNotWrittenMutationTimeMillis + + MAX_WRITE_PERMISSIONS_DELAY_MILLIS - currentTimeMillis, 0); + final long writeDelayMillis = Math.min(WRITE_PERMISSIONS_DELAY_MILLIS, + maxDelayMillis); + + Message message = mHandler.obtainMessage(userId); + mHandler.sendMessageDelayed(message, writeDelayMillis); + } else { + mLastNotWrittenMutationTimesMillis.put(userId, currentTimeMillis); + Message message = mHandler.obtainMessage(userId); + mHandler.sendMessageDelayed(message, WRITE_PERMISSIONS_DELAY_MILLIS); + mWriteScheduled.put(userId, true); + } + } + + private void writePermissionsSync(int userId) { + AtomicFile destination = new AtomicFile(getUserRuntimePermissionsFile(userId)); + + ArrayMap<String, Set<String>> permissionsForPackage = new ArrayMap<>(); + ArrayMap<String, Set<String>> permissionsForSharedUser = new ArrayMap<>(); + + synchronized (mLock) { + mWriteScheduled.delete(userId); + + final int packageCount = mPackages.size(); + for (int i = 0; i < packageCount; i++) { + String packageName = mPackages.keyAt(i); + PackageSetting packageSetting = mPackages.valueAt(i); + if (packageSetting.sharedUser == null) { + PermissionsState permissionsState = packageSetting.getPermissionsState(); + Set<String> permissions = permissionsState.getRuntimePermissions(userId); + if (!permissions.isEmpty()) { + permissionsForPackage.put(packageName, permissions); + } + } + } + + final int sharedUserCount = mSharedUsers.size(); + for (int i = 0; i < sharedUserCount; i++) { + String sharedUserName = mSharedUsers.keyAt(i); + SharedUserSetting sharedUser = mSharedUsers.valueAt(i); + PermissionsState permissionsState = sharedUser.getPermissionsState(); + Set<String> permissions = permissionsState.getRuntimePermissions(userId); + if (!permissions.isEmpty()) { + permissionsForSharedUser.put(sharedUserName, permissions); + } + } + } + + FileOutputStream out = null; + try { + out = destination.startWrite(); + + XmlSerializer serializer = Xml.newSerializer(); + serializer.setOutput(out, "utf-8"); + serializer.setFeature( + "http://xmlpull.org/v1/doc/features.html#indent-output", true); + serializer.startDocument(null, true); + serializer.startTag(null, TAG_RUNTIME_PERMISSIONS); + + final int packageCount = permissionsForPackage.size(); + for (int i = 0; i < packageCount; i++) { + String packageName = permissionsForPackage.keyAt(i); + Set<String> permissions = permissionsForPackage.valueAt(i); + serializer.startTag(null, TAG_PACKAGE); + serializer.attribute(null, ATTR_NAME, packageName); + writePermissions(serializer, permissions); + serializer.endTag(null, TAG_PACKAGE); + } + + final int sharedUserCount = permissionsForSharedUser.size(); + for (int i = 0; i < sharedUserCount; i++) { + String packageName = permissionsForSharedUser.keyAt(i); + Set<String> permissions = permissionsForSharedUser.valueAt(i); + serializer.startTag(null, TAG_SHARED_USER); + serializer.attribute(null, ATTR_NAME, packageName); + writePermissions(serializer, permissions); + serializer.endTag(null, TAG_SHARED_USER); + } + + serializer.endTag(null, TAG_RUNTIME_PERMISSIONS); + serializer.endDocument(); + destination.finishWrite(out); + } catch (IOException e) { + Slog.wtf(PackageManagerService.TAG, + "Failed to write settings, restoring backup", e); + destination.failWrite(out); + } finally { + IoUtils.closeQuietly(out); + } + } + + private void onUserRemoved(int userId) { + // Make sure we do not + mHandler.removeMessages(userId); + + for (SettingBase sb : mPackages.values()) { + revokeRuntimePermissions(sb, userId); + } + + for (SettingBase sb : mSharedUsers.values()) { + revokeRuntimePermissions(sb, userId); + } + } + + private void revokeRuntimePermissions(SettingBase sb, int userId) { + PermissionsState permissionsState = sb.getPermissionsState(); + for (String permission : permissionsState.getRuntimePermissions(userId)) { + BasePermission bp = mPermissions.get(permission); + if (bp != null) { + permissionsState.revokeRuntimePermission(bp, userId); + } + } + } + + public void readStateForUserSyncLPr(int userId) { + File permissionsFile = getUserRuntimePermissionsFile(userId); + if (!permissionsFile.exists()) { + return; + } + + FileInputStream in; + try { + in = new FileInputStream(permissionsFile); + } catch (FileNotFoundException fnfe) { + Slog.i(PackageManagerService.TAG, "No permissions state"); + return; + } + + try { + XmlPullParser parser = Xml.newPullParser(); + parser.setInput(in, null); + parseRuntimePermissionsLPr(parser, userId); + } catch (XmlPullParserException | IOException ise) { + throw new IllegalStateException("Failed parsing permissions file: " + + permissionsFile , ise); + } finally { + IoUtils.closeQuietly(in); + } + } + + private void parseRuntimePermissionsLPr(XmlPullParser parser, int userId) + throws IOException, XmlPullParserException { + parser.next(); + skipEmptyTextTags(parser); + if (!accept(parser, XmlPullParser.START_TAG, TAG_RUNTIME_PERMISSIONS)) { + return; + } + + parser.next(); + + while (parsePackageLPr(parser, userId) + || parseSharedUserLPr(parser, userId)) { + parser.next(); + } + + skipEmptyTextTags(parser); + expect(parser, XmlPullParser.END_TAG, TAG_RUNTIME_PERMISSIONS); + } + + private boolean parsePackageLPr(XmlPullParser parser, int userId) + throws IOException, XmlPullParserException { + skipEmptyTextTags(parser); + if (!accept(parser, XmlPullParser.START_TAG, TAG_PACKAGE)) { + return false; + } + + String name = parser.getAttributeValue(null, ATTR_NAME); + + parser.next(); + + PackageSetting ps = mPackages.get(name); + if (ps != null) { + while (parsePermissionLPr(parser, ps.getPermissionsState(), userId)) { + parser.next(); + } + } + + skipEmptyTextTags(parser); + expect(parser, XmlPullParser.END_TAG, TAG_PACKAGE); + + return true; + } + + private boolean parseSharedUserLPr(XmlPullParser parser, int userId) + throws IOException, XmlPullParserException { + skipEmptyTextTags(parser); + if (!accept(parser, XmlPullParser.START_TAG, TAG_SHARED_USER)) { + return false; + } + + String name = parser.getAttributeValue(null, ATTR_NAME); + + parser.next(); + + SharedUserSetting sus = mSharedUsers.get(name); + if (sus != null) { + while (parsePermissionLPr(parser, sus.getPermissionsState(), userId)) { + parser.next(); + } + } + + skipEmptyTextTags(parser); + expect(parser, XmlPullParser.END_TAG, TAG_SHARED_USER); + + return true; + } + + private boolean parsePermissionLPr(XmlPullParser parser, PermissionsState permissionsState, + int userId) throws IOException, XmlPullParserException { + skipEmptyTextTags(parser); + if (!accept(parser, XmlPullParser.START_TAG, TAG_ITEM)) { + return false; + } + + String name = parser.getAttributeValue(null, ATTR_NAME); + + parser.next(); + + BasePermission bp = mPermissions.get(name); + if (bp != null) { + if (permissionsState.grantRuntimePermission(bp, userId) == + PermissionsState.PERMISSION_OPERATION_FAILURE) { + Slog.w(PackageManagerService.TAG, "Duplicate permission:" + name); + } + } else { + Slog.w(PackageManagerService.TAG, "Unknown permission:" + name); + } + + skipEmptyTextTags(parser); + expect(parser, XmlPullParser.END_TAG, TAG_ITEM); + + return true; + } + + private void expect(XmlPullParser parser, int type, String tag) + throws IOException, XmlPullParserException { + if (!accept(parser, type, tag)) { + throw new XmlPullParserException("Expected event: " + type + + " and tag: " + tag + " but got event: " + parser.getEventType() + + " and tag:" + parser.getName()); + } + } + + private void skipEmptyTextTags(XmlPullParser parser) + throws IOException, XmlPullParserException { + while (accept(parser, XmlPullParser.TEXT, null) + && parser.isWhitespace()) { + parser.next(); + } + } + + private boolean accept(XmlPullParser parser, int type, String tag) + throws IOException, XmlPullParserException { + if (parser.getEventType() != type) { + return false; + } + if (tag != null) { + if (!tag.equals(parser.getName())) { + return false; + } + } else if (parser.getName() != null) { + return false; + } + return true; + } + + private void writePermissions(XmlSerializer serializer, Set<String> permissions) + throws IOException { + for (String permission : permissions) { + serializer.startTag(null, TAG_ITEM); + serializer.attribute(null, ATTR_NAME, permission); + serializer.endTag(null, TAG_ITEM); + } + } + + private final class MyHandler extends Handler { + public MyHandler() { + super(BackgroundThread.getHandler().getLooper()); + } + + @Override + public void handleMessage(Message message) { + final int userId = message.what; + Runnable callback = (Runnable) message.obj; + writePermissionsSync(userId); + if (callback != null) { + callback.run(); + } + } + } + } } diff --git a/services/core/java/com/android/server/pm/SharedUserSetting.java b/services/core/java/com/android/server/pm/SharedUserSetting.java index d95739c..06e020a 100644 --- a/services/core/java/com/android/server/pm/SharedUserSetting.java +++ b/services/core/java/com/android/server/pm/SharedUserSetting.java @@ -21,7 +21,7 @@ import android.util.ArraySet; /** * Settings data for a particular shared user ID we know about. */ -final class SharedUserSetting extends GrantedPermissions { +final class SharedUserSetting extends SettingBase { final String name; int userId; |