diff --git a/core/api/system-current.txt b/core/api/system-current.txt index 6267dbf376f77..e4b3bff52353f 100644 --- a/core/api/system-current.txt +++ b/core/api/system-current.txt @@ -3292,6 +3292,7 @@ package android.content.pm { method @Deprecated @RequiresPermission(android.Manifest.permission.INTENT_FILTER_VERIFICATION_AGENT) public abstract void verifyIntentFilter(int, int, @NonNull java.util.List); field public static final String ACTION_REQUEST_PERMISSIONS = "android.content.pm.action.REQUEST_PERMISSIONS"; field public static final String ACTION_REQUEST_PERMISSIONS_FOR_OTHER = "android.content.pm.action.REQUEST_PERMISSIONS_FOR_OTHER"; + field public static final String EXTRA_REQUEST_PERMISSIONS_LEGACY_ACCESS_PERMISSION_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_LEGACY_ACCESS_PERMISSION_NAMES"; field public static final String EXTRA_REQUEST_PERMISSIONS_NAMES = "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES"; field public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS"; field public static final String FEATURE_BROADCAST_RADIO = "android.hardware.broadcastradio"; diff --git a/core/api/test-current.txt b/core/api/test-current.txt index 4132c64a44c9a..b5af3334aff07 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -2059,7 +2059,7 @@ package android.permission { method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List getIndicatorAppOpUsageData(); method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List getIndicatorAppOpUsageData(boolean); method @NonNull public android.content.AttributionSource registerAttributionSource(@NonNull android.content.AttributionSource); - method public void revokePostNotificationPermissionWithoutKillForTest(@NonNull String, int); + method @RequiresPermission(android.Manifest.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL) public void revokePostNotificationPermissionWithoutKillForTest(@NonNull String, int); } } diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index e9466e99c5ca3..0938c0e0cf272 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -4287,6 +4287,21 @@ public abstract class PackageManager { public static final String EXTRA_REQUEST_PERMISSIONS_RESULTS = "android.content.pm.extra.REQUEST_PERMISSIONS_RESULTS"; + /** + * Indicates that the package requesting permissions has legacy access for some permissions, + * or had it, but it was recently revoked. These request dialogs may show different text, + * indicating that the app is requesting continued access to a permission. Will be cleared + * from any permission request intent, if set by a non-system server app. + *

+ * Type: String[] + *

+ * + * @hide + */ + @SystemApi + public static final String EXTRA_REQUEST_PERMISSIONS_LEGACY_ACCESS_PERMISSION_NAMES + = "android.content.pm.extra.REQUEST_PERMISSIONS_LEGACY_ACCESS_PERMISSION_NAMES"; + /** * String extra for {@link PackageInstallObserver} in the 'extras' Bundle in case of * {@link #INSTALL_FAILED_DUPLICATE_PERMISSION}. This extra names the package which provides diff --git a/core/java/android/permission/PermissionManager.java b/core/java/android/permission/PermissionManager.java index 12fa0ddfc648a..fc7ac116a470a 100644 --- a/core/java/android/permission/PermissionManager.java +++ b/core/java/android/permission/PermissionManager.java @@ -16,6 +16,12 @@ package android.permission; +import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; +import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_ROLE; +import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED; +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.Build.VERSION_CODES.S; import android.Manifest; @@ -106,6 +112,16 @@ public final class PermissionManager { */ public static final int PERMISSION_HARD_DENIED = 2; + /** + * The set of flags that indicate that a permission state has been explicitly set + * + * @hide + */ + public static final int EXPLICIT_SET_FLAGS = FLAG_PERMISSION_USER_SET + | FLAG_PERMISSION_USER_FIXED | FLAG_PERMISSION_POLICY_FIXED + | FLAG_PERMISSION_SYSTEM_FIXED | FLAG_PERMISSION_GRANTED_BY_DEFAULT + | FLAG_PERMISSION_GRANTED_BY_ROLE; + /** * Activity action: Launch UI to review permission decisions. *

@@ -1447,6 +1463,7 @@ public final class PermissionManager { * @hide */ @TestApi + @RequiresPermission(Manifest.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL) public void revokePostNotificationPermissionWithoutKillForTest(@NonNull String packageName, int userId) { try { diff --git a/services/core/java/com/android/server/notification/PermissionHelper.java b/services/core/java/com/android/server/notification/PermissionHelper.java index 5d18069ea205a..86ac7c185f034 100644 --- a/services/core/java/com/android/server/notification/PermissionHelper.java +++ b/services/core/java/com/android/server/notification/PermissionHelper.java @@ -179,7 +179,7 @@ public final class PermissionHelper { assertFlag(); final long callingId = Binder.clearCallingIdentity(); try { - if (grant) { + if (grant && !reviewRequired) { mPermManager.grantRuntimePermission(packageName, NOTIFICATION_PERMISSION, userId); } else { mPermManager.revokeRuntimePermission(packageName, NOTIFICATION_PERMISSION, userId, @@ -210,8 +210,10 @@ public final class PermissionHelper { if (pkgPerm == null || pkgPerm.packageName == null) { return; } - setNotificationPermission(pkgPerm.packageName, pkgPerm.userId, pkgPerm.granted, - pkgPerm.userSet, !pkgPerm.userSet); + if (!isPermissionFixed(pkgPerm.packageName, pkgPerm.userId)) { + setNotificationPermission(pkgPerm.packageName, pkgPerm.userId, pkgPerm.granted, + pkgPerm.userSet, !pkgPerm.userSet); + } } public boolean isPermissionFixed(String packageName, @UserIdInt int userId) { @@ -239,7 +241,8 @@ public final class PermissionHelper { try { int flags = mPermManager.getPermissionFlags(packageName, NOTIFICATION_PERMISSION, userId); - return (flags & PackageManager.FLAG_PERMISSION_USER_SET) != 0; + return (flags & (PackageManager.FLAG_PERMISSION_USER_SET + | PackageManager.FLAG_PERMISSION_USER_FIXED)) != 0; } catch (RemoteException e) { Slog.e(TAG, "Could not reach system server", e); } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java index ed351fd4aef91..e9074c429aac4 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java @@ -17,6 +17,7 @@ package com.android.server.pm.permission; import static android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY; +import static android.Manifest.permission.POST_NOTIFICATIONS; import static android.Manifest.permission.READ_EXTERNAL_STORAGE; import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; import static android.content.pm.PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT; @@ -752,11 +753,14 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt flagValues &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; flagMask &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; flagValues &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT; - flagValues &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; flagValues &= ~FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; flagValues &= ~FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; flagValues &= ~FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; flagValues &= ~PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION; + // REVIEW_REQUIRED can only be set by non-system apps for for POST_NOTIFICATIONS + if (!POST_NOTIFICATIONS.equals(permName)) { + flagValues &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; + } } final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName); diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java index c9a8701ec7afe..c637c67640926 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyService.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -37,7 +37,7 @@ import android.app.AppOpsManagerInternal; import android.app.TaskInfo; import android.app.compat.CompatChanges; import android.compat.annotation.ChangeId; -import android.compat.annotation.EnabledSince; +import android.compat.annotation.EnabledAfter; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; @@ -58,6 +58,7 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.UserHandle; import android.permission.PermissionControllerManager; +import android.permission.PermissionManager; import android.provider.Settings; import android.provider.Telephony; import android.telecom.TelecomManager; @@ -141,7 +142,7 @@ public final class PermissionPolicyService extends SystemService { * This change reflects the presence of the new Notification Permission */ @ChangeId - @EnabledSince(targetSdkVersion = Build.VERSION_CODES.TIRAMISU) + @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.S_V2) private static final long NOTIFICATION_PERM_CHANGE_ID = 194833441L; private List mAppOpPermissions; @@ -149,7 +150,6 @@ public final class PermissionPolicyService extends SystemService { private Context mContext; private PackageManagerInternal mPackageManagerInternal; private NotificationManagerInternal mNotificationManager; - private PermissionManagerServiceInternal mPermissionManagerService; private final PackageManager mPackageManager; public PermissionPolicyService(@NonNull Context context) { @@ -1001,13 +1001,48 @@ public final class PermissionPolicyService extends SystemService { private class Internal extends PermissionPolicyInternal { - private ActivityInterceptorCallback mActivityInterceptorCallback = + // UIDs that, if a grant dialog is shown for POST_NOTIFICATIONS before next reboot, + // should display a "continue allowing" message, rather than an "allow" message + private final ArraySet mContinueNotifGrantMessageUids = new ArraySet<>(); + + private final ActivityInterceptorCallback mActivityInterceptorCallback = new ActivityInterceptorCallback() { @Nullable @Override public ActivityInterceptorCallback.ActivityInterceptResult intercept( ActivityInterceptorInfo info) { - return null; + String action = info.intent.getAction(); + ActivityInterceptResult result = null; + if (!PackageManager.ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(action) + && !PackageManager.ACTION_REQUEST_PERMISSIONS.equals(action)) { + return null; + } + // Only this interceptor can add LEGACY_ACCESS_PERMISSION_NAMES + if (info.intent.getStringArrayExtra(PackageManager + .EXTRA_REQUEST_PERMISSIONS_LEGACY_ACCESS_PERMISSION_NAMES) + != null) { + result = new ActivityInterceptResult( + new Intent(info.intent), info.checkedOptions); + result.intent.removeExtra(PackageManager + .EXTRA_REQUEST_PERMISSIONS_LEGACY_ACCESS_PERMISSION_NAMES); + } + if (PackageManager.ACTION_REQUEST_PERMISSIONS.equals(action) + && !mContinueNotifGrantMessageUids.contains(info.realCallingUid)) { + return result; + } + if (PackageManager.ACTION_REQUEST_PERMISSIONS_FOR_OTHER.equals(action)) { + String otherPkg = info.intent.getStringExtra(Intent.EXTRA_PACKAGE_NAME); + if (otherPkg == null || (mPackageManager.getPermissionFlags( + POST_NOTIFICATIONS, otherPkg, UserHandle.of(info.userId)) + & FLAG_PERMISSION_REVIEW_REQUIRED) == 0) { + return result; + } + } + + mContinueNotifGrantMessageUids.remove(info.realCallingUid); + return new ActivityInterceptResult(info.intent.putExtra(PackageManager + .EXTRA_REQUEST_PERMISSIONS_LEGACY_ACCESS_PERMISSION_NAMES, + new String[] { POST_NOTIFICATIONS }), info.checkedOptions); } @Override @@ -1057,12 +1092,21 @@ public final class PermissionPolicyService extends SystemService { launchNotificationPermissionRequestDialog(packageName, user, taskId); } - private void clearNotificationReviewFlagsIfNeeded(String packageName, UserHandle userId) { - if (!CompatChanges.isChangeEnabled(NOTIFICATION_PERM_CHANGE_ID, packageName, userId)) { + private void clearNotificationReviewFlagsIfNeeded(String packageName, UserHandle user) { + if (!CompatChanges.isChangeEnabled(NOTIFICATION_PERM_CHANGE_ID, packageName, user) + || ((mPackageManager.getPermissionFlags(POST_NOTIFICATIONS, packageName, user) + & FLAG_PERMISSION_REVIEW_REQUIRED) == 0)) { return; } - mPackageManager.updatePermissionFlags(POST_NOTIFICATIONS, packageName, - FLAG_PERMISSION_REVIEW_REQUIRED, 0, userId); + try { + int uid = mPackageManager.getPackageUidAsUser(packageName, 0, + user.getIdentifier()); + mContinueNotifGrantMessageUids.add(uid); + mPackageManager.updatePermissionFlags(POST_NOTIFICATIONS, packageName, + FLAG_PERMISSION_REVIEW_REQUIRED, 0, user); + } catch (PackageManager.NameNotFoundException e) { + // Do nothing + } } private void launchNotificationPermissionRequestDialog(String pkgName, UserHandle user, @@ -1142,8 +1186,10 @@ public final class PermissionPolicyService extends SystemService { if (pkg == null || pkg.getPackageName() == null || Objects.equals(pkgName, mPackageManager.getPermissionControllerPackageName()) || pkg.getTargetSdkVersion() < Build.VERSION_CODES.M) { - Slog.w(LOG_TAG, "Cannot check for Notification prompt, no package for " - + pkgName + " or pkg is Permission Controller"); + if (pkg == null) { + Slog.w(LOG_TAG, "Cannot check for Notification prompt, no package for " + + pkgName); + } return false; } @@ -1175,9 +1221,10 @@ public final class PermissionPolicyService extends SystemService { } boolean hasCreatedNotificationChannels = mNotificationManager .getNumNotificationChannelsForPackage(pkg.getPackageName(), uid, true) > 0; - boolean needsReview = (mPackageManager.getPermissionFlags(POST_NOTIFICATIONS, pkgName, - user) & FLAG_PERMISSION_REVIEW_REQUIRED) != 0; - return hasCreatedNotificationChannels && needsReview; + int flags = mPackageManager.getPermissionFlags(POST_NOTIFICATIONS, pkgName, user); + boolean explicitlySet = (flags & PermissionManager.EXPLICIT_SET_FLAGS) != 0; + boolean needsReview = (flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0; + return hasCreatedNotificationChannels && (needsReview || !explicitlySet); } } } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java index 3b6718207c834..a3440b4bdb54e 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/PermissionHelperTest.java @@ -233,8 +233,8 @@ public class PermissionHelperTest extends UiServiceTestCase { public void testSetNotificationPermission_grantReviewRequired() throws Exception { mPermissionHelper.setNotificationPermission("pkg", 10, true, false, true); - verify(mPermManager).grantRuntimePermission( - "pkg", Manifest.permission.POST_NOTIFICATIONS, 10); + verify(mPermManager).revokeRuntimePermission( + "pkg", Manifest.permission.POST_NOTIFICATIONS, 10, "PermissionHelper"); verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS, FLAG_PERMISSION_REVIEW_REQUIRED, FLAG_PERMISSION_REVIEW_REQUIRED, true, 10); } @@ -245,8 +245,8 @@ public class PermissionHelperTest extends UiServiceTestCase { "pkg", 10, true, false); mPermissionHelper.setNotificationPermission(pkgPerm); - verify(mPermManager).grantRuntimePermission( - "pkg", Manifest.permission.POST_NOTIFICATIONS, 10); + verify(mPermManager).revokeRuntimePermission( + "pkg", Manifest.permission.POST_NOTIFICATIONS, 10, "PermissionHelper"); verify(mPermManager).updatePermissionFlags("pkg", Manifest.permission.POST_NOTIFICATIONS, FLAG_PERMISSION_REVIEW_REQUIRED, FLAG_PERMISSION_REVIEW_REQUIRED, true, 10); }