summaryrefslogtreecommitdiffstats
path: root/services/core/java/com/android/server
diff options
context:
space:
mode:
Diffstat (limited to 'services/core/java/com/android/server')
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java7
-rw-r--r--services/core/java/com/android/server/am/BroadcastQueue.java2
-rw-r--r--services/core/java/com/android/server/pm/BasePermission.java5
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java500
-rw-r--r--services/core/java/com/android/server/pm/PackageSetting.java6
-rw-r--r--services/core/java/com/android/server/pm/PackageSettingBase.java10
-rw-r--r--services/core/java/com/android/server/pm/PermissionsState.java523
-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.java696
-rw-r--r--services/core/java/com/android/server/pm/SharedUserSetting.java2
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;