summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/AppOpsManagerInternal.java18
-rw-r--r--core/java/android/content/pm/PackageManager.java15
-rw-r--r--core/java/android/content/pm/PackageParser.java26
-rw-r--r--services/core/java/com/android/server/AppOpsService.java91
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java55
-rw-r--r--services/core/java/com/android/server/pm/Settings.java14
-rw-r--r--services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java27
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java322
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java6
9 files changed, 497 insertions, 77 deletions
diff --git a/core/java/android/app/AppOpsManagerInternal.java b/core/java/android/app/AppOpsManagerInternal.java
index f5d5e6e9a950..7fe21b23738c 100644
--- a/core/java/android/app/AppOpsManagerInternal.java
+++ b/core/java/android/app/AppOpsManagerInternal.java
@@ -16,6 +16,7 @@
package android.app;
+import android.annotation.NonNull;
import android.util.SparseIntArray;
import com.android.internal.util.function.QuadFunction;
@@ -73,4 +74,21 @@ public abstract class AppOpsManagerInternal {
* access to app ops for their user.
*/
public abstract void setDeviceAndProfileOwners(SparseIntArray owners);
+
+ /**
+ * Sets the app-ops mode for a certain app-op and uid.
+ *
+ * <p>Similar as {@link AppOpsManager#setMode} but does not require the package manager to be
+ * working. Hence this can be used very early during boot.
+ *
+ * <p>Only for internal callers. Does <u>not</u> verify that package name belongs to uid.
+ *
+ * @param code The op code to set.
+ * @param uid The UID for which to set.
+ * @param packageName The package for which to set.
+ * @param mode The new mode to set.
+ * @param isPrivileged If the package is privileged
+ */
+ public abstract void setMode(int code, int uid, @NonNull String packageName, int mode,
+ boolean isPrivileged);
}
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index 67b86c0e6e42..7459b39dc6b4 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -2943,6 +2943,15 @@ public abstract class PackageManager {
public static final int FLAG_PERMISSION_REVIEW_REQUIRED = 1 << 6;
/**
+ * Permission flag: The permission has not been explicitly requested by
+ * the app but has been added automatically by the system. Revoke once
+ * the app does explicitly request it.
+ *
+ * @hide
+ */
+ public static final int FLAG_PERMISSION_REVOKE_WHEN_REQUESTED = 1 << 7;
+
+ /**
* Mask for all permission flags.
*
* @hide
@@ -3593,7 +3602,10 @@ public abstract class PackageManager {
FLAG_PERMISSION_POLICY_FIXED,
FLAG_PERMISSION_REVOKE_ON_UPGRADE,
FLAG_PERMISSION_SYSTEM_FIXED,
- FLAG_PERMISSION_GRANTED_BY_DEFAULT
+ FLAG_PERMISSION_GRANTED_BY_DEFAULT,
+ /*
+ FLAG_PERMISSION_REVOKE_WHEN_REQUESED
+ */
})
@Retention(RetentionPolicy.SOURCE)
public @interface PermissionFlags {}
@@ -6133,6 +6145,7 @@ public abstract class PackageManager {
case FLAG_PERMISSION_REVOKE_ON_UPGRADE: return "REVOKE_ON_UPGRADE";
case FLAG_PERMISSION_USER_FIXED: return "USER_FIXED";
case FLAG_PERMISSION_REVIEW_REQUIRED: return "REVIEW_REQUIRED";
+ case FLAG_PERMISSION_REVOKE_WHEN_REQUESTED: return "REVOKE_WHEN_REQUESTED";
default: return Integer.toString(flag);
}
}
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index dc33bde7d3d7..7ef526453e9a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -2435,7 +2435,7 @@ public class PackageParser {
}
final int NP = PackageParser.NEW_PERMISSIONS.length;
- StringBuilder implicitPerms = null;
+ StringBuilder newPermsMsg = null;
for (int ip=0; ip<NP; ip++) {
final PackageParser.NewPermissionInfo npi
= PackageParser.NEW_PERMISSIONS[ip];
@@ -2443,19 +2443,20 @@ public class PackageParser {
break;
}
if (!pkg.requestedPermissions.contains(npi.name)) {
- if (implicitPerms == null) {
- implicitPerms = new StringBuilder(128);
- implicitPerms.append(pkg.packageName);
- implicitPerms.append(": compat added ");
+ if (newPermsMsg == null) {
+ newPermsMsg = new StringBuilder(128);
+ newPermsMsg.append(pkg.packageName);
+ newPermsMsg.append(": compat added ");
} else {
- implicitPerms.append(' ');
+ newPermsMsg.append(' ');
}
- implicitPerms.append(npi.name);
+ newPermsMsg.append(npi.name);
pkg.requestedPermissions.add(npi.name);
+ pkg.implicitPermissions.add(npi.name);
}
}
- if (implicitPerms != null) {
- Slog.i(TAG, implicitPerms.toString());
+ if (newPermsMsg != null) {
+ Slog.i(TAG, newPermsMsg.toString());
}
@@ -2472,6 +2473,7 @@ public class PackageParser {
final String perm = newPerms.get(in);
if (!pkg.requestedPermissions.contains(perm)) {
pkg.requestedPermissions.add(perm);
+ pkg.implicitPermissions.add(perm);
}
}
}
@@ -6394,6 +6396,9 @@ public class PackageParser {
@UnsupportedAppUsage
public final ArrayList<String> requestedPermissions = new ArrayList<String>();
+ /** Permissions requested but not in the manifest. */
+ public final ArrayList<String> implicitPermissions = new ArrayList<>();
+
@UnsupportedAppUsage
public ArrayList<String> protectedBroadcasts;
@@ -6923,6 +6928,8 @@ public class PackageParser {
dest.readStringList(requestedPermissions);
internStringArrayList(requestedPermissions);
+ dest.readStringList(implicitPermissions);
+ internStringArrayList(implicitPermissions);
protectedBroadcasts = dest.createStringArrayList();
internStringArrayList(protectedBroadcasts);
@@ -7087,6 +7094,7 @@ public class PackageParser {
dest.writeParcelableList(instrumentation, flags);
dest.writeStringList(requestedPermissions);
+ dest.writeStringList(implicitPermissions);
dest.writeStringList(protectedBroadcasts);
// TODO: This doesn't work: b/64295061
diff --git a/services/core/java/com/android/server/AppOpsService.java b/services/core/java/com/android/server/AppOpsService.java
index cd98263e417e..32667b850888 100644
--- a/services/core/java/com/android/server/AppOpsService.java
+++ b/services/core/java/com/android/server/AppOpsService.java
@@ -1210,13 +1210,29 @@ public class AppOpsService extends IAppOpsService.Stub {
@Override
public void setMode(int code, int uid, String packageName, int mode) {
+ setMode(code, uid, packageName, mode, true, false);
+ }
+
+ /**
+ * Sets the mode for a certain op and uid.
+ *
+ * @param code The op code to set
+ * @param uid The UID for which to set
+ * @param packageName The package for which to set
+ * @param mode The new mode to set
+ * @param verifyUid Iff {@code true}, check that the package name belongs to the uid
+ * @param isPrivileged Whether the package is privileged. (Only used if {@code verifyUid ==
+ * false})
+ */
+ private void setMode(int code, int uid, @NonNull String packageName, int mode,
+ boolean verifyUid, boolean isPrivileged) {
enforceManageAppOpsModes(Binder.getCallingPid(), Binder.getCallingUid(), uid);
verifyIncomingOp(code);
ArraySet<ModeCallback> repCbs = null;
code = AppOpsManager.opToSwitch(code);
synchronized (this) {
UidState uidState = getUidStateLocked(uid, false);
- Op op = getOpLocked(code, uid, packageName, true);
+ Op op = getOpLocked(code, uid, packageName, true, verifyUid, isPrivileged);
if (op != null) {
if (op.mode != mode) {
op.mode = mode;
@@ -1575,7 +1591,7 @@ public class AppOpsService extends IAppOpsService.Stub {
&& uidState.opModes.indexOfKey(code) >= 0) {
return uidState.opModes.get(code);
}
- Op op = getOpLocked(code, uid, resolvedPackageName, false);
+ Op op = getOpLocked(code, uid, resolvedPackageName, false, true, false);
if (op == null) {
return AppOpsManager.opToDefaultMode(code);
}
@@ -1918,7 +1934,7 @@ public class AppOpsService extends IAppOpsService.Stub {
}
ClientState client = (ClientState) token;
synchronized (this) {
- Op op = getOpLocked(code, uid, resolvedPackageName, true);
+ Op op = getOpLocked(code, uid, resolvedPackageName, true, true, false);
if (op == null) {
return;
}
@@ -2172,6 +2188,43 @@ public class AppOpsService extends IAppOpsService.Stub {
return ops;
}
+ /**
+ * Get the state of all ops for a package, <b>don't verify that package belongs to uid</b>.
+ *
+ * <p>Usually callers should use {@link #getOpLocked} and not call this directly.
+ *
+ * @param uid The uid the of the package
+ * @param packageName The package name for which to get the state for
+ * @param edit Iff {@code true} create the {@link Ops} object if not yet created
+ * @param isPrivileged Whether the package is privileged or not
+ *
+ * @return The {@link Ops state} of all ops for the package
+ */
+ private @Nullable Ops getOpsRawNoVerifyLocked(int uid, @NonNull String packageName,
+ boolean edit, boolean isPrivileged) {
+ UidState uidState = getUidStateLocked(uid, edit);
+ if (uidState == null) {
+ return null;
+ }
+
+ if (uidState.pkgOps == null) {
+ if (!edit) {
+ return null;
+ }
+ uidState.pkgOps = new ArrayMap<>();
+ }
+
+ Ops ops = uidState.pkgOps.get(packageName);
+ if (ops == null) {
+ if (!edit) {
+ return null;
+ }
+ ops = new Ops(packageName, uidState, isPrivileged);
+ uidState.pkgOps.put(packageName, ops);
+ }
+ return ops;
+ }
+
private void scheduleWriteLocked() {
if (!mWriteScheduled) {
mWriteScheduled = true;
@@ -2188,9 +2241,29 @@ public class AppOpsService extends IAppOpsService.Stub {
}
}
- private Op getOpLocked(int code, int uid, String packageName, boolean edit) {
- Ops ops = getOpsRawLocked(uid, packageName, edit,
- false /* uidMismatchExpected */);
+ /**
+ * Get the state of an op for a uid.
+ *
+ * @param code The code of the op
+ * @param uid The uid the of the package
+ * @param packageName The package name for which to get the state for
+ * @param edit Iff {@code true} create the {@link Op} object if not yet created
+ * @param verifyUid Iff {@code true} check that the package belongs to the uid
+ * @param isPrivileged Whether the package is privileged or not (only used if {@code verifyUid
+ * == false})
+ *
+ * @return The {@link Op state} of the op
+ */
+ private @Nullable Op getOpLocked(int code, int uid, @NonNull String packageName, boolean edit,
+ boolean verifyUid, boolean isPrivileged) {
+ Ops ops;
+
+ if (verifyUid) {
+ ops = getOpsRawLocked(uid, packageName, edit, false /* uidMismatchExpected */);
+ } else {
+ ops = getOpsRawNoVerifyLocked(uid, packageName, edit, isPrivileged);
+ }
+
if (ops == null) {
return null;
}
@@ -3948,5 +4021,11 @@ public class AppOpsService extends IAppOpsService.Stub {
mProfileOwners = owners;
}
}
+
+ @Override
+ public void setMode(int code, int uid, @NonNull String packageName, int mode,
+ boolean isPrivileged) {
+ AppOpsService.this.setMode(code, uid, packageName, mode, false, isPrivileged);
+ }
}
}
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index ed3e8577a5b4..3a111301ce89 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -239,7 +239,6 @@ import android.os.storage.StorageManager;
import android.os.storage.StorageManagerInternal;
import android.os.storage.VolumeInfo;
import android.os.storage.VolumeRecord;
-import android.permission.PermissionManager;
import android.provider.Settings.Global;
import android.provider.Settings.Secure;
import android.security.KeyStore;
@@ -2930,60 +2929,6 @@ public class PackageManagerService extends IPackageManager.Stub
checkDefaultBrowser();
- // If a granted permission is split, all new permissions should be granted too
- if (mIsUpgrade) {
- final int callingUid = getCallingUid();
-
- final List<PermissionManager.SplitPermissionInfo> splitPermissions =
- mContext.getSystemService(PermissionManager.class).getSplitPermissions();
- final int numSplitPerms = splitPermissions.size();
- for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
- final PermissionManager.SplitPermissionInfo splitPerm =
- splitPermissions.get(splitPermNum);
- final String rootPerm = splitPerm.getSplitPermission();
-
- if (preUpgradeSdkVersion >= splitPerm.getTargetSdk()) {
- continue;
- }
-
- final int numPackages = mPackages.size();
- for (int packageNum = 0; packageNum < numPackages; packageNum++) {
- final PackageParser.Package pkg = mPackages.valueAt(packageNum);
-
- if (pkg.applicationInfo.targetSdkVersion >= splitPerm.getTargetSdk()
- || !pkg.requestedPermissions.contains(rootPerm)) {
- continue;
- }
-
- final int userId = UserHandle.getUserId(pkg.applicationInfo.uid);
- final String pkgName = pkg.packageName;
-
- if (checkPermission(rootPerm, pkgName, userId) == PERMISSION_DENIED) {
- continue;
- }
-
- final List<String> newPerms = splitPerm.getNewPermissions();
-
- final int numNewPerms = newPerms.size();
- for (int newPermNum = 0; newPermNum < numNewPerms; newPermNum++) {
- final String newPerm = newPerms.get(newPermNum);
- if (checkPermission(newPerm, pkgName, userId) == PERMISSION_GRANTED) {
- continue;
- }
-
- if (DEBUG_PERMISSIONS) {
- Slog.v(TAG, "Granting " + newPerm + " to " + pkgName
- + " as the root permission " + rootPerm
- + " is already granted");
- }
-
- mPermissionManager.grantRuntimePermission(newPerm, pkgName, true,
- callingUid, userId, null);
- }
- }
- }
- }
-
// clear only after permissions and other defaults have been updated
mExistingSystemPackages.clear();
mPromoteSystemApps = false;
diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java
index e2818b70deb1..4c93441b5c7b 100644
--- a/services/core/java/com/android/server/pm/Settings.java
+++ b/services/core/java/com/android/server/pm/Settings.java
@@ -20,6 +20,7 @@ import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DEFAULT;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
import static android.content.pm.PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
@@ -256,6 +257,7 @@ public final class Settings {
private static final String ATTR_USER_SET = "set";
private static final String ATTR_USER_FIXED = "fixed";
private static final String ATTR_REVOKE_ON_UPGRADE = "rou";
+ private static final String ATTR_REVOKE_WHEN_REQUESTED = "rwr";
// Flag mask of restored permission grants that are applied at install time
private static final int USER_RUNTIME_GRANT_MASK =
@@ -5011,6 +5013,9 @@ public final class Settings {
if ((g.grantBits&FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
pw.print(" revoke_on_upgrade");
}
+ if ((g.grantBits & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) {
+ pw.print(" revoke_when_requested");
+ }
pw.println();
}
}
@@ -5326,6 +5331,11 @@ public final class Settings {
if ((g.grantBits&FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
serializer.attribute(null, ATTR_REVOKE_ON_UPGRADE, "true");
}
+ if ((g.grantBits & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED)
+ != 0) {
+ serializer.attribute(null, ATTR_REVOKE_WHEN_REQUESTED,
+ "true");
+ }
serializer.endTag(null, TAG_PERMISSION_ENTRY);
}
serializer.endTag(null, TAG_RESTORED_RUNTIME_PERMISSIONS);
@@ -5512,6 +5522,10 @@ public final class Settings {
if ("true".equals(parser.getAttributeValue(null, ATTR_REVOKE_ON_UPGRADE))) {
permBits |= FLAG_PERMISSION_REVOKE_ON_UPGRADE;
}
+ if ("true".equals(parser.getAttributeValue(null,
+ ATTR_REVOKE_WHEN_REQUESTED))) {
+ permBits |= FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
+ }
if (isGranted || permBits != 0) {
rememberRestoredUserGrantLPr(pkgName, permName, isGranted, permBits, userId);
diff --git a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
index 9d1a30107d35..030e148cd13c 100644
--- a/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
+++ b/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.java
@@ -1009,6 +1009,24 @@ public final class DefaultPermissionGrantPolicy {
}
}
+ /**
+ * Check if a permission is already fixed or is set by the user.
+ *
+ * <p>A permission should not be set by the default policy if the user or other policies already
+ * set the permission.
+ *
+ * @param flags The flags of the permission
+ *
+ * @return {@code true} iff the permission can be set without violating a policy of the users
+ * intention
+ */
+ private boolean isFixedOrUserSet(int flags) {
+ return (flags & (PackageManager.FLAG_PERMISSION_USER_SET
+ | PackageManager.FLAG_PERMISSION_USER_FIXED
+ | PackageManager.FLAG_PERMISSION_POLICY_FIXED
+ | PackageManager.FLAG_PERMISSION_SYSTEM_FIXED)) != 0;
+ }
+
private void grantRuntimePermissions(PackageInfo pkg,
Set<String> permissionsWithoutSplits, boolean systemFixed, boolean ignoreSystemPackage,
int userId) {
@@ -1078,13 +1096,14 @@ public final class DefaultPermissionGrantPolicy {
final int flags = mContext.getPackageManager().getPermissionFlags(
permission, pkg.packageName, user);
- // If any flags are set to the permission, then it is either set in
- // its current state by the system or device/profile owner or the user.
- // In all these cases we do not want to clobber the current state.
+ // Certain flags imply that the permission's current state by the system or
+ // device/profile owner or the user. In these cases we do not want to clobber the
+ // current state.
+ //
// Unless the caller wants to override user choices. The override is
// to make sure we can grant the needed permission to the default
// sms and phone apps after the user chooses this in the UI.
- if (flags == 0 || ignoreSystemPackage) {
+ if (!isFixedOrUserSet(flags) || ignoreSystemPackage) {
// Never clobber policy fixed permissions.
// We must allow the grant of a system-fixed permission because
// system-fixed is sticky, but the permission itself may be revoked.
diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
index d52bd640977f..af05a7987bea 100644
--- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
+++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java
@@ -18,8 +18,25 @@ package com.android.server.pm.permission;
import static android.Manifest.permission.READ_EXTERNAL_STORAGE;
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_DEFAULT;
+import static android.app.AppOpsManager.MODE_ERRORED;
+import static android.app.AppOpsManager.MODE_FOREGROUND;
+import static android.app.AppOpsManager.MODE_IGNORED;
+import static android.app.AppOpsManager.OP_NONE;
+import static android.app.AppOpsManager.permissionToOp;
+import static android.app.AppOpsManager.permissionToOpCode;
+import static android.content.pm.ApplicationInfo.PRIVATE_FLAG_PRIVILEGED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
+import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
+import static android.os.UserHandle.getAppId;
+import static android.os.UserHandle.getUid;
import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
@@ -30,6 +47,9 @@ import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import android.Manifest;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.AppOpsManager;
+import android.app.AppOpsManagerInternal;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.PackageManagerInternal;
@@ -49,6 +69,7 @@ import android.os.UserManager;
import android.os.UserManagerInternal;
import android.os.storage.StorageManager;
import android.os.storage.StorageManagerInternal;
+import android.permission.PermissionManager;
import android.text.TextUtils;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -832,7 +853,8 @@ public class PermissionManagerService {
}
if (DEBUG_PERMISSIONS) {
- Slog.i(TAG, "Granting permission " + perm + " to package " + pkg.packageName);
+ Slog.i(TAG, "Considering granting permission " + perm + " to package "
+ + pkg.packageName);
}
if (grant != GRANT_DENIED) {
@@ -1021,6 +1043,11 @@ public class PermissionManagerService {
// changed.
ps.setInstallPermissionsFixed(true);
}
+
+ updatedUserIds = revokePermissionsNoLongerImplicitLocked(permissionsState, pkg,
+ updatedUserIds);
+ updatedUserIds = setInitialGrantForNewImplicitPermissionsLocked(origPermissions,
+ permissionsState, pkg, updatedUserIds);
}
// Persist the runtime permissions state for users with changes. If permissions
@@ -1031,6 +1058,299 @@ public class PermissionManagerService {
}
}
+ /**
+ * Set app op for a app-op related to a permission.
+ *
+ * @param permission The permission the app-op belongs to
+ * @param pkg The package the permission belongs to
+ * @param userId The user to be changed
+ * @param mode The new mode to set
+ */
+ private void setAppOpMode(@NonNull String permission, @NonNull PackageParser.Package pkg,
+ @UserIdInt int userId, int mode) {
+ AppOpsManagerInternal appOpsInternal = LocalServices.getService(
+ AppOpsManagerInternal.class);
+
+ appOpsInternal.setMode(permissionToOpCode(permission),
+ getUid(userId, getAppId(pkg.applicationInfo.uid)), pkg.packageName, mode,
+ (pkg.applicationInfo.privateFlags & PRIVATE_FLAG_PRIVILEGED) != 0);
+ }
+
+ /**
+ * Revoke permissions that are not implicit anymore and that have
+ * {@link PackageManager#FLAG_PERMISSION_REVOKE_WHEN_REQUESTED} set.
+ *
+ * @param ps The state of the permissions of the package
+ * @param pkg The package that is currently looked at
+ * @param updatedUserIds a list of user ids that needs to be amended if the permission state
+ * for a user is changed.
+ *
+ * @return The updated value of the {@code updatedUserIds} parameter
+ */
+ private @NonNull int[] revokePermissionsNoLongerImplicitLocked(
+ @NonNull PermissionsState ps, @NonNull PackageParser.Package pkg,
+ @NonNull int[] updatedUserIds) {
+ AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class);
+
+ String pkgName = pkg.packageName;
+
+ int[] users = UserManagerService.getInstance().getUserIds();
+ int numUsers = users.length;
+ for (int i = 0; i < numUsers; i++) {
+ int userId = users[i];
+
+ for (String permission : ps.getPermissions(userId)) {
+ if (!pkg.implicitPermissions.contains(permission)) {
+ if (!ps.hasInstallPermission(permission)) {
+ int flags = ps.getRuntimePermissionState(permission, userId).getFlags();
+
+ if ((flags & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) {
+ BasePermission bp = mSettings.getPermissionLocked(permission);
+
+ ps.updatePermissionFlags(bp, userId,
+ FLAG_PERMISSION_REVOKE_WHEN_REQUESTED
+ | FLAG_PERMISSION_USER_FIXED | FLAG_PERMISSION_USER_SET,
+ 0);
+ updatedUserIds = ArrayUtils.appendInt(updatedUserIds,
+ userId);
+
+ if ((flags & (FLAG_PERMISSION_GRANTED_BY_DEFAULT
+ | FLAG_PERMISSION_POLICY_FIXED | FLAG_PERMISSION_SYSTEM_FIXED))
+ == 0) {
+ if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
+ if (permissionToOpCode(permission) != OP_NONE) {
+ setAppOpMode(permission, pkg, userId, MODE_IGNORED);
+
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, "Revoking app-op "
+ + permissionToOp(permission) + " for " + pkgName
+ + " as it is now requested");
+ }
+ }
+ } else {
+ int revokeOpSuccess = ps.revokeRuntimePermission(bp, userId);
+ if (revokeOpSuccess
+ != PermissionsState.PERMISSION_OPERATION_FAILURE) {
+
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, "Revoking runtime permission " + permission
+ + " for " + pkgName
+ + " as it is now requested");
+ }
+ }
+ }
+
+ List<String> fgPerms = mBackgroundPermissions.get(permission);
+ if (fgPerms != null) {
+ int numFgPerms = fgPerms.size();
+ for (int fgPermNum = 0; fgPermNum < numFgPerms; fgPermNum++) {
+ String fgPerm = fgPerms.get(fgPermNum);
+
+ int mode = appOpsManager.unsafeCheckOpRaw(
+ permissionToOp(fgPerm),
+ getUid(userId, getAppId(pkg.applicationInfo.uid)),
+ pkgName);
+
+ if (mode == MODE_ALLOWED) {
+ setAppOpMode(fgPerm, pkg, userId, MODE_FOREGROUND);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return updatedUserIds;
+ }
+
+ /**
+ * {@code newPerm} is newly added; Inherit the state from {@code sourcePerms}.
+ *
+ * <p>A single new permission can be split off from several source permissions. In this case
+ * the most leniant state is inherited.
+ *
+ * <p>Warning: This does not handle foreground / background permissions
+ *
+ * @param sourcePerms The permissions to inherit from
+ * @param newPerm The permission to inherit to
+ * @param ps The permission state of the package
+ * @param pkg The package requesting the permissions
+ * @param userId The user the permission belongs to
+ */
+ private void inheritPermissionStateToNewImplicitPermissionLocked(
+ @NonNull ArraySet<String> sourcePerms, @NonNull String newPerm,
+ @NonNull PermissionsState ps, @NonNull PackageParser.Package pkg,
+ @UserIdInt int userId) {
+ AppOpsManager appOpsManager = mContext.getSystemService(AppOpsManager.class);
+ String pkgName = pkg.packageName;
+
+ if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
+ if (permissionToOp(newPerm) != null) {
+ int mostLenientSourceMode = MODE_ERRORED;
+
+ // Find most lenient source permission state.
+ int numSourcePerms = sourcePerms.size();
+ for (int i = 0; i < numSourcePerms; i++) {
+ String sourcePerm = sourcePerms.valueAt(i);
+
+ if (ps.hasRuntimePermission(sourcePerm, userId)) {
+ String sourceOp = permissionToOp(sourcePerm);
+
+ if (sourceOp != null) {
+ int mode = appOpsManager.unsafeCheckOpRaw(sourceOp,
+ getUid(userId, getAppId(pkg.applicationInfo.uid)), pkgName);
+
+ if (mode == MODE_FOREGROUND) {
+ throw new IllegalArgumentException("split permission" + sourcePerm
+ + " has app-op state " + AppOpsManager.MODE_NAMES[mode]);
+ }
+
+ // Leniency order: allowed > ignored > default
+ if (mode == MODE_ALLOWED) {
+ mostLenientSourceMode = MODE_ALLOWED;
+ break;
+ } else if (mode == MODE_IGNORED) {
+ mostLenientSourceMode = MODE_IGNORED;
+ } else if (mode == MODE_DEFAULT
+ && mostLenientSourceMode != MODE_IGNORED) {
+ mostLenientSourceMode = MODE_DEFAULT;
+ }
+ }
+ }
+ }
+
+ if (mostLenientSourceMode != MODE_ERRORED) {
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, newPerm + " inherits app-ops state " + mostLenientSourceMode
+ + " from " + sourcePerms + " for " + pkgName);
+ }
+
+ setAppOpMode(newPerm, pkg, userId, mostLenientSourceMode);
+ }
+ }
+ } else {
+ boolean isGranted = false;
+
+ int numSourcePerm = sourcePerms.size();
+ for (int i = 0; i < numSourcePerm; i++) {
+ String sourcePerm = sourcePerms.valueAt(i);
+ if (ps.hasRuntimePermission(sourcePerm, userId)
+ && ps.getRuntimePermissionState(sourcePerm, userId).isGranted()) {
+ isGranted = true;
+ break;
+ }
+ }
+
+ if (isGranted) {
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, newPerm + " inherits runtime perm grant from " + sourcePerms
+ + " for " + pkgName);
+ }
+
+ ps.grantRuntimePermission(mSettings.getPermissionLocked(newPerm), userId);
+ }
+ }
+ }
+
+ /**
+ * Set the state of a implicit permission that is seen for the first time.
+ *
+ * @param origPs The permission state of the package before the split
+ * @param ps The new permission state
+ * @param pkg The package the permission belongs to
+ * @param updatedUserIds List of users for which the permission state has already been changed
+ *
+ * @return List of users for which the permission state has been changed
+ */
+ private @NonNull int[] setInitialGrantForNewImplicitPermissionsLocked(
+ @NonNull PermissionsState origPs,
+ @NonNull PermissionsState ps, @NonNull PackageParser.Package pkg,
+ @NonNull int[] updatedUserIds) {
+ String pkgName = pkg.packageName;
+ ArraySet<String> newImplicitPermissions = new ArraySet<>();
+
+ int numRequestedPerms = pkg.requestedPermissions.size();
+ for (int i = 0; i < numRequestedPerms; i++) {
+ BasePermission bp = mSettings.getPermissionLocked(pkg.requestedPermissions.get(i));
+ if (bp != null) {
+ String perm = bp.getName();
+
+ if (!origPs.hasRequestedPermission(perm) && pkg.implicitPermissions.contains(
+ perm)) {
+ newImplicitPermissions.add(perm);
+
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, perm + " is newly added for " + pkgName);
+ }
+ }
+ }
+ }
+
+ ArrayMap<String, ArraySet<String>> newToSplitPerms = new ArrayMap<>();
+
+ int numSplitPerms = PermissionManager.SPLIT_PERMISSIONS.size();
+ for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
+ PermissionManager.SplitPermissionInfo spi =
+ PermissionManager.SPLIT_PERMISSIONS.get(splitPermNum);
+
+ List<String> newPerms = spi.getNewPermissions();
+ int numNewPerms = newPerms.size();
+ for (int newPermNum = 0; newPermNum < numNewPerms; newPermNum++) {
+ String newPerm = newPerms.get(newPermNum);
+
+ ArraySet<String> splitPerms = newToSplitPerms.get(newPerm);
+ if (splitPerms == null) {
+ splitPerms = new ArraySet<>();
+ newToSplitPerms.put(newPerm, splitPerms);
+ }
+
+ splitPerms.add(spi.getSplitPermission());
+ }
+ }
+
+ int numNewImplicitPerms = newImplicitPermissions.size();
+ for (int newImplicitPermNum = 0; newImplicitPermNum < numNewImplicitPerms;
+ newImplicitPermNum++) {
+ String newPerm = newImplicitPermissions.valueAt(newImplicitPermNum);
+ ArraySet<String> sourcePerms = newToSplitPerms.get(newPerm);
+
+ if (sourcePerms != null) {
+ if (!ps.hasInstallPermission(newPerm)) {
+ BasePermission bp = mSettings.getPermissionLocked(newPerm);
+
+ int[] users = UserManagerService.getInstance().getUserIds();
+ int numUsers = users.length;
+ for (int userNum = 0; userNum < numUsers; userNum++) {
+ int userId = users[userNum];
+
+ ps.updatePermissionFlags(bp, userId,
+ FLAG_PERMISSION_REVOKE_WHEN_REQUESTED,
+ FLAG_PERMISSION_REVOKE_WHEN_REQUESTED);
+ updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
+
+ if (!origPs.hasRequestedPermission(sourcePerms)) {
+ // Both permissions are new, do nothing
+ if (DEBUG_PERMISSIONS) {
+ Slog.i(TAG, newPerm + " does not inherit from " + sourcePerms
+ + " for " + pkgName + " as split permission is also new");
+ }
+
+ break;
+ } else {
+ inheritPermissionStateToNewImplicitPermissionLocked(sourcePerms,
+ newPerm, ps, pkg, userId);
+ }
+ }
+ }
+ }
+ }
+
+ return updatedUserIds;
+ }
+
private boolean isNewPlatformPermissionForPackage(String perm, PackageParser.Package pkg) {
boolean allowed = false;
final int NP = PackageParser.NEW_PERMISSIONS.length;
diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
index ebac8fb712be..58c4bbf88d8c 100644
--- a/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
+++ b/services/tests/servicestests/src/com/android/server/pm/PackageParserTest.java
@@ -15,7 +15,10 @@
*/
package com.android.server.pm;
-import static org.junit.Assert.*;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo;
@@ -461,6 +464,7 @@ public class PackageParserTest {
pkg.services.add(new PackageParser.Service(dummy, new ServiceInfo()));
pkg.instrumentation.add(new PackageParser.Instrumentation(dummy, new InstrumentationInfo()));
pkg.requestedPermissions.add("foo7");
+ pkg.implicitPermissions.add("foo25");
pkg.protectedBroadcasts = new ArrayList<>();
pkg.protectedBroadcasts.add("foo8");