Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ed12d506 authored by Nate Myren's avatar Nate Myren
Browse files

Clear the "hasSensitiveContent" bit if connected to Wifi

When sending notifications to the SystemUi that have the
"hasSensitiveContent" bit set, if the device is connected to wifi, clear
the bit. We don't want system UI redacting the notification content
(absent other redaction rules) based on OTPs if connected to wifi.

Test: atest NotificationManagerServiceTest
Flag: android.app.redact_sensitive_content_notifications_on_lockscreen
Bug: 358403414
Change-Id: Ia337ca546f921e322dbe12c7a7ca1b57ed6242ad
parent 6b4d2a3c
Loading
Loading
Loading
Loading
+57 −7
Original line number Diff line number Diff line
@@ -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;
@@ -253,6 +254,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;
@@ -654,6 +659,7 @@ public class NotificationManagerService extends SystemService {
    private UsageStatsManagerInternal mUsageStatsManagerInternal;
    private TelecomManager mTelecomManager;
    private PowerManager mPowerManager;
    private ConnectivityManager mConnectivityManager;
    private PostNotificationTrackerFactory mPostNotificationTrackerFactory;
    private LockPatternUtils mLockUtils;
@@ -775,6 +781,8 @@ public class NotificationManagerService extends SystemService {
    private ModuleInfo mAdservicesModuleInfo;
    private boolean mConnectedToWifi;
    static class Archive {
        final SparseArray<Boolean> mEnabled;
        final int mBufferSize;
@@ -2541,6 +2549,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();
@@ -2573,6 +2582,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));
@@ -2788,6 +2799,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.
     */
@@ -2897,6 +2938,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,
@@ -11576,13 +11618,21 @@ 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)) {
            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,
                    rankings.size(),
@@ -11611,7 +11661,7 @@ public class NotificationManagerService extends SystemService {
                            : (record.getRankingScore() > 0 ?  RANKING_PROMOTED : RANKING_DEMOTED),
                    record.getNotification().isBubbleNotification(),
                    record.getProposedImportance(),
                    record.hasSensitiveContent()
                    hasSensitiveContent
            );
            rankings.add(ranking);
        }
+121 −6
Original line number Diff line number Diff line
@@ -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));
@@ -14294,7 +14302,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.
@@ -14304,7 +14313,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.
@@ -14313,7 +14322,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);
    }
@@ -14345,13 +14354,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);
+2 −0
Original line number Diff line number Diff line
@@ -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")) {