diff --git a/core/api/test-current.txt b/core/api/test-current.txt index d03dd1623074..c2f960f85c73 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -282,6 +282,7 @@ package android.app { field public static final String OPSTR_ACTIVITY_RECOGNITION = "android:activity_recognition"; field public static final String OPSTR_ACTIVITY_RECOGNITION_SOURCE = "android:activity_recognition_source"; field public static final String OPSTR_MANAGE_ONGOING_CALLS = "android:manage_ongoing_calls"; + field @FlaggedApi("android.service.notification.redact_sensitive_notifications_from_untrusted_listeners") public static final String OPSTR_RECEIVE_SENSITIVE_NOTIFICATIONS = "android:receive_sensitive_notifications"; field public static final String OPSTR_RECORD_AUDIO_HOTWORD = "android:record_audio_hotword"; field public static final String OPSTR_RESERVED_FOR_TESTING = "android:reserved_for_testing"; field public static final String OPSTR_USE_ICC_AUTH_WITH_DEVICE_IDENTIFIER = "android:use_icc_auth_with_device_identifier"; diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 6865f9c5013b..2313fa27afe1 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -20,6 +20,7 @@ import static android.location.flags.Flags.FLAG_LOCATION_BYPASS; import static android.media.audio.Flags.roForegroundAudioControl; import static android.permission.flags.Flags.FLAG_OP_ENABLE_MOBILE_DATA_BY_USER; +import static android.service.notification.Flags.FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS; import static android.view.contentprotection.flags.Flags.FLAG_CREATE_ACCESSIBILITY_OVERLAY_APP_OP_ENABLED; import static android.view.contentprotection.flags.Flags.FLAG_RAPID_CLEAR_NOTIFICATIONS_BY_LISTENER_APP_OP_ENABLED; @@ -1597,9 +1598,19 @@ public static String flagsToString(@OpFlags int flags) { */ public static final int OP_EMERGENCY_LOCATION = AppProtoEnums.APP_OP_EMERGENCY_LOCATION; + /** + * Allows apps with a NotificationListenerService to receive notifications with sensitive + * information + *

Apps with a NotificationListenerService without this permission will not be able + * to view certain types of sensitive information contained in notifications + * @hide + */ + public static final int OP_RECEIVE_SENSITIVE_NOTIFICATIONS = + AppProtoEnums.APP_OP_RECEIVE_SENSITIVE_NOTIFICATIONS; + /** @hide */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) - public static final int _NUM_OP = 148; + public static final int _NUM_OP = 149; /** * All app ops represented as strings. @@ -1751,6 +1762,7 @@ public static String flagsToString(@OpFlags int flags) { OPSTR_ARCHIVE_ICON_OVERLAY, OPSTR_UNARCHIVAL_CONFIRMATION, OPSTR_EMERGENCY_LOCATION, + OPSTR_RECEIVE_SENSITIVE_NOTIFICATIONS, }) public @interface AppOpString {} @@ -2476,6 +2488,18 @@ public static String flagsToString(@OpFlags int flags) { @FlaggedApi(FLAG_LOCATION_BYPASS) public static final String OPSTR_EMERGENCY_LOCATION = "android:emergency_location"; + /** + * Allows apps with a NotificationListenerService to receive notifications with sensitive + * information + *

Apps with a NotificationListenerService without this permission will not be able + * to view certain types of sensitive information contained in notifications + * @hide + */ + @TestApi + @FlaggedApi(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS) + public static final String OPSTR_RECEIVE_SENSITIVE_NOTIFICATIONS = + "android:receive_sensitive_notifications"; + /** {@link #sAppOpsToNote} not initialized yet for this op */ private static final byte SHOULD_COLLECT_NOTE_OP_NOT_INITIALIZED = 0; /** Should not collect noting of this app-op in {@link #sAppOpsToNote} */ @@ -3055,6 +3079,9 @@ public static String flagsToString(@OpFlags int flags) { // even though this has a permission associated, this op is only used for tracking, // and the client is responsible for checking the LOCATION_BYPASS permission. .setPermission(Manifest.permission.LOCATION_BYPASS).build(), + new AppOpInfo.Builder(OP_RECEIVE_SENSITIVE_NOTIFICATIONS, + OPSTR_RECEIVE_SENSITIVE_NOTIFICATIONS, "RECEIVE_SENSITIVE_NOTIFICATIONS") + .setDefaultMode(MODE_IGNORED).build(), }; // The number of longs needed to form a full bitmask of app ops diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index a8adc06f1860..4023e52e3b13 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -23,6 +23,7 @@ import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.NOT_FOREGROUND_SERVICE; import static android.app.AppOpsManager.MODE_ALLOWED; import static android.app.AppOpsManager.MODE_DEFAULT; +import static android.app.AppOpsManager.OP_RECEIVE_SENSITIVE_NOTIFICATIONS; import static android.app.Flags.FLAG_LIFETIME_EXTENSION_REFACTOR; import static android.app.Flags.lifetimeExtensionRefactor; import static android.app.Flags.sortSectionByTime; @@ -11942,7 +11943,10 @@ boolean isAppTrustedNotificationListenerService(int uid, String pkg) { long token = Binder.clearCallingIdentity(); try { if (mPackageManager.checkUidPermission(RECEIVE_SENSITIVE_NOTIFICATIONS, uid) - == PERMISSION_GRANTED || mPackageManagerInternal.isPlatformSigned(pkg)) { + == PERMISSION_GRANTED || mPackageManagerInternal.isPlatformSigned(pkg) + || mAppOps + .noteOpNoThrow(OP_RECEIVE_SENSITIVE_NOTIFICATIONS, uid, pkg, null, null) + == MODE_ALLOWED) { return true; }