diff options
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"); |