diff options
3 files changed, 180 insertions, 13 deletions
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index f3eed7d22bec..837003f87598 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -27,6 +27,7 @@ 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.notificationClassificationUi; +import static android.app.Flags.redactSensitiveContentNotificationsOnLockscreen; import static android.app.Flags.sortSectionByTime; import static android.app.Notification.BubbleMetadata.FLAG_SUPPRESS_NOTIFICATION; import static android.app.Notification.EXTRA_BUILDER_APPLICATION_INFO; @@ -254,6 +255,10 @@ import android.content.pm.VersionedPackage; import android.content.res.Resources; import android.database.ContentObserver; import android.metrics.LogMaker; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; +import android.net.NetworkRequest; import android.net.Uri; import android.os.Binder; import android.os.Build; @@ -656,6 +661,7 @@ public class NotificationManagerService extends SystemService { private UsageStatsManagerInternal mUsageStatsManagerInternal; private TelecomManager mTelecomManager; private PowerManager mPowerManager; + private ConnectivityManager mConnectivityManager; private PostNotificationTrackerFactory mPostNotificationTrackerFactory; private LockPatternUtils mLockUtils; @@ -777,6 +783,8 @@ public class NotificationManagerService extends SystemService { private ModuleInfo mAdservicesModuleInfo; + private boolean mConnectedToWifi; + static class Archive { final SparseArray<Boolean> mEnabled; final int mBufferSize; @@ -2573,6 +2581,7 @@ public class NotificationManagerService extends SystemService { TelecomManager telecomManager, NotificationChannelLogger channelLogger, SystemUiSystemPropertiesFlags.FlagResolver flagResolver, PermissionManager permissionManager, PowerManager powerManager, + ConnectivityManager connectivityManager, PostNotificationTrackerFactory postNotificationTrackerFactory) { mHandler = handler; Resources resources = getContext().getResources(); @@ -2605,6 +2614,8 @@ public class NotificationManagerService extends SystemService { mUm = userManager; mTelecomManager = telecomManager; mPowerManager = powerManager; + mConnectivityManager = connectivityManager; + registerNetworkCallback(); mPostNotificationTrackerFactory = postNotificationTrackerFactory; mPlatformCompat = IPlatformCompat.Stub.asInterface( ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); @@ -2820,6 +2831,36 @@ public class NotificationManagerService extends SystemService { mAppOps.startWatchingMode(AppOpsManager.OP_POST_NOTIFICATION, null, mAppOpsListener); } + private void registerNetworkCallback() { + NetworkRequest request = new NetworkRequest.Builder().addTransportType( + NetworkCapabilities.TRANSPORT_WIFI).build(); + mConnectivityManager.registerNetworkCallback(request, + new ConnectivityManager.NetworkCallback() { + // Need to post to another thread, as we can't call synchronous ConnectivityManager + // methods from the callback itself, due to potential race conditions. + @Override + public void onAvailable(@NonNull Network network) { + mHandler.post(() -> updateWifiConnectionState()); + } + @Override + public void onLost(@NonNull Network network) { + mHandler.post(() -> updateWifiConnectionState()); + } + }); + updateWifiConnectionState(); + } + + @VisibleForTesting() + void updateWifiConnectionState() { + Network current = mConnectivityManager.getActiveNetwork(); + NetworkCapabilities capabilities = mConnectivityManager.getNetworkCapabilities(current); + if (current == null || capabilities == null) { + mConnectedToWifi = false; + return; + } + mConnectedToWifi = capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI); + } + /** * Cleanup broadcast receivers change listeners. */ @@ -2930,6 +2971,7 @@ public class NotificationManagerService extends SystemService { new NotificationChannelLoggerImpl(), SystemUiSystemPropertiesFlags.getResolver(), getContext().getSystemService(PermissionManager.class), getContext().getSystemService(PowerManager.class), + getContext().getSystemService(ConnectivityManager.class), new PostNotificationTrackerFactory() {}); publishBinderService(Context.NOTIFICATION_SERVICE, mService, /* allowIsolated= */ false, @@ -11645,12 +11687,20 @@ public class NotificationManagerService extends SystemService { new NotificationListenerService.Ranking(); ArrayList<Notification.Action> smartActions = record.getSystemGeneratedSmartActions(); ArrayList<CharSequence> smartReplies = record.getSmartReplies(); - if (redactSensitiveNotificationsFromUntrustedListeners() - && info != null - && !mListeners.isUidTrusted(info.uid) - && mListeners.hasSensitiveContent(record)) { - smartActions = null; - smartReplies = null; + boolean hasSensitiveContent = record.hasSensitiveContent(); + if (redactSensitiveNotificationsFromUntrustedListeners()) { + if (!mListeners.isUidTrusted(info.uid) && mListeners.hasSensitiveContent(record)) { + smartActions = null; + smartReplies = null; + } + if (redactSensitiveContentNotificationsOnLockscreen()) { + if (mListeners.hasSensitiveContent(record) && mConnectedToWifi + && info.isSystemUi) { + // We don't inform systemUI of sensitive content if + // connected to wifi, though we do still redact from untrusted listeners. + hasSensitiveContent = false; + } + } } ranking.populate( key, @@ -11680,7 +11730,7 @@ public class NotificationManagerService extends SystemService { : (record.getRankingScore() > 0 ? RANKING_PROMOTED : RANKING_DEMOTED), record.getNotification().isBubbleNotification(), record.getProposedImportance(), - record.hasSensitiveContent() + hasSensitiveContent ); rankings.add(ranking); } diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index e43b28bb9404..fdb6a6802b7e 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -24,6 +24,7 @@ import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.NOT_ import static android.app.ActivityManagerInternal.ServiceNotificationPolicy.SHOW_IMMEDIATELY; import static android.app.ActivityTaskManager.INVALID_TASK_ID; import static android.app.Flags.FLAG_KEYGUARD_PRIVATE_NOTIFICATIONS; +import static android.app.Flags.FLAG_REDACT_SENSITIVE_CONTENT_NOTIFICATIONS_ON_LOCKSCREEN; import static android.app.Flags.FLAG_SORT_SECTION_BY_TIME; import static android.app.Notification.EXTRA_ALLOW_DURING_SETUP; import static android.app.Notification.EXTRA_PICTURE; @@ -251,6 +252,9 @@ import android.graphics.drawable.Icon; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.session.MediaSession; +import android.net.ConnectivityManager; +import android.net.Network; +import android.net.NetworkCapabilities; import android.net.Uri; import android.os.Binder; import android.os.Build; @@ -475,6 +479,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { @Mock private PowerManager mPowerManager; @Mock + private ConnectivityManager mConnectivityManager; + @Mock private LightsManager mLightsManager; private final ArrayList<WakeLock> mAcquiredWakeLocks = new ArrayList<>(); private final TestPostNotificationTrackerFactory mPostNotificationTrackerFactory = @@ -765,6 +771,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mActivityIntentImmutable = spy(PendingIntent.getActivity(mContext, 0, new Intent().setPackage(mPkg), FLAG_IMMUTABLE)); + when(mConnectivityManager.getActiveNetwork()).thenReturn(null); + initNMS(); } @@ -798,7 +806,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mAppOpsManager, mUm, mHistoryManager, mStatsManager, mAmi, mToastRateLimiter, mPermissionHelper, mock(UsageStatsManagerInternal.class), mTelecomManager, mLogger, mTestFlagResolver, mPermissionManager, - mPowerManager, mPostNotificationTrackerFactory); + mPowerManager, mConnectivityManager, mPostNotificationTrackerFactory); mService.setAttentionHelper(mAttentionHelper); mService.setLockPatternUtils(mock(LockPatternUtils.class)); @@ -14314,7 +14322,8 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { mService.addNotification(pkgB); mService.setIsVisibleToListenerReturnValue(true); - NotificationRankingUpdate nru = mService.makeRankingUpdateLocked(null); + ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class); + NotificationRankingUpdate nru = mService.makeRankingUpdateLocked(info); assertEquals(2, nru.getRankingMap().getOrderedKeys().length); // when only user 0 entering the lockdown mode, its notification will be suppressed. @@ -14324,7 +14333,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertTrue(mStrongAuthTracker.isInLockDownMode(0)); assertFalse(mStrongAuthTracker.isInLockDownMode(1)); - nru = mService.makeRankingUpdateLocked(null); + nru = mService.makeRankingUpdateLocked(info); assertEquals(1, nru.getRankingMap().getOrderedKeys().length); // User 0 exits lockdown mode. Its notification will be resumed. @@ -14333,7 +14342,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertFalse(mStrongAuthTracker.isInLockDownMode(0)); assertFalse(mStrongAuthTracker.isInLockDownMode(1)); - nru = mService.makeRankingUpdateLocked(null); + nru = mService.makeRankingUpdateLocked(info); assertEquals(2, nru.getRankingMap().getOrderedKeys().length); } @@ -14365,13 +14374,119 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { assertEquals(0, ranking2.getSmartReplies().size()); } + private NotificationRecord getSensitiveNotificationRecord() { + NotificationRecord record = new NotificationRecord(mContext, + generateSbn("a", 1000, 9, 0), mTestNotificationChannel); + Bundle signals = new Bundle(); + signals.putBoolean(Adjustment.KEY_SENSITIVE_CONTENT, true); + Adjustment adjustment = new Adjustment("a", record.getKey(), signals, "", 0); + record.addAdjustment(adjustment); + record.applyAdjustments(); + return record; + } + + @Test + public void testMakeRankingUpdate_clearsHasSensitiveContentIfConnectedToWifi() { + mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS, + FLAG_REDACT_SENSITIVE_CONTENT_NOTIFICATIONS_ON_LOCKSCREEN); + when(mConnectivityManager.getActiveNetwork()).thenReturn(mock(Network.class)); + when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn( + new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .build() + ); + mService.updateWifiConnectionState(); + when(mListeners.hasSensitiveContent(any())).thenReturn(true); + NotificationRecord pkgA = new NotificationRecord(mContext, + generateSbn("a", 1000, 9, 0), mTestNotificationChannel); + mService.addNotification(pkgA); + ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class); + info.isSystemUi = true; + when(info.enabledAndUserMatches(anyInt())).thenReturn(true); + when(info.isSameUser(anyInt())).thenReturn(true); + NotificationRankingUpdate nru = mService.makeRankingUpdateLocked(info); + NotificationListenerService.Ranking ranking = + nru.getRankingMap().getRawRankingObject(pkgA.getSbn().getKey()); + assertFalse(ranking.hasSensitiveContent()); + } + + @Test + public void testMakeRankingUpdate_doesntClearHasSensitiveContentIfNotConnectedToWifi() { + mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS, + FLAG_REDACT_SENSITIVE_CONTENT_NOTIFICATIONS_ON_LOCKSCREEN); + when(mConnectivityManager.getActiveNetwork()).thenReturn(mock(Network.class)); + when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn( + new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .build() + ); + mService.updateWifiConnectionState(); + when(mListeners.hasSensitiveContent(any())).thenReturn(true); + NotificationRecord record = getSensitiveNotificationRecord(); + mService.addNotification(record); + ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class); + info.isSystemUi = true; + when(info.enabledAndUserMatches(anyInt())).thenReturn(true); + when(info.isSameUser(anyInt())).thenReturn(true); + NotificationRankingUpdate nru = mService.makeRankingUpdateLocked(info); + NotificationListenerService.Ranking ranking = + nru.getRankingMap().getRawRankingObject(record.getSbn().getKey()); + assertTrue(ranking.hasSensitiveContent()); + } + + @Test + public void testMakeRankingUpdate_doesntClearHasSensitiveContentIfNotSysUi() { + mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS); + mSetFlagsRule.disableFlags(FLAG_REDACT_SENSITIVE_CONTENT_NOTIFICATIONS_ON_LOCKSCREEN); + when(mConnectivityManager.getActiveNetwork()).thenReturn(mock(Network.class)); + when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn( + new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .build() + ); + mService.updateWifiConnectionState(); + when(mListeners.hasSensitiveContent(any())).thenReturn(true); + NotificationRecord record = getSensitiveNotificationRecord(); + mService.addNotification(record); + ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class); + when(info.enabledAndUserMatches(anyInt())).thenReturn(true); + when(info.isSameUser(anyInt())).thenReturn(true); + NotificationRankingUpdate nru = mService.makeRankingUpdateLocked(info); + NotificationListenerService.Ranking ranking = + nru.getRankingMap().getRawRankingObject(record.getSbn().getKey()); + assertTrue(ranking.hasSensitiveContent()); + } + + @Test + public void testMakeRankingUpdate_doesntClearHasSensitiveContentIfFlagDisabled() { + mSetFlagsRule.enableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS); + mSetFlagsRule.disableFlags(FLAG_REDACT_SENSITIVE_CONTENT_NOTIFICATIONS_ON_LOCKSCREEN); + when(mConnectivityManager.getActiveNetwork()).thenReturn(mock(Network.class)); + when(mConnectivityManager.getNetworkCapabilities(any())).thenReturn( + new NetworkCapabilities.Builder() + .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) + .build() + ); + mService.updateWifiConnectionState(); + when(mListeners.hasSensitiveContent(any())).thenReturn(true); + NotificationRecord record = getSensitiveNotificationRecord(); + mService.addNotification(record); + ManagedServices.ManagedServiceInfo info = mock(ManagedServices.ManagedServiceInfo.class); + info.isSystemUi = true; + when(info.enabledAndUserMatches(anyInt())).thenReturn(true); + when(info.isSameUser(anyInt())).thenReturn(true); + NotificationRankingUpdate nru = mService.makeRankingUpdateLocked(info); + NotificationListenerService.Ranking ranking = + nru.getRankingMap().getRawRankingObject(record.getSbn().getKey()); + assertTrue(ranking.hasSensitiveContent()); + } + @Test public void testMakeRankingUpdate_doestntRedactIfFlagDisabled() { mSetFlagsRule.disableFlags(FLAG_REDACT_SENSITIVE_NOTIFICATIONS_FROM_UNTRUSTED_LISTENERS); when(mListeners.isUidTrusted(anyInt())).thenReturn(false); when(mListeners.hasSensitiveContent(any())).thenReturn(true); - NotificationRecord pkgA = new NotificationRecord(mContext, - generateSbn("a", 1000, 9, 0), mTestNotificationChannel); + NotificationRecord pkgA = getSensitiveNotificationRecord(); addSmartActionsAndReplies(pkgA); mService.addNotification(pkgA); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java index c1bb3e7408fc..82d87d40031a 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/RoleObserverTest.java @@ -47,6 +47,7 @@ import android.companion.ICompanionDeviceManager; import android.content.Context; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; +import android.net.ConnectivityManager; import android.os.Looper; import android.os.PowerManager; import android.os.UserHandle; @@ -171,6 +172,7 @@ public class RoleObserverTest extends UiServiceTestCase { mock(NotificationChannelLogger.class), new TestableFlagResolver(), mock(PermissionManager.class), mock(PowerManager.class), + mock(ConnectivityManager.class), new NotificationManagerService.PostNotificationTrackerFactory() {}); } catch (SecurityException e) { if (!e.getMessage().contains("Permission Denial: not allowed to send broadcast")) { |