Allow shell to revoke notification permission without kill

Add the revokePostNotificationPermissionWithoutKillForTest API, which
will allow the shell to revoke the POST_NOTIFICATIONS permission without
killing this app. Gate this permission behind the
REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL permission, which is
signature|privileged, accessible only to the shell.

Ignore-AOSP-First: Contains information about unreleased features
Test: manual
Bug: 194833441
Change-Id: I3177d1aeb338591c1d736aa6b4f073b6db6227e7
This commit is contained in:
Nate Myren
2022-01-06 13:20:36 -08:00
parent 7d24159ba2
commit 2c54f50da6
8 changed files with 85 additions and 5 deletions

View File

@@ -36,6 +36,7 @@ package android {
field public static final String RECORD_BACKGROUND_AUDIO = "android.permission.RECORD_BACKGROUND_AUDIO";
field public static final String REMOVE_TASKS = "android.permission.REMOVE_TASKS";
field public static final String RESET_APP_ERRORS = "android.permission.RESET_APP_ERRORS";
field public static final String REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL = "android.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL";
field public static final String SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS = "android.permission.SET_AND_VERIFY_LOCKSCREEN_CREDENTIALS";
field public static final String START_TASKS_FROM_RECENTS = "android.permission.START_TASKS_FROM_RECENTS";
field public static final String SUSPEND_APPS = "android.permission.SUSPEND_APPS";
@@ -2036,6 +2037,7 @@ package android.permission {
method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermGroupUsage> getIndicatorAppOpUsageData();
method @NonNull @RequiresPermission(android.Manifest.permission.GET_APP_OPS_STATS) public java.util.List<android.permission.PermGroupUsage> getIndicatorAppOpUsageData(boolean);
method @NonNull public android.content.AttributionSource registerAttributionSource(@NonNull android.content.AttributionSource);
method public void revokePostNotificationPermissionWithoutKillForTest(@NonNull String, int);
}
}

View File

@@ -67,6 +67,8 @@ interface IPermissionManager {
void revokeRuntimePermission(String packageName, String permissionName, int userId,
String reason);
void revokePostNotificationPermissionWithoutKillForTest(String packageName, int userId);
boolean shouldShowRequestPermissionRationale(String packageName, String permissionName,
int userId);

View File

@@ -1362,6 +1362,26 @@ public final class PermissionManager {
return false;
}
/**
* Revoke the POST_NOTIFICATIONS permission, without killing the app. This method must ONLY BE
* USED in CTS or local tests.
*
* @param packageName The package to be revoked
* @param userId The user for which to revoke
*
* @hide
*/
@TestApi
public void revokePostNotificationPermissionWithoutKillForTest(@NonNull String packageName,
int userId) {
try {
mPermissionManager.revokePostNotificationPermissionWithoutKillForTest(packageName,
userId);
} catch (RemoteException e) {
e.rethrowFromSystemServer();
}
}
/* @hide */
private static int checkPermissionUncached(@Nullable String permission, int pid, int uid) {
final IActivityManager am = ActivityManager.getService();

View File

@@ -4438,6 +4438,12 @@
<permission android:name="android.permission.REVOKE_RUNTIME_PERMISSIONS"
android:protectionLevel="signature|installer|verifier" />
<!-- @TestApi Allows an application to revoke the POST_NOTIFICATIONS permission from an app
without killing the app. Only granted to the shell.
@hide -->
<permission android:name="android.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL"
android:protectionLevel="signature" />
<!-- @SystemApi Allows the system to read runtime permission state.
@hide -->
<permission android:name="android.permission.GET_RUNTIME_PERMISSIONS"

View File

@@ -613,6 +613,9 @@
<!-- Permission required for CTS test - CtsSafetyCenterTestCases -->
<uses-permission android:name="android.permission.READ_SAFETY_CENTER_STATUS" />
<!-- Permission required for CTS test - Notification test suite -->
<uses-permission android:name="android.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL" />
<application android:label="@string/app_label"
android:theme="@android:style/Theme.DeviceDefault.DayNight"
android:defaultToDeviceProtectedStorage="true"

View File

@@ -548,6 +548,12 @@ public class PermissionManagerService extends IPermissionManager.Stub {
reason);
}
@Override
public void revokePostNotificationPermissionWithoutKillForTest(String packageName, int userId) {
mPermissionManagerServiceImpl.revokePostNotificationPermissionWithoutKillForTest(
packageName, userId);
}
@Override
public boolean shouldShowRequestPermissionRationale(String packageName, String permissionName,
int userId) {

View File

@@ -170,6 +170,10 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
private static final String TAG = "PackageManager";
private static final String LOG_TAG = PermissionManagerServiceImpl.class.getSimpleName();
private static final String SKIP_KILL_APP_REASON_NOTIFICATION_TEST = "skip permission revoke "
+ "app kill for notification test";
private static final long BACKUP_TIMEOUT_MILLIS = SECONDS.toMillis(60);
// For automotive products, CarService enforces allow-listing of the privileged permissions
@@ -326,11 +330,15 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
mPackageManagerInt.writeSettings(true);
}
@Override
public void onPermissionRevoked(int uid, int userId, String reason) {
public void onPermissionRevoked(int uid, int userId, String reason, boolean overrideKill) {
mOnPermissionChangeListeners.onPermissionsChanged(uid);
// Critical; after this call the application should never have the permission
mPackageManagerInt.writeSettings(false);
if (overrideKill) {
return;
}
final int appId = UserHandle.getAppId(uid);
if (reason == null) {
mHandler.post(() -> killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED));
@@ -1445,9 +1453,29 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
reason, mDefaultPermissionCallback);
}
@Override
public void revokePostNotificationPermissionWithoutKillForTest(String packageName, int userId) {
final int callingUid = Binder.getCallingUid();
final boolean overridePolicy =
checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY)
== PackageManager.PERMISSION_GRANTED;
mContext.enforceCallingPermission(
android.Manifest.permission.REVOKE_POST_NOTIFICATIONS_WITHOUT_KILL, "");
revokeRuntimePermissionInternal(packageName, Manifest.permission.POST_NOTIFICATIONS,
overridePolicy, true, callingUid, userId,
SKIP_KILL_APP_REASON_NOTIFICATION_TEST, mDefaultPermissionCallback);
}
private void revokeRuntimePermissionInternal(String packageName, String permName,
boolean overridePolicy, int callingUid, final int userId, String reason,
PermissionCallback callback) {
boolean overridePolicy, int callingUid, final int userId,
String reason, PermissionCallback callback) {
revokeRuntimePermissionInternal(packageName, permName, overridePolicy, false, callingUid,
userId, reason, callback);
}
private void revokeRuntimePermissionInternal(String packageName, String permName,
boolean overridePolicy, boolean overrideKill, int callingUid, final int userId,
String reason, PermissionCallback callback) {
if (PermissionManager.DEBUG_TRACE_PERMISSION_UPDATES
&& PermissionManager.shouldTraceGrant(packageName, permName, userId)) {
Log.i(TAG, "System is revoking " + packageName + " "
@@ -1559,7 +1587,7 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
if (callback != null) {
if (isRuntimePermission) {
callback.onPermissionRevoked(UserHandle.getUid(userId, pkg.getUid()), userId,
reason);
reason, overrideKill);
} else {
mDefaultPermissionCallback.onInstallPermissionRevoked();
}
@@ -5232,7 +5260,11 @@ public class PermissionManagerServiceImpl implements PermissionManagerServiceInt
public void onPermissionChanged() {}
public void onPermissionGranted(int uid, @UserIdInt int userId) {}
public void onInstallPermissionGranted() {}
public void onPermissionRevoked(int uid, @UserIdInt int userId, String reason) {}
public void onPermissionRevoked(int uid, @UserIdInt int userId, String reason) {
onPermissionRevoked(uid, userId, reason, false);
}
public void onPermissionRevoked(int uid, @UserIdInt int userId, String reason,
boolean overrideKill) {}
public void onInstallPermissionRevoked() {}
public void onPermissionUpdated(@UserIdInt int[] updatedUserIds, boolean sync) {}
public void onPermissionUpdatedNotifyListener(@UserIdInt int[] updatedUserIds, boolean sync,

View File

@@ -317,6 +317,15 @@ public interface PermissionManagerServiceInterface extends PermissionManagerInte
void revokeRuntimePermission(String packageName, String permName, int userId,
String reason);
/**
* Revoke the POST_NOTIFICATIONS permission, without killing the app. This method must ONLY BE
* USED in CTS or local tests.
*
* @param packageName The package to be revoked
* @param userId The user for which to revoke
*/
void revokePostNotificationPermissionWithoutKillForTest(String packageName, int userId);
/**
* Get whether you should show UI with rationale for requesting a permission. You should do this
* only if you do not have the permission and the context in which the permission is requested