Redact work notifications separately from personal

This will strictly decrease the set of cases where work notifications
can be shown.

They will continue to honour the parent profile's settings, but will
also get redacted/hidden if the work profile has stricter settings
than the parent or when the parent is unlocked but the profile is still
locked.

Bug: 31001762
Change-Id: I2152631dbb8beb7a9899d6406b05f4447d757010
diff --git a/core/java/android/content/Intent.java b/core/java/android/content/Intent.java
index 70010b2..215cb5b 100644
--- a/core/java/android/content/Intent.java
+++ b/core/java/android/content/Intent.java
@@ -3037,6 +3037,18 @@
             "android.intent.action.MANAGED_PROFILE_UNAVAILABLE";
 
     /**
+     * Broadcast sent to the system user when the 'device locked' state changes for any user.
+     * Carries an extra {@link #EXTRA_USER_HANDLE} that specifies the ID of the user for which
+     * the device was locked or unlocked.
+     *
+     * This is only sent to registered receivers.
+     *
+     * @hide
+     */
+    public static final String ACTION_DEVICE_LOCKED_CHANGED =
+            "android.intent.action.DEVICE_LOCKED_CHANGED";
+
+    /**
      * Sent when the user taps on the clock widget in the system's "quick settings" area.
      */
     public static final String ACTION_QUICK_CLOCK =
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index effc3fc..5d62b11 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -494,6 +494,8 @@
 
     <protected-broadcast android:name="com.android.server.retaildemo.ACTION_RESET_DEMO" />
 
+    <protected-broadcast android:name="android.intent.action.DEVICE_LOCKED_CHANGED" />
+
     <!-- ====================================================================== -->
     <!--                          RUNTIME PERMISSIONS                           -->
     <!-- ====================================================================== -->
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
index 75b9417..6c63b7d 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/BaseStatusBar.java
@@ -158,7 +158,7 @@
             "com.android.systemui.statusbar.banner_action_cancel";
     private static final String BANNER_ACTION_SETUP =
             "com.android.systemui.statusbar.banner_action_setup";
-    private static final String WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION
+    private static final String NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION
             = "com.android.systemui.statusbar.work_challenge_unlocked_notification_action";
 
     protected CommandQueue mCommandQueue;
@@ -214,14 +214,14 @@
     protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
 
     // public mode, private notifications, etc
-    private boolean mLockscreenPublicMode = false;
+    private final SparseBooleanArray mLockscreenPublicMode = new SparseBooleanArray();
     private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
     private final SparseBooleanArray mUsersAllowingNotifications = new SparseBooleanArray();
 
     private UserManager mUserManager;
     private int mDensity;
 
-    private KeyguardManager mKeyguardManager;
+    protected KeyguardManager mKeyguardManager;
     private LockPatternUtils mLockPatternUtils;
 
     // UI-specific methods
@@ -460,11 +460,11 @@
             row.setUserExpanded(true);
 
             if (!mAllowLockscreenRemoteInput) {
-                if (isLockscreenPublicMode()) {
+                final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
+                if (isLockscreenPublicMode(userId)) {
                     onLockedRemoteInput(row, view);
                     return true;
                 }
-                final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
                 if (mUserManager.getUserInfo(userId).isManagedProfile()
                         && mKeyguardManager.isDeviceLocked(userId)) {
                     onLockedWorkRemoteInput(userId, row, view);
@@ -555,7 +555,7 @@
 
                     );
                 }
-            } else if (WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION.equals(action)) {
+            } else if (NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION.equals(action)) {
                 final IntentSender intentSender = intent.getParcelableExtra(Intent.EXTRA_INTENT);
                 final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX);
                 if (intentSender != null) {
@@ -572,7 +572,6 @@
                         /* ignore */
                     }
                 }
-                onWorkChallengeUnlocked();
             }
         }
     };
@@ -580,12 +579,18 @@
     private final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
-            String action = intent.getAction();
+            final String action = intent.getAction();
+            final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
+
             if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action) &&
                     isCurrentProfile(getSendingUserId())) {
                 mUsersAllowingPrivateNotifications.clear();
                 updateLockscreenNotificationSetting();
                 updateNotifications();
+            } else if (Intent.ACTION_DEVICE_LOCKED_CHANGED.equals(action)) {
+                if (userId != mCurrentUserId && isCurrentProfile(userId)) {
+                    onWorkChallengeChanged();
+                }
             }
         }
     };
@@ -810,7 +815,7 @@
         mContext.registerReceiver(mBroadcastReceiver, filter);
 
         IntentFilter internalFilter = new IntentFilter();
-        internalFilter.addAction(WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION);
+        internalFilter.addAction(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
         internalFilter.addAction(BANNER_ACTION_CANCEL);
         internalFilter.addAction(BANNER_ACTION_SETUP);
         mContext.registerReceiver(mBroadcastReceiver, internalFilter, PERMISSION_SELF, null);
@@ -818,6 +823,7 @@
         IntentFilter allUsersFilter = new IntentFilter();
         allUsersFilter.addAction(
                 DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
+        allUsersFilter.addAction(Intent.ACTION_DEVICE_LOCKED_CHANGED);
         mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, allUsersFilter,
                 null, null);
         updateCurrentProfilesCache();
@@ -1117,9 +1123,10 @@
             @Override
             public void onClick(View v) {
                 // If the user has security enabled, show challenge if the setting is changed.
-                if (guts.hasImportanceChanged() && isLockscreenPublicMode() &&
-                        (mState == StatusBarState.KEYGUARD
-                        || mState == StatusBarState.SHADE_LOCKED)) {
+                if (guts.hasImportanceChanged()
+                            && isLockscreenPublicMode(sbn.getUser().getIdentifier())
+                            && (mState == StatusBarState.KEYGUARD
+                                    || mState == StatusBarState.SHADE_LOCKED)) {
                     OnDismissAction dismissAction = new OnDismissAction() {
                         @Override
                         public boolean onDismiss() {
@@ -1421,15 +1428,15 @@
     /**
      * Save the current "public" (locked and secure) state of the lockscreen.
      */
-    public void setLockscreenPublicMode(boolean publicMode) {
-        mLockscreenPublicMode = publicMode;
+    public void setLockscreenPublicMode(boolean publicMode, int userId) {
+        mLockscreenPublicMode.put(userId, publicMode);
     }
 
-    public boolean isLockscreenPublicMode() {
-        return mLockscreenPublicMode;
+    public boolean isLockscreenPublicMode(int userId) {
+        return mLockscreenPublicMode.get(userId, false);
     }
 
-    protected void onWorkChallengeUnlocked() {}
+    protected void onWorkChallengeChanged() {}
 
     /**
      * Has the given user chosen to allow notifications to be shown even when the lockscreen is in
@@ -1487,8 +1494,9 @@
      * If so, notifications should be hidden.
      */
     @Override  // NotificationData.Environment
-    public boolean shouldHideNotifications(int userid) {
-        return isLockscreenPublicMode() && !userAllowsNotificationsInPublic(userid);
+    public boolean shouldHideNotifications(int userId) {
+        return isLockscreenPublicMode(mCurrentUserId) && !userAllowsNotificationsInPublic(userId)
+                || (userId != mCurrentUserId && shouldHideNotifications(mCurrentUserId));
     }
 
     /**
@@ -1497,7 +1505,7 @@
      */
     @Override // NotificationDate.Environment
     public boolean shouldHideNotifications(String key) {
-        return isLockscreenPublicMode()
+        return isLockscreenPublicMode(mCurrentUserId)
                 && mNotificationData.getVisibilityOverride(key) == Notification.VISIBILITY_SECRET;
     }
 
@@ -1505,8 +1513,8 @@
      * Returns true if we're on a secure lockscreen.
      */
     @Override  // NotificationData.Environment
-    public boolean onSecureLockScreen() {
-        return isLockscreenPublicMode();
+    public boolean isSecurelyLocked(int userId) {
+        return isLockscreenPublicMode(userId);
     }
 
     public void onNotificationClear(StatusBarNotification notification) {
@@ -2058,8 +2066,7 @@
         if (newIntent == null) {
             return false;
         }
-        final Intent callBackIntent = new Intent(
-                WORK_CHALLENGE_UNLOCKED_NOTIFICATION_ACTION);
+        final Intent callBackIntent = new Intent(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
         callBackIntent.putExtra(Intent.EXTRA_INTENT, intendSender);
         callBackIntent.putExtra(Intent.EXTRA_INDEX, notificationKey);
         callBackIntent.setPackage(mContext.getPackageName());
@@ -2258,14 +2265,16 @@
                 entry.row.setOnKeyguard(false);
                 entry.row.setSystemExpanded(visibleNotifications == 0 && !childNotification);
             }
+            int userId = entry.notification.getUserId();
             boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
                     entry.notification) && !entry.row.isRemoved();
             boolean childWithVisibleSummary = childNotification
                     && mGroupManager.getGroupSummary(entry.notification).getVisibility()
                     == View.VISIBLE;
             boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
-            if (suppressedSummary || (isLockscreenPublicMode() && !mShowLockscreenNotifications) ||
-                    (onKeyguard && !childWithVisibleSummary
+            if (suppressedSummary
+                    || (isLockscreenPublicMode(userId) && !mShowLockscreenNotifications)
+                    || (onKeyguard && !childWithVisibleSummary
                             && (visibleNotifications >= maxNotifications || !showOnKeyguard))) {
                 entry.row.setVisibility(View.GONE);
                 if (onKeyguard && showOnKeyguard && !childNotification && !suppressedSummary) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
index b6e54af..bae16f3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationData.java
@@ -380,7 +380,7 @@
             return true;
         }
 
-        if (mEnvironment.onSecureLockScreen() &&
+        if (mEnvironment.isSecurelyLocked(sbn.getUserId()) &&
                 (sbn.getNotification().visibility == Notification.VISIBILITY_SECRET
                         || mEnvironment.shouldHideNotifications(sbn.getUserId())
                         || mEnvironment.shouldHideNotifications(sbn.getKey()))) {
@@ -463,7 +463,7 @@
      * Provides access to keyguard state and user settings dependent data.
      */
     public interface Environment {
-        public boolean onSecureLockScreen();
+        public boolean isSecurelyLocked(int userId);
         public boolean shouldHideNotifications(int userid);
         public boolean shouldHideNotifications(String key);
         public boolean isDeviceProvisioned();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
index fc3b024..0237593 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/PhoneStatusBar.java
@@ -37,6 +37,7 @@
 import android.app.ActivityManagerNative;
 import android.app.ActivityOptions;
 import android.app.IActivityManager;
+import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.PendingIntent;
 import android.app.StatusBarManager;
@@ -1716,18 +1717,21 @@
         for (int i=0; i<N; i++) {
             Entry ent = activeNotifications.get(i);
             int vis = ent.notification.getNotification().visibility;
+            int userId = ent.notification.getUserId();
 
             // Display public version of the notification if we need to redact.
-            final boolean hideSensitive =
-                    !userAllowsPrivateNotificationsInPublic(ent.notification.getUserId());
+            boolean deviceSensitive = (isLockscreenPublicMode(mCurrentUserId)
+                    && !userAllowsPrivateNotificationsInPublic(mCurrentUserId));
+            boolean userSensitive = deviceSensitive || (isLockscreenPublicMode(userId)
+                    && !userAllowsPrivateNotificationsInPublic(userId));
             boolean sensitiveNote = vis == Notification.VISIBILITY_PRIVATE;
             boolean sensitivePackage = packageHasVisibilityOverride(ent.notification.getKey());
-            boolean sensitive = (sensitiveNote && hideSensitive) || sensitivePackage;
-            boolean showingPublic = sensitive && isLockscreenPublicMode();
+            boolean sensitive = (sensitiveNote && userSensitive) || sensitivePackage;
+            boolean showingPublic = sensitive && isLockscreenPublicMode(userId);
             if (showingPublic) {
                 updatePublicContentView(ent, ent.notification);
             }
-            ent.row.setSensitive(sensitive, hideSensitive);
+            ent.row.setSensitive(sensitive, deviceSensitive);
             if (ent.autoRedacted && ent.legacy) {
                 // TODO: Also fade this? Or, maybe easier (and better), provide a dark redacted form
                 // for legacy auto redacted notifications.
@@ -4275,17 +4279,23 @@
     }
 
     private void updatePublicMode() {
-        boolean isPublic = false;
-        if (mStatusBarKeyguardViewManager.isShowing()) {
-            for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
-                UserInfo userInfo = mCurrentProfiles.valueAt(i);
-                if (mStatusBarKeyguardViewManager.isSecure(userInfo.id)) {
-                    isPublic = true;
-                    break;
+        final boolean showingKeyguard = mStatusBarKeyguardViewManager.isShowing();
+        final boolean devicePublic = showingKeyguard
+                && mStatusBarKeyguardViewManager.isSecure(mCurrentUserId);
+
+        // Look for public mode users. Users are considered public in either case of:
+        //   - device keyguard is shown in secure mode;
+        //   - profile is locked with a work challenge.
+        for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
+            final int userId = mCurrentProfiles.valueAt(i).id;
+            boolean isProfilePublic = devicePublic;
+            if (!devicePublic && userId != mCurrentUserId) {
+                if (mStatusBarKeyguardViewManager.isSecure(userId)) {
+                    isProfilePublic = mKeyguardManager.isDeviceLocked(userId);
                 }
             }
+            setLockscreenPublicMode(isProfilePublic, userId);
         }
-        setLockscreenPublicMode(isPublic);
     }
 
     protected void updateKeyguardState(boolean goingToFullShade, boolean fromShadeLocked) {
@@ -4342,7 +4352,8 @@
     public void updateStackScrollerState(boolean goingToFullShade, boolean fromShadeLocked) {
         if (mStackScroller == null) return;
         boolean onKeyguard = mState == StatusBarState.KEYGUARD;
-        mStackScroller.setHideSensitive(isLockscreenPublicMode(), goingToFullShade);
+        boolean publicMode = isAnyProfilePublicMode();
+        mStackScroller.setHideSensitive(publicMode, goingToFullShade);
         mStackScroller.setDimmed(onKeyguard, fromShadeLocked /* animate */);
         mStackScroller.setExpandingEnabled(!onKeyguard);
         ActivatableNotificationView activatedChild = mStackScroller.getActivatedChild();
@@ -4591,6 +4602,7 @@
      * @param expandView The view to expand after going to the shade.
      */
     public void goToLockedShade(View expandView) {
+        int userId = mCurrentUserId;
         ExpandableNotificationRow row = null;
         if (expandView instanceof ExpandableNotificationRow) {
             row = (ExpandableNotificationRow) expandView;
@@ -4598,10 +4610,13 @@
             // Indicate that the group expansion is changing at this time -- this way the group
             // and children backgrounds / divider animations will look correct.
             row.setGroupExpansionChanging(true);
+            if (row.getStatusBarNotification() != null) {
+                userId = row.getStatusBarNotification().getUserId();
+            }
         }
         boolean fullShadeNeedsBouncer = !userAllowsPrivateNotificationsInPublic(mCurrentUserId)
                 || !mShowLockscreenNotifications || mFalsingManager.shouldEnforceBouncer();
-        if (isLockscreenPublicMode() && fullShadeNeedsBouncer) {
+        if (isLockscreenPublicMode(userId) && fullShadeNeedsBouncer) {
             mLeaveOpenOnKeyguardHide = true;
             showBouncer();
             mDraggedDownRow = row;
@@ -4646,10 +4661,20 @@
         mPendingWorkRemoteInputView = clicked;
     }
 
+    private boolean isAnyProfilePublicMode() {
+        for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
+            if (isLockscreenPublicMode(mCurrentProfiles.valueAt(i).id)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
     @Override
-    protected void onWorkChallengeUnlocked() {
-        if (mPendingWorkRemoteInputView != null) {
-            final View pendingWorkRemoteInputView = mPendingWorkRemoteInputView;
+    protected void onWorkChallengeChanged() {
+        updatePublicMode();
+        updateNotifications();
+        if (mPendingWorkRemoteInputView != null && !isAnyProfilePublicMode()) {
             // Expand notification panel and the notification row, then click on remote input view
             final Runnable clickPendingViewRunnable = new Runnable() {
                 @Override
diff --git a/services/core/java/com/android/server/trust/TrustManagerService.java b/services/core/java/com/android/server/trust/TrustManagerService.java
index d219aed..353d663 100644
--- a/services/core/java/com/android/server/trust/TrustManagerService.java
+++ b/services/core/java/com/android/server/trust/TrustManagerService.java
@@ -872,6 +872,11 @@
                         } catch (RemoteException e) {
                         }
                     }
+                    final Intent lockIntent = new Intent(Intent.ACTION_DEVICE_LOCKED_CHANGED);
+                    lockIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
+                    lockIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
+                    mContext.sendBroadcastAsUser(lockIntent, UserHandle.SYSTEM,
+                            Manifest.permission.TRUST_LISTENER, /* options */ null);
                 }
             } finally {
                 Binder.restoreCallingIdentity(identity);