Split NotificationUserManager out of StatusBar.

This class handles functionality related to private/public contents,
redacting notifications, whether to show notifications on the
keyguard, and user changes.

Bug: 63874929
Bug: 62602530
Test: runtest systemui
Test: Compile and run
Change-Id: If44fdf5f89c1e72c4bd5f49731fd4a158ea50552
diff --git a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
index 526a8f4..f28096f 100644
--- a/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
+++ b/packages/SystemUI/src/com/android/systemui/SystemUIFactory.java
@@ -27,6 +27,8 @@
 import com.android.systemui.Dependency.DependencyProvider;
 import com.android.systemui.keyguard.DismissCallbackRegistry;
 import com.android.systemui.statusbar.KeyguardIndicationController;
+import com.android.systemui.statusbar.NotificationGutsManager;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.ScrimView;
 import com.android.systemui.statusbar.phone.DozeParameters;
 import com.android.systemui.statusbar.phone.KeyguardBouncer;
@@ -39,7 +41,6 @@
 import com.android.systemui.statusbar.phone.ScrimController;
 import com.android.systemui.statusbar.phone.StatusBarIconController;
 import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.volume.VolumeDialogControllerImpl;
 
 import java.util.function.Consumer;
 
@@ -113,5 +114,10 @@
     }
 
     public void injectDependencies(ArrayMap<Object, DependencyProvider> providers,
-            Context context) { }
+            Context context) {
+        providers.put(NotificationLockscreenUserManager.class,
+                () -> new NotificationLockscreenUserManager(context));
+        providers.put(NotificationGutsManager.class, () -> new NotificationGutsManager(
+                Dependency.get(NotificationLockscreenUserManager.class), context));
+    }
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
index f451fda..2e572e1 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationGutsManager.java
@@ -37,7 +37,6 @@
 
 import com.android.internal.logging.MetricsLogger;
 import com.android.internal.logging.nano.MetricsProto;
-import com.android.internal.statusbar.IStatusBarService;
 import com.android.systemui.Dependency;
 import com.android.systemui.Dumpable;
 import com.android.systemui.Interpolators;
@@ -66,30 +65,27 @@
 
     private final MetricsLogger mMetricsLogger = Dependency.get(MetricsLogger.class);
     private final Set<String> mNonBlockablePkgs;
-    private final NotificationPresenter mPresenter;
-    // TODO: Create NotificationListContainer interface and use it instead of
-    // NotificationStackScrollLayout here
-    private final NotificationStackScrollLayout mStackScroller;
     private final Context mContext;
     private final AccessibilityManager mAccessibilityManager;
+    private final NotificationLockscreenUserManager mLockscreenUserManager;
+
     // which notification is currently being longpress-examined by the user
     private NotificationGuts mNotificationGutsExposed;
     private NotificationMenuRowPlugin.MenuItem mGutsMenuItem;
-    private final NotificationInfo.CheckSaveListener mCheckSaveListener;
-    private final OnSettingsClickListener mOnSettingsClickListener;
+    private NotificationPresenter mPresenter;
+
+    // TODO: Create NotificationListContainer interface and use it instead of
+    // NotificationStackScrollLayout here
+    private NotificationStackScrollLayout mStackScroller;
+    private NotificationInfo.CheckSaveListener mCheckSaveListener;
+    private OnSettingsClickListener mOnSettingsClickListener;
     private String mKeyToRemoveOnGutsClosed;
 
     public NotificationGutsManager(
-            NotificationPresenter presenter,
-            NotificationStackScrollLayout stackScroller,
-            NotificationInfo.CheckSaveListener checkSaveListener,
-            Context context,
-            OnSettingsClickListener onSettingsClickListener) {
-        mPresenter = presenter;
-        mStackScroller = stackScroller;
-        mCheckSaveListener = checkSaveListener;
+            NotificationLockscreenUserManager lockscreenUserManager,
+            Context context) {
+        mLockscreenUserManager = lockscreenUserManager;
         mContext = context;
-        mOnSettingsClickListener = onSettingsClickListener;
         Resources res = context.getResources();
 
         mNonBlockablePkgs = new HashSet<>();
@@ -100,6 +96,16 @@
                 mContext.getSystemService(Context.ACCESSIBILITY_SERVICE);
     }
 
+    public void setUp(NotificationPresenter presenter,
+            NotificationStackScrollLayout stackScroller,
+            NotificationInfo.CheckSaveListener checkSaveListener,
+            OnSettingsClickListener onSettingsClickListener) {
+        mPresenter = presenter;
+        mStackScroller = stackScroller;
+        mCheckSaveListener = checkSaveListener;
+        mOnSettingsClickListener = onSettingsClickListener;
+    }
+
     public String getKeyToRemoveOnGutsClosed() {
         return mKeyToRemoveOnGutsClosed;
     }
@@ -189,7 +195,7 @@
             // system user.
             NotificationInfo.OnSettingsClickListener onSettingsClick = null;
             if (!userHandle.equals(UserHandle.ALL)
-                    || mPresenter.getCurrentUserId() == UserHandle.USER_SYSTEM) {
+                    || mLockscreenUserManager.getCurrentUserId() == UserHandle.USER_SYSTEM) {
                 onSettingsClick = (View v, NotificationChannel channel, int appUid) -> {
                     mMetricsLogger.action(MetricsProto.MetricsEvent.ACTION_NOTE_INFO);
                     guts.resetFalsingCheck();
@@ -354,7 +360,8 @@
 
     @Override
     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
-        pw.print("mKeyToRemoveOnGutsClosed: ");
+        pw.println("NotificationGutsManager state:");
+        pw.print("  mKeyToRemoveOnGutsClosed: ");
         pw.println(mKeyToRemoveOnGutsClosed);
     }
 
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
new file mode 100644
index 0000000..644d834
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationLockscreenUserManager.java
@@ -0,0 +1,455 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.statusbar;
+
+import android.app.ActivityManager;
+import android.app.Notification;
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.pm.UserInfo;
+import android.database.ContentObserver;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.service.notification.StatusBarNotification;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.SparseBooleanArray;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.android.internal.statusbar.IStatusBarService;
+import com.android.systemui.Dependency;
+import com.android.systemui.Dumpable;
+import com.android.systemui.OverviewProxyService;
+import com.android.systemui.R;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+
+/**
+ * Handles keeping track of the current user, profiles, and various things related to hiding
+ * contents, redacting notifications, and the lockscreen.
+ */
+public class NotificationLockscreenUserManager implements Dumpable {
+    private static final String TAG = "LockscreenUserManager";
+    private static final boolean ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT = false;
+    public static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
+    public static final String NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION
+            = "com.android.systemui.statusbar.work_challenge_unlocked_notification_action";
+
+    private final DevicePolicyManager mDevicePolicyManager;
+    private final SparseBooleanArray mLockscreenPublicMode = new SparseBooleanArray();
+    private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
+    private final SparseBooleanArray mUsersAllowingNotifications = new SparseBooleanArray();
+    private final DeviceProvisionedController mDeviceProvisionedController =
+            Dependency.get(DeviceProvisionedController.class);
+    private final UserManager mUserManager;
+    private final IStatusBarService mBarService;
+
+    private boolean mShowLockscreenNotifications;
+    private boolean mAllowLockscreenRemoteInput;
+
+    protected final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            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();
+                mPresenter.updateNotifications();
+            } else if (Intent.ACTION_DEVICE_LOCKED_CHANGED.equals(action)) {
+                if (userId != mCurrentUserId && isCurrentProfile(userId)) {
+                    mPresenter.onWorkChallengeChanged();
+                }
+            }
+        }
+    };
+
+    protected final BroadcastReceiver mBaseBroadcastReceiver = new BroadcastReceiver() {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            String action = intent.getAction();
+            if (Intent.ACTION_USER_SWITCHED.equals(action)) {
+                mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
+                updateCurrentProfilesCache();
+                Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
+
+                updateLockscreenNotificationSetting();
+
+                mPresenter.onUserSwitched(mCurrentUserId);
+            } else if (Intent.ACTION_USER_ADDED.equals(action)) {
+                updateCurrentProfilesCache();
+            } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
+                // Start the overview connection to the launcher service
+                Dependency.get(OverviewProxyService.class).startConnectionToCurrentUser();
+            } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
+                List<ActivityManager.RecentTaskInfo> recentTask = null;
+                try {
+                    recentTask = ActivityManager.getService().getRecentTasks(1,
+                            ActivityManager.RECENT_WITH_EXCLUDED,
+                            mCurrentUserId).getList();
+                } catch (RemoteException e) {
+                    // Abandon hope activity manager not running.
+                }
+                if (recentTask != null && recentTask.size() > 0) {
+                    UserInfo user = mUserManager.getUserInfo(recentTask.get(0).userId);
+                    if (user != null && user.isManagedProfile()) {
+                        Toast toast = Toast.makeText(mContext,
+                                R.string.managed_profile_foreground_toast,
+                                Toast.LENGTH_SHORT);
+                        TextView text = toast.getView().findViewById(android.R.id.message);
+                        text.setCompoundDrawablesRelativeWithIntrinsicBounds(
+                                R.drawable.stat_sys_managed_profile_status, 0, 0, 0);
+                        int paddingPx = mContext.getResources().getDimensionPixelSize(
+                                R.dimen.managed_profile_toast_padding);
+                        text.setCompoundDrawablePadding(paddingPx);
+                        toast.show();
+                    }
+                }
+            } 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) {
+                    try {
+                        mContext.startIntentSender(intentSender, null, 0, 0, 0);
+                    } catch (IntentSender.SendIntentException e) {
+                        /* ignore */
+                    }
+                }
+                if (notificationKey != null) {
+                    try {
+                        mBarService.onNotificationClick(notificationKey);
+                    } catch (RemoteException e) {
+                        /* ignore */
+                    }
+                }
+            }
+        }
+    };
+
+    protected final Context mContext;
+    protected final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<>();
+
+    protected int mCurrentUserId = 0;
+    protected NotificationPresenter mPresenter;
+    protected ContentObserver mLockscreenSettingsObserver;
+    protected ContentObserver mSettingsObserver;
+
+    public NotificationLockscreenUserManager(Context context) {
+        mContext = context;
+        mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
+                Context.DEVICE_POLICY_SERVICE);
+        mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
+        mCurrentUserId = ActivityManager.getCurrentUser();
+        mBarService = IStatusBarService.Stub.asInterface(
+                ServiceManager.getService(Context.STATUS_BAR_SERVICE));
+    }
+
+    public void setUpWithPresenter(NotificationPresenter presenter) {
+        mPresenter = presenter;
+
+        mLockscreenSettingsObserver = new ContentObserver(mPresenter.getHandler()) {
+            @Override
+            public void onChange(boolean selfChange) {
+                // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS or
+                // LOCK_SCREEN_SHOW_NOTIFICATIONS, so we just dump our cache ...
+                mUsersAllowingPrivateNotifications.clear();
+                mUsersAllowingNotifications.clear();
+                // ... and refresh all the notifications
+                updateLockscreenNotificationSetting();
+                mPresenter.updateNotifications();
+            }
+        };
+
+        mSettingsObserver = new ContentObserver(mPresenter.getHandler()) {
+            @Override
+            public void onChange(boolean selfChange) {
+                updateLockscreenNotificationSetting();
+                if (mDeviceProvisionedController.isDeviceProvisioned()) {
+                    mPresenter.updateNotifications();
+                }
+            }
+        };
+
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
+                mLockscreenSettingsObserver,
+                UserHandle.USER_ALL);
+
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
+                true,
+                mLockscreenSettingsObserver,
+                UserHandle.USER_ALL);
+
+        mContext.getContentResolver().registerContentObserver(
+                Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
+                mSettingsObserver);
+
+        if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
+            mContext.getContentResolver().registerContentObserver(
+                    Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT),
+                    false,
+                    mSettingsObserver,
+                    UserHandle.USER_ALL);
+        }
+
+        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);
+
+        IntentFilter filter = new IntentFilter();
+        filter.addAction(Intent.ACTION_USER_SWITCHED);
+        filter.addAction(Intent.ACTION_USER_ADDED);
+        filter.addAction(Intent.ACTION_USER_PRESENT);
+        filter.addAction(Intent.ACTION_USER_UNLOCKED);
+        mContext.registerReceiver(mBaseBroadcastReceiver, filter);
+
+        IntentFilter internalFilter = new IntentFilter();
+        internalFilter.addAction(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
+        mContext.registerReceiver(mBaseBroadcastReceiver, internalFilter, PERMISSION_SELF, null);
+
+        updateCurrentProfilesCache();
+
+        mSettingsObserver.onChange(false);  // set up
+    }
+
+    public boolean shouldShowLockscreenNotifications() {
+        return mShowLockscreenNotifications;
+    }
+
+    public boolean shouldAllowLockscreenRemoteInput() {
+        return mAllowLockscreenRemoteInput;
+    }
+
+    public boolean isCurrentProfile(int userId) {
+        synchronized (mCurrentProfiles) {
+            return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null;
+        }
+    }
+
+    /**
+     * Returns true if we're on a secure lockscreen and the user wants to hide notification data.
+     * If so, notifications should be hidden.
+     */
+    public boolean shouldHideNotifications(int userId) {
+        return isLockscreenPublicMode(userId) && !userAllowsNotificationsInPublic(userId)
+                || (userId != mCurrentUserId && shouldHideNotifications(mCurrentUserId));
+    }
+
+    /**
+     * Returns true if we're on a secure lockscreen and the user wants to hide notifications via
+     * package-specific override.
+     */
+    public boolean shouldHideNotifications(String key) {
+        return isLockscreenPublicMode(mCurrentUserId)
+                && mPresenter.getNotificationData().getVisibilityOverride(key) ==
+                        Notification.VISIBILITY_SECRET;
+    }
+
+    public boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
+        return mShowLockscreenNotifications
+                && !mPresenter.getNotificationData().isAmbient(sbn.getKey());
+    }
+
+    private void setShowLockscreenNotifications(boolean show) {
+        mShowLockscreenNotifications = show;
+    }
+
+    private void setLockscreenAllowRemoteInput(boolean allowLockscreenRemoteInput) {
+        mAllowLockscreenRemoteInput = allowLockscreenRemoteInput;
+    }
+
+    protected void updateLockscreenNotificationSetting() {
+        final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
+                1,
+                mCurrentUserId) != 0;
+        final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(
+                null /* admin */, mCurrentUserId);
+        final boolean allowedByDpm = (dpmFlags
+                & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
+
+        setShowLockscreenNotifications(show && allowedByDpm);
+
+        if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
+            final boolean remoteInput = Settings.Secure.getIntForUser(mContext.getContentResolver(),
+                    Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT,
+                    0,
+                    mCurrentUserId) != 0;
+            final boolean remoteInputDpm =
+                    (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT) == 0;
+
+            setLockscreenAllowRemoteInput(remoteInput && remoteInputDpm);
+        } else {
+            setLockscreenAllowRemoteInput(false);
+        }
+    }
+
+    /**
+     * Has the given user chosen to allow their private (full) notifications to be shown even
+     * when the lockscreen is in "public" (secure & locked) mode?
+     */
+    public boolean userAllowsPrivateNotificationsInPublic(int userHandle) {
+        if (userHandle == UserHandle.USER_ALL) {
+            return true;
+        }
+
+        if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
+            final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
+                    mContext.getContentResolver(),
+                    Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
+            final boolean allowedByDpm = adminAllowsUnredactedNotifications(userHandle);
+            final boolean allowed = allowedByUser && allowedByDpm;
+            mUsersAllowingPrivateNotifications.append(userHandle, allowed);
+            return allowed;
+        }
+
+        return mUsersAllowingPrivateNotifications.get(userHandle);
+    }
+
+    private boolean adminAllowsUnredactedNotifications(int userHandle) {
+        if (userHandle == UserHandle.USER_ALL) {
+            return true;
+        }
+        final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */,
+                userHandle);
+        return (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) == 0;
+    }
+
+    /**
+     * Save the current "public" (locked and secure) state of the lockscreen.
+     */
+    public void setLockscreenPublicMode(boolean publicMode, int userId) {
+        mLockscreenPublicMode.put(userId, publicMode);
+    }
+
+    public boolean isLockscreenPublicMode(int userId) {
+        if (userId == UserHandle.USER_ALL) {
+            return mLockscreenPublicMode.get(mCurrentUserId, false);
+        }
+        return mLockscreenPublicMode.get(userId, false);
+    }
+
+    /**
+     * Has the given user chosen to allow notifications to be shown even when the lockscreen is in
+     * "public" (secure & locked) mode?
+     */
+    private boolean userAllowsNotificationsInPublic(int userHandle) {
+        if (userHandle == UserHandle.USER_ALL) {
+            return true;
+        }
+
+        if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
+            final boolean allowed = 0 != Settings.Secure.getIntForUser(
+                    mContext.getContentResolver(),
+                    Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle);
+            mUsersAllowingNotifications.append(userHandle, allowed);
+            return allowed;
+        }
+
+        return mUsersAllowingNotifications.get(userHandle);
+    }
+
+    /** @return true if the entry needs redaction when on the lockscreen. */
+    public boolean needsRedaction(NotificationData.Entry ent) {
+        int userId = ent.notification.getUserId();
+
+        boolean currentUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(mCurrentUserId);
+        boolean notiUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(userId);
+        boolean redactedLockscreen = currentUserWantsRedaction || notiUserWantsRedaction;
+
+        boolean notificationRequestsRedaction =
+                ent.notification.getNotification().visibility == Notification.VISIBILITY_PRIVATE;
+        boolean userForcesRedaction = packageHasVisibilityOverride(ent.notification.getKey());
+
+        return userForcesRedaction || notificationRequestsRedaction && redactedLockscreen;
+    }
+
+    private boolean packageHasVisibilityOverride(String key) {
+        return mPresenter.getNotificationData().getVisibilityOverride(key) ==
+                Notification.VISIBILITY_PRIVATE;
+    }
+
+
+    private void updateCurrentProfilesCache() {
+        synchronized (mCurrentProfiles) {
+            mCurrentProfiles.clear();
+            if (mUserManager != null) {
+                for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) {
+                    mCurrentProfiles.put(user.id, user);
+                }
+            }
+        }
+    }
+
+    public boolean isAnyProfilePublicMode() {
+        for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
+            if (isLockscreenPublicMode(mCurrentProfiles.valueAt(i).id)) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    /**
+     * Returns the current user id. This can change if the user is switched.
+     */
+    public int getCurrentUserId() {
+        return mCurrentUserId;
+    }
+
+    public SparseArray<UserInfo> getCurrentProfiles() {
+        return mCurrentProfiles;
+    }
+
+    public void destroy() {
+        mContext.unregisterReceiver(mBaseBroadcastReceiver);
+        mContext.unregisterReceiver(mAllUsersReceiver);
+    }
+
+    @Override
+    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+        pw.println("NotificationLockscreenUserManager state:");
+        pw.print("  mCurrentUserId=");
+        pw.println(mCurrentUserId);
+        pw.print("  mShowLockscreenNotifications=");
+        pw.println(mShowLockscreenNotifications);
+        pw.print("  mAllowLockscreenRemoteInput=");
+        pw.println(mAllowLockscreenRemoteInput);
+        pw.print("  mCurrentProfiles=");
+        for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
+            final int userId = mCurrentProfiles.valueAt(i).id;
+            pw.print("" + userId + " ");
+        }
+        pw.println();
+    }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
index e65bab2..283a6e3 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationMediaManager.java
@@ -1,3 +1,18 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
 package com.android.systemui.statusbar;
 
 import android.app.Notification;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
index 1aca60c..8670887 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationPresenter.java
@@ -16,6 +16,7 @@
 package com.android.systemui.statusbar;
 
 import android.content.Intent;
+import android.os.Handler;
 import android.service.notification.NotificationListenerService;
 
 /**
@@ -39,11 +40,6 @@
     boolean isPresenterLocked();
 
     /**
-     * Returns the current user id. This can change if the user is switched.
-     */
-    int getCurrentUserId();
-
-    /**
      * Runs the given intent. The presenter may want to run some animations or close itself when
      * this happens.
      */
@@ -54,6 +50,11 @@
      */
     NotificationData getNotificationData();
 
+    /**
+     * Returns the Handler for NotificationPresenter.
+     */
+    Handler getHandler();
+
     // TODO: Create NotificationEntryManager and move this method to there.
     /**
      * Signals that some notifications have changed, and NotificationPresenter should update itself.
@@ -76,4 +77,20 @@
      * Gets the latest ranking map.
      */
     NotificationListenerService.RankingMap getLatestRankingMap();
+
+    /**
+     * Called when the locked status of the device is changed for a work profile.
+     */
+    void onWorkChallengeChanged();
+
+    /**
+     * Called when the current user changes.
+     * @param newUserId new user id
+     */
+    void onUserSwitched(int newUserId);
+
+    /**
+     * Gets the NotificationLockscreenUserManager for this Presenter.
+     */
+    NotificationLockscreenUserManager getNotificationLockscreenUserManager();
 }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
index 5941af2..5ba6f6a 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/car/CarStatusBar.java
@@ -335,8 +335,8 @@
     }
 
     @Override
-    public void userSwitched(int newUserId) {
-        super.userSwitched(newUserId);
+    public void onUserSwitched(int newUserId) {
+        super.onUserSwitched(newUserId);
         if (mFullscreenUserSwitcher != null) {
             mFullscreenUserSwitcher.onUserSwitched(newUserId);
         }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
index fff6abe..9a837ef 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationPanelView.java
@@ -503,7 +503,8 @@
             if (suppressedSummary) {
                 continue;
             }
-            if (!mStatusBar.shouldShowOnKeyguard(row.getStatusBarNotification())) {
+            if (!mStatusBar.getNotificationLockscreenUserManager().shouldShowOnKeyguard(
+                    row.getStatusBarNotification())) {
                 continue;
             }
             if (row.isRemoved()) {
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
index a5609da..87be9bd 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBar.java
@@ -16,7 +16,6 @@
 
 package com.android.systemui.statusbar.phone;
 
-import static android.app.StatusBarManager.DISABLE2_NOTIFICATION_SHADE;
 import static android.app.StatusBarManager.WINDOW_STATE_HIDDEN;
 import static android.app.StatusBarManager.WINDOW_STATE_SHOWING;
 import static android.app.StatusBarManager.windowStateToString;
@@ -25,6 +24,9 @@
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_ASLEEP;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_AWAKE;
 import static com.android.systemui.keyguard.WakefulnessLifecycle.WAKEFULNESS_WAKING;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager
+        .NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION;
+import static com.android.systemui.statusbar.NotificationLockscreenUserManager.PERMISSION_SELF;
 import static com.android.systemui.statusbar.NotificationMediaManager.DEBUG_MEDIA;
 import static com.android.systemui.statusbar.notification.NotificationInflater.InflationCallback;
 import static com.android.systemui.statusbar.phone.BarTransitions.MODE_LIGHTS_OUT;
@@ -108,7 +110,6 @@
 import android.util.Log;
 import android.util.Slog;
 import android.util.SparseArray;
-import android.util.SparseBooleanArray;
 import android.view.Display;
 import android.view.IWindowManager;
 import android.view.KeyEvent;
@@ -127,7 +128,6 @@
 import android.widget.ImageView;
 import android.widget.RemoteViews;
 import android.widget.TextView;
-import android.widget.Toast;
 
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.colorextraction.ColorExtractor;
@@ -153,7 +153,6 @@
 import com.android.systemui.EventLogTags;
 import com.android.systemui.ForegroundServiceController;
 import com.android.systemui.Interpolators;
-import com.android.systemui.OverviewProxyService;
 import com.android.systemui.Prefs;
 import com.android.systemui.R;
 import com.android.systemui.RecentsComponent;
@@ -201,6 +200,7 @@
 import com.android.systemui.statusbar.NotificationData.Entry;
 import com.android.systemui.statusbar.NotificationGutsManager;
 import com.android.systemui.statusbar.NotificationInfo;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.NotificationMediaManager;
 import com.android.systemui.statusbar.NotificationPresenter;
 import com.android.systemui.statusbar.NotificationShelf;
@@ -238,7 +238,6 @@
         .OnChildLocationsChangedListener;
 import com.android.systemui.util.NotificationChannels;
 import com.android.systemui.util.leak.LeakDetector;
-import com.android.systemui.util.wakelock.WakeLock;
 import com.android.systemui.volume.VolumeComponent;
 
 import java.io.FileDescriptor;
@@ -267,7 +266,6 @@
             = SystemProperties.getBoolean("debug.child_notifs", true);
     public static final boolean FORCE_REMOTE_INPUT_HISTORY =
             SystemProperties.getBoolean("debug.force_remoteinput_history", true);
-    private static final boolean ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT = false;
 
     protected static final int MSG_HIDE_RECENT_APPS = 1020;
     protected static final int MSG_PRELOAD_RECENT_APPS = 1022;
@@ -278,7 +276,6 @@
     protected static final boolean ENABLE_HEADS_UP = true;
     protected static final String SETTING_HEADS_UP_TICKER = "ticker_gets_heads_up";
 
-    private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
 
     // Should match the values in PhoneWindowManager
     public static final String SYSTEM_DIALOG_REASON_HOME_KEY = "homekey";
@@ -289,8 +286,6 @@
             "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 NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION
-            = "com.android.systemui.statusbar.work_challenge_unlocked_notification_action";
     public static final String TAG = "StatusBar";
     public static final boolean DEBUG = false;
     public static final boolean SPEW = false;
@@ -380,8 +375,6 @@
      */
     protected int mState;
     protected boolean mBouncerShowing;
-    protected boolean mShowLockscreenNotifications;
-    protected boolean mAllowLockscreenRemoteInput;
 
     private PhoneStatusBarPolicy mIconPolicy;
 
@@ -544,6 +537,7 @@
             new PorterDuffXfermode(PorterDuff.Mode.SRC_OVER);
 
     private NotificationMediaManager mMediaManager;
+    protected NotificationLockscreenUserManager mLockscreenUserManager;
 
     /** Keys of notifications currently visible to the user. */
     private final ArraySet<NotificationVisibility> mCurrentlyVisibleNotifications =
@@ -742,6 +736,8 @@
         mSystemServicesProxy = SystemServicesProxy.getInstance(mContext);
         mOverlayManager = IOverlayManager.Stub.asInterface(
                 ServiceManager.getService(Context.OVERLAY_SERVICE));
+        mLockscreenUserManager = Dependency.get(NotificationLockscreenUserManager.class);
+        mGutsManager = Dependency.get(NotificationGutsManager.class);
 
         mColorExtractor = Dependency.get(SysuiColorExtractor.class);
         mColorExtractor.addOnColorsChangedListener(this);
@@ -777,26 +773,6 @@
 
         mDeviceProvisionedController = Dependency.get(DeviceProvisionedController.class);
         mDeviceProvisionedController.addCallback(mDeviceProvisionedListener);
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
-                mSettingsObserver);
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
-                mLockscreenSettingsObserver,
-                UserHandle.USER_ALL);
-        if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
-            mContext.getContentResolver().registerContentObserver(
-                    Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT),
-                    false,
-                    mSettingsObserver,
-                    UserHandle.USER_ALL);
-        }
-
-        mContext.getContentResolver().registerContentObserver(
-                Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
-                true,
-                mLockscreenSettingsObserver,
-                UserHandle.USER_ALL);
 
         mBarService = IStatusBarService.Stub.asInterface(
                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
@@ -808,6 +784,7 @@
         mLockPatternUtils = new LockPatternUtils(mContext);
 
         mMediaManager = new NotificationMediaManager(this, mContext);
+        mLockscreenUserManager.setUpWithPresenter(this);
 
         // Connect in to the status bar manager service
         mCommandQueue = getComponent(CommandQueue.class);
@@ -828,7 +805,6 @@
 
         createAndAddWindows();
 
-        mSettingsObserver.onChange(false); // set up
         mCommandQueue.disable(switches[0], switches[6], false /* animate */);
         setSystemUiVisibility(switches[1], switches[7], switches[8], 0xffffffff,
                 fullscreenStackBounds, dockedStackBounds);
@@ -863,29 +839,13 @@
                    ));
         }
 
-        mCurrentUserId = ActivityManager.getCurrentUser();
-        setHeadsUpUser(mCurrentUserId);
-
-        IntentFilter filter = new IntentFilter();
-        filter.addAction(Intent.ACTION_USER_SWITCHED);
-        filter.addAction(Intent.ACTION_USER_ADDED);
-        filter.addAction(Intent.ACTION_USER_PRESENT);
-        filter.addAction(Intent.ACTION_USER_UNLOCKED);
-        mContext.registerReceiver(mBaseBroadcastReceiver, filter);
+        setHeadsUpUser(mLockscreenUserManager.getCurrentUserId());
 
         IntentFilter internalFilter = new IntentFilter();
-        internalFilter.addAction(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
         internalFilter.addAction(BANNER_ACTION_CANCEL);
         internalFilter.addAction(BANNER_ACTION_SETUP);
-        mContext.registerReceiver(mBaseBroadcastReceiver, internalFilter, PERMISSION_SELF, null);
-
-        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();
+        mContext.registerReceiver(mBannerActionBroadcastReceiver, internalFilter, PERMISSION_SELF,
+                null);
 
         IVrManager vrManager = IVrManager.Stub.asInterface(ServiceManager.getService(
                 Context.VR_SERVICE));
@@ -899,7 +859,6 @@
 
         // Lastly, call to the icon policy to install/update all the icons.
         mIconPolicy = new PhoneStatusBarPolicy(mContext, mIconController);
-        mSettingsObserver.onChange(false); // set up
 
         mHeadsUpObserver.onChange(true); // set up
         if (ENABLE_HEADS_UP) {
@@ -944,8 +903,7 @@
         // into fragments, but the rest here, it leaves some awkward lifecycle and whatnot.
         mNotificationPanel = mStatusBarWindow.findViewById(R.id.notification_panel);
         mStackScroller = mStatusBarWindow.findViewById(R.id.notification_stack_scroller);
-        mGutsManager = new NotificationGutsManager(this, mStackScroller,
-                mCheckSaveListener, mContext,
+        mGutsManager.setUp(this, mStackScroller, mCheckSaveListener,
                 key -> {
                     try {
                         mBarService.onNotificationSettingsViewed(key);
@@ -1431,7 +1389,7 @@
                 }
             }
             try {
-                mBarService.onClearAllNotifications(mCurrentUserId);
+                mBarService.onClearAllNotifications(mLockscreenUserManager.getCurrentUserId());
             } catch (Exception ex) {
             }
         });
@@ -1471,15 +1429,6 @@
         }
     }
 
-    protected void setZenMode(int mode) {
-        // start old BaseStatusBar.setZenMode().
-        if (isDeviceProvisioned()) {
-            mZenMode = mode;
-            updateNotifications();
-        }
-        // end old BaseStatusBar.setZenMode().
-    }
-
     protected void startKeyguard() {
         Trace.beginSection("StatusBar#startKeyguard");
         KeyguardViewMediator keyguardViewMediator = getComponent(KeyguardViewMediator.class);
@@ -1876,12 +1825,17 @@
             int userId = ent.notification.getUserId();
 
             // Display public version of the notification if we need to redact.
-            boolean devicePublic = isLockscreenPublicMode(mCurrentUserId);
-            boolean userPublic = devicePublic || isLockscreenPublicMode(userId);
-            boolean needsRedaction = needsRedaction(ent);
+            // TODO: This area uses a lot of calls into NotificationLockscreenUserManager.
+            // We can probably move some of this code there.
+            boolean devicePublic = mLockscreenUserManager.isLockscreenPublicMode(
+                    mLockscreenUserManager.getCurrentUserId());
+            boolean userPublic = devicePublic
+                    || mLockscreenUserManager.isLockscreenPublicMode(userId);
+            boolean needsRedaction = mLockscreenUserManager.needsRedaction(ent);
             boolean sensitive = userPublic && needsRedaction;
             boolean deviceSensitive = devicePublic
-                    && !userAllowsPrivateNotificationsInPublic(mCurrentUserId);
+                    && !mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(
+                    mLockscreenUserManager.getCurrentUserId());
             ent.row.setSensitive(sensitive, deviceSensitive);
             ent.row.setNeedsRedaction(needsRedaction);
             if (mGroupManager.isChildInGroupWithSummary(ent.row.getStatusBarNotification())) {
@@ -1973,21 +1927,6 @@
         mNotificationIconAreaController.updateNotificationIcons(mNotificationData);
     }
 
-    /** @return true if the entry needs redaction when on the lockscreen. */
-    private boolean needsRedaction(Entry ent) {
-        int userId = ent.notification.getUserId();
-
-        boolean currentUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(mCurrentUserId);
-        boolean notiUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(userId);
-        boolean redactedLockscreen = currentUserWantsRedaction || notiUserWantsRedaction;
-
-        boolean notificationRequestsRedaction =
-                ent.notification.getNotification().visibility == Notification.VISIBILITY_PRIVATE;
-        boolean userForcesRedaction = packageHasVisibilityOverride(ent.notification.getKey());
-
-        return userForcesRedaction || notificationRequestsRedaction && redactedLockscreen;
-    }
-
     /**
      * Disable QS if device not provisioned.
      * If the user switcher is simple then disable QS during setup because
@@ -2090,9 +2029,6 @@
         mQSPanel.clickTile(tile);
     }
 
-    private boolean packageHasVisibilityOverride(String key) {
-        return mNotificationData.getVisibilityOverride(key) == Notification.VISIBILITY_PRIVATE;
-    }
 
     private void updateClearAll() {
         if (!mClearAllEnabled) {
@@ -2694,7 +2630,7 @@
         OverlayInfo themeInfo = null;
         try {
             themeInfo = mOverlayManager.getOverlayInfo("com.android.systemui.theme.dark",
-                    mCurrentUserId);
+                    mLockscreenUserManager.getCurrentUserId());
         } catch (RemoteException e) {
             e.printStackTrace();
         }
@@ -3333,13 +3269,11 @@
         pw.println(BarTransitions.modeToString(mStatusBarMode));
         pw.print("  mDozing="); pw.println(mDozing);
         pw.print("  mZenMode=");
-        pw.println(Settings.Global.zenModeToString(mZenMode));
+        pw.println(Settings.Global.zenModeToString(Settings.Global.getInt(
+                mContext.getContentResolver(), Settings.Global.ZEN_MODE,
+                Settings.Global.ZEN_MODE_OFF)));
         pw.print("  mUseHeadsUp=");
         pw.println(mUseHeadsUp);
-        pw.print("  mGutsManager: ");
-        if (mGutsManager != null) {
-            mGutsManager.dump(fd, pw, args);
-        }
         if (mStatusBarView != null) {
             dumpBarTransitions(pw, "mStatusBarView", mStatusBarView.getBarTransitions());
         }
@@ -3472,7 +3406,7 @@
         if (onlyProvisioned && !isDeviceProvisioned()) return;
 
         final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
-                mContext, intent, mCurrentUserId);
+                mContext, intent, mLockscreenUserManager.getCurrentUserId());
         Runnable runnable = () -> {
             mAssistManager.hideAssist();
             intent.setFlags(
@@ -3564,7 +3498,7 @@
                 if (mRemoteInputController != null) {
                     mRemoteInputController.closeRemoteInputs();
                 }
-                if (isCurrentProfile(getSendingUserId())) {
+                if (mLockscreenUserManager.isCurrentProfile(getSendingUserId())) {
                     int flags = CommandQueue.FLAG_EXCLUDE_NONE;
                     String reason = intent.getStringExtra("reason");
                     if (reason != null && reason.equals(SYSTEM_DIALOG_REASON_RECENT_APPS)) {
@@ -3656,7 +3590,8 @@
         mScreenPinningRequest.onConfigurationChanged();
     }
 
-    public void userSwitched(int newUserId) {
+    @Override
+    public void onUserSwitched(int newUserId) {
         // Begin old BaseStatusBar.userSwitched
         setHeadsUpUser(newUserId);
         // End old BaseStatusBar.userSwitched
@@ -3673,6 +3608,11 @@
         setLockscreenUser(newUserId);
     }
 
+    @Override
+    public NotificationLockscreenUserManager getNotificationLockscreenUserManager() {
+        return mLockscreenUserManager;
+    }
+
     protected void setLockscreenUser(int newUserId) {
         mLockscreenWallpaper.setCurrentUser(newUserId);
         mScrimController.setCurrentUser(newUserId);
@@ -3919,7 +3859,8 @@
 
     public void destroy() {
         // Begin old BaseStatusBar.destroy().
-        mContext.unregisterReceiver(mBaseBroadcastReceiver);
+        mContext.unregisterReceiver(mBannerActionBroadcastReceiver);
+        mLockscreenUserManager.destroy();
         try {
             mNotificationListener.unregisterAsSystemService();
         } catch (RemoteException e) {
@@ -4325,18 +4266,21 @@
         mKeyguardMonitor.notifyKeyguardDoneFading();
     }
 
+    // TODO: Move this to NotificationLockscreenUserManager.
     private void updatePublicMode() {
         final boolean showingKeyguard = mStatusBarKeyguardViewManager.isShowing();
         final boolean devicePublic = showingKeyguard
-                && mStatusBarKeyguardViewManager.isSecure(mCurrentUserId);
+                && mStatusBarKeyguardViewManager.isSecure(
+                        mLockscreenUserManager.getCurrentUserId());
 
         // 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;
+        SparseArray<UserInfo> currentProfiles = mLockscreenUserManager.getCurrentProfiles();
+        for (int i = currentProfiles.size() - 1; i >= 0; i--) {
+            final int userId = currentProfiles.valueAt(i).id;
             boolean isProfilePublic = devicePublic;
-            if (!devicePublic && userId != mCurrentUserId) {
+            if (!devicePublic && userId != mLockscreenUserManager.getCurrentUserId()) {
                 // We can't rely on KeyguardManager#isDeviceLocked() for unified profile challenge
                 // due to a race condition where this code could be called before
                 // TrustManagerService updates its internal records, resulting in an incorrect
@@ -4346,7 +4290,7 @@
                     isProfilePublic = mKeyguardManager.isDeviceLocked(userId);
                 }
             }
-            setLockscreenPublicMode(isProfilePublic, userId);
+            mLockscreenUserManager.setLockscreenPublicMode(isProfilePublic, userId);
         }
     }
 
@@ -4404,7 +4348,7 @@
             mUiOffloadThread.submit(() -> {
                 try {
                     mOverlayManager.setEnabled("com.android.systemui.theme.dark",
-                            useDarkTheme, mCurrentUserId);
+                            useDarkTheme, mLockscreenUserManager.getCurrentUserId());
                 } catch (RemoteException e) {
                     Log.w(TAG, "Can't change theme", e);
                 }
@@ -4456,7 +4400,7 @@
     public void updateStackScrollerState(boolean goingToFullShade, boolean fromShadeLocked) {
         if (mStackScroller == null) return;
         boolean onKeyguard = mState == StatusBarState.KEYGUARD;
-        boolean publicMode = isAnyProfilePublicMode();
+        boolean publicMode = mLockscreenUserManager.isAnyProfilePublicMode();
         mStackScroller.setHideSensitive(publicMode, goingToFullShade);
         mStackScroller.setDimmed(onKeyguard, fromShadeLocked /* animate */);
         mStackScroller.setExpandingEnabled(!onKeyguard);
@@ -4750,7 +4694,7 @@
             return;
         }
 
-        int userId = mCurrentUserId;
+        int userId = mLockscreenUserManager.getCurrentUserId();
         ExpandableNotificationRow row = null;
         if (expandView instanceof ExpandableNotificationRow) {
             row = (ExpandableNotificationRow) expandView;
@@ -4762,9 +4706,11 @@
                 userId = row.getStatusBarNotification().getUserId();
             }
         }
-        boolean fullShadeNeedsBouncer = !userAllowsPrivateNotificationsInPublic(mCurrentUserId)
-                || !mShowLockscreenNotifications || mFalsingManager.shouldEnforceBouncer();
-        if (isLockscreenPublicMode(userId) && fullShadeNeedsBouncer) {
+        boolean fullShadeNeedsBouncer = !mLockscreenUserManager.
+                userAllowsPrivateNotificationsInPublic(mLockscreenUserManager.getCurrentUserId())
+                || !mLockscreenUserManager.shouldShowLockscreenNotifications()
+                || mFalsingManager.shouldEnforceBouncer();
+        if (mLockscreenUserManager.isLockscreenPublicMode(userId) && fullShadeNeedsBouncer) {
             mLeaveOpenOnKeyguardHide = true;
             showBouncerIfKeyguard();
             mDraggedDownRow = row;
@@ -4843,19 +4789,12 @@
         mPendingWorkRemoteInputView = clicked;
     }
 
-    private boolean isAnyProfilePublicMode() {
-        for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
-            if (isLockscreenPublicMode(mCurrentProfiles.valueAt(i).id)) {
-                return true;
-            }
-        }
-        return false;
-    }
-
-    protected void onWorkChallengeChanged() {
+    @Override
+    public void onWorkChallengeChanged() {
         updatePublicMode();
         updateNotifications();
-        if (mPendingWorkRemoteInputView != null && !isAnyProfilePublicMode()) {
+        if (mPendingWorkRemoteInputView != null
+                && !mLockscreenUserManager.isAnyProfilePublicMode()) {
             // Expand notification panel and the notification row, then click on remote input view
             final Runnable clickPendingViewRunnable = () -> {
                 final View pendingWorkRemoteInputView = mPendingWorkRemoteInputView;
@@ -5135,12 +5074,14 @@
     }
 
     boolean isCameraAllowedByAdmin() {
-        if (mDevicePolicyManager.getCameraDisabled(null, mCurrentUserId)) {
+        if (mDevicePolicyManager.getCameraDisabled(null,
+                mLockscreenUserManager.getCurrentUserId())) {
             return false;
         } else if (mStatusBarKeyguardViewManager == null ||
                 (isKeyguardShowing() && isKeyguardSecure())) {
             // Check if the admin has disabled the camera specifically for the keyguard
-            return (mDevicePolicyManager.getKeyguardDisabledFeatures(null, mCurrentUserId)
+            return (mDevicePolicyManager.
+                    getKeyguardDisabledFeatures(null, mLockscreenUserManager.getCurrentUserId())
                     & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_CAMERA) == 0;
         }
 
@@ -5429,8 +5370,6 @@
     // handling reordering
     protected final VisualStabilityManager mVisualStabilityManager = new VisualStabilityManager();
 
-    protected int mCurrentUserId = 0;
-    final protected SparseArray<UserInfo> mCurrentProfiles = new SparseArray<>();
 
     protected AccessibilityManager mAccessibilityManager;
 
@@ -5457,11 +5396,6 @@
     protected PowerManager mPowerManager;
     protected StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
 
-    // public mode, private notifications, etc
-    private final SparseBooleanArray mLockscreenPublicMode = new SparseBooleanArray();
-    private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
-    private final SparseBooleanArray mUsersAllowingNotifications = new SparseBooleanArray();
-
     private UserManager mUserManager;
 
     protected KeyguardManager mKeyguardManager;
@@ -5479,8 +5413,6 @@
 
     protected RecentsComponent mRecents;
 
-    protected int mZenMode;
-
     protected NotificationShelf mNotificationShelf;
     protected DismissView mDismissView;
     protected EmptyShadeView mEmptyShadeView;
@@ -5519,30 +5451,6 @@
         }
     };
 
-    protected final ContentObserver mSettingsObserver = new ContentObserver(mHandler) {
-        @Override
-        public void onChange(boolean selfChange) {
-            final int mode = Settings.Global.getInt(mContext.getContentResolver(),
-                    Settings.Global.ZEN_MODE, Settings.Global.ZEN_MODE_OFF);
-            setZenMode(mode);
-
-            updateLockscreenNotificationSetting();
-        }
-    };
-
-    private final ContentObserver mLockscreenSettingsObserver = new ContentObserver(mHandler) {
-        @Override
-        public void onChange(boolean selfChange) {
-            // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS or
-            // LOCK_SCREEN_SHOW_NOTIFICATIONS, so we just dump our cache ...
-            mUsersAllowingPrivateNotifications.clear();
-            mUsersAllowingNotifications.clear();
-            // ... and refresh all the notifications
-            updateLockscreenNotificationSetting();
-            updateNotifications();
-        }
-    };
-
     private final RemoteViews.OnClickHandler mOnClickHandler = new RemoteViews.OnClickHandler() {
 
         @Override
@@ -5569,7 +5477,8 @@
             final boolean isActivity = pendingIntent.isActivity();
             if (isActivity) {
                 final boolean afterKeyguardGone = PreviewInflater.wouldLaunchResolverActivity(
-                        mContext, pendingIntent.getIntent(), mCurrentUserId);
+                        mContext, pendingIntent.getIntent(),
+                        mLockscreenUserManager.getCurrentUserId());
                 dismissKeyguardThenExecute(() -> {
                     try {
                         ActivityManager.getService().resumeAppSwitches();
@@ -5690,9 +5599,9 @@
 
             row.setUserExpanded(true);
 
-            if (!mAllowLockscreenRemoteInput) {
+            if (!mLockscreenUserManager.shouldAllowLockscreenRemoteInput()) {
                 final int userId = pendingIntent.getCreatorUserHandle().getIdentifier();
-                if (isLockscreenPublicMode(userId)) {
+                if (mLockscreenUserManager.isLockscreenPublicMode(userId)) {
                     onLockedRemoteInput(row, view);
                     return true;
                 }
@@ -5748,51 +5657,15 @@
         }
     };
 
-    private final BroadcastReceiver mBaseBroadcastReceiver = new BroadcastReceiver() {
+    private final BroadcastReceiver mBannerActionBroadcastReceiver = new BroadcastReceiver() {
         @Override
         public void onReceive(Context context, Intent intent) {
             String action = intent.getAction();
-            if (Intent.ACTION_USER_SWITCHED.equals(action)) {
-                mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
-                updateCurrentProfilesCache();
-                Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
-
-                updateLockscreenNotificationSetting();
-
-                userSwitched(mCurrentUserId);
-            } else if (Intent.ACTION_USER_ADDED.equals(action)) {
-                updateCurrentProfilesCache();
-            } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
-                // Start the overview connection to the launcher service
-                Dependency.get(OverviewProxyService.class).startConnectionToCurrentUser();
-            } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
-                List<ActivityManager.RecentTaskInfo> recentTask = null;
-                try {
-                    recentTask = ActivityManager.getService().getRecentTasks(1,
-                            ActivityManager.RECENT_WITH_EXCLUDED,
-                            mCurrentUserId).getList();
-                } catch (RemoteException e) {
-                    // Abandon hope activity manager not running.
-                }
-                if (recentTask != null && recentTask.size() > 0) {
-                    UserInfo user = mUserManager.getUserInfo(recentTask.get(0).userId);
-                    if (user != null && user.isManagedProfile()) {
-                        Toast toast = Toast.makeText(mContext,
-                                R.string.managed_profile_foreground_toast,
-                                Toast.LENGTH_SHORT);
-                        TextView text = toast.getView().findViewById(android.R.id.message);
-                        text.setCompoundDrawablesRelativeWithIntrinsicBounds(
-                                R.drawable.stat_sys_managed_profile_status, 0, 0, 0);
-                        int paddingPx = mContext.getResources().getDimensionPixelSize(
-                                R.dimen.managed_profile_toast_padding);
-                        text.setCompoundDrawablePadding(paddingPx);
-                        toast.show();
-                    }
-                }
-            } else if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) {
+            if (BANNER_ACTION_CANCEL.equals(action) || BANNER_ACTION_SETUP.equals(action)) {
                 NotificationManager noMan = (NotificationManager)
                         mContext.getSystemService(Context.NOTIFICATION_SERVICE);
-                noMan.cancel(SystemMessage.NOTE_HIDDEN_NOTIFICATIONS);
+                noMan.cancel(com.android.internal.messages.nano.SystemMessageProto.SystemMessage.
+                        NOTE_HIDDEN_NOTIFICATIONS);
 
                 Settings.Secure.putInt(mContext.getContentResolver(),
                         Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 0);
@@ -5804,42 +5677,6 @@
 
                     );
                 }
-            } 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) {
-                    try {
-                        mContext.startIntentSender(intentSender, null, 0, 0, 0);
-                    } catch (IntentSender.SendIntentException e) {
-                        /* ignore */
-                    }
-                }
-                if (notificationKey != null) {
-                    try {
-                        mBarService.onNotificationClick(notificationKey);
-                    } catch (RemoteException e) {
-                        /* ignore */
-                    }
-                }
-            }
-        }
-    };
-
-    private final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() {
-        @Override
-        public void onReceive(Context context, Intent intent) {
-            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();
-                }
             }
         }
     };
@@ -5929,17 +5766,6 @@
 
     };
 
-    private void updateCurrentProfilesCache() {
-        synchronized (mCurrentProfiles) {
-            mCurrentProfiles.clear();
-            if (mUserManager != null) {
-                for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) {
-                    mCurrentProfiles.put(user.id, user);
-                }
-            }
-        }
-    }
-
     protected void notifyUserAboutHiddenNotifications() {
         if (0 != Settings.Secure.getInt(mContext.getContentResolver(),
                 Settings.Secure.SHOW_NOTE_ABOUT_NOTIFICATION_HIDING, 1)) {
@@ -5994,9 +5820,9 @@
         final int notificationUserId = n.getUserId();
         if (DEBUG && MULTIUSER_DEBUG) {
             Log.v(TAG, String.format("%s: current userid: %d, notification userid: %d", n,
-                    mCurrentUserId, notificationUserId));
+                    mLockscreenUserManager.getCurrentUserId(), notificationUserId));
         }
-        return isCurrentProfile(notificationUserId);
+        return mLockscreenUserManager.isCurrentProfile(notificationUserId);
     }
 
     protected void setNotificationShown(StatusBarNotification n) {
@@ -6011,12 +5837,6 @@
         }
     }
 
-    protected boolean isCurrentProfile(int userId) {
-        synchronized (mCurrentProfiles) {
-            return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null;
-        }
-    }
-
     @Override
     public NotificationGroupManager getGroupManager() {
         return mGroupManager;
@@ -6103,89 +5923,14 @@
         KeyboardShortcuts.dismiss();
     }
 
-    /**
-     * Save the current "public" (locked and secure) state of the lockscreen.
-     */
-    public void setLockscreenPublicMode(boolean publicMode, int userId) {
-        mLockscreenPublicMode.put(userId, publicMode);
-    }
-
-    public boolean isLockscreenPublicMode(int userId) {
-        if (userId == UserHandle.USER_ALL) {
-            return mLockscreenPublicMode.get(mCurrentUserId, false);
-        }
-        return mLockscreenPublicMode.get(userId, false);
-    }
-
-    /**
-     * Has the given user chosen to allow notifications to be shown even when the lockscreen is in
-     * "public" (secure & locked) mode?
-     */
-    public boolean userAllowsNotificationsInPublic(int userHandle) {
-        if (userHandle == UserHandle.USER_ALL) {
-            return true;
-        }
-
-        if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
-            final boolean allowed = 0 != Settings.Secure.getIntForUser(
-                    mContext.getContentResolver(),
-                    Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle);
-            mUsersAllowingNotifications.append(userHandle, allowed);
-            return allowed;
-        }
-
-        return mUsersAllowingNotifications.get(userHandle);
-    }
-
-    /**
-     * Has the given user chosen to allow their private (full) notifications to be shown even
-     * when the lockscreen is in "public" (secure & locked) mode?
-     */
-    public boolean userAllowsPrivateNotificationsInPublic(int userHandle) {
-        if (userHandle == UserHandle.USER_ALL) {
-            return true;
-        }
-
-        if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
-            final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
-                    mContext.getContentResolver(),
-                    Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
-            final boolean allowedByDpm = adminAllowsUnredactedNotifications(userHandle);
-            final boolean allowed = allowedByUser && allowedByDpm;
-            mUsersAllowingPrivateNotifications.append(userHandle, allowed);
-            return allowed;
-        }
-
-        return mUsersAllowingPrivateNotifications.get(userHandle);
-    }
-
-    private boolean adminAllowsUnredactedNotifications(int userHandle) {
-        if (userHandle == UserHandle.USER_ALL) {
-            return true;
-        }
-        final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */,
-                    userHandle);
-        return (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS) == 0;
-    }
-
-    /**
-     * Returns true if we're on a secure lockscreen and the user wants to hide notification data.
-     * If so, notifications should be hidden.
-     */
     @Override  // NotificationData.Environment
     public boolean shouldHideNotifications(int userId) {
-        return isLockscreenPublicMode(userId) && !userAllowsNotificationsInPublic(userId)
-                || (userId != mCurrentUserId && shouldHideNotifications(mCurrentUserId));
+        return mLockscreenUserManager.shouldHideNotifications(userId);
     }
 
-    /**
-     * Returns true if we're on a secure lockscreen and the user wants to hide notifications via
-     * package-specific override.
-     */
     @Override // NotificationDate.Environment
     public boolean shouldHideNotifications(String key) {
-        return isLockscreenPublicMode(mCurrentUserId)
-                && mNotificationData.getVisibilityOverride(key) == Notification.VISIBILITY_SECRET;
+        return mLockscreenUserManager.shouldHideNotifications(key);
     }
 
     /**
@@ -6193,7 +5938,7 @@
      */
     @Override  // NotificationData.Environment
     public boolean isSecurelyLocked(int userId) {
-        return isLockscreenPublicMode(userId);
+        return mLockscreenUserManager.isLockscreenPublicMode(userId);
     }
 
     /**
@@ -6274,7 +6019,7 @@
 
     private void updateNotification(Entry entry, PackageManager pmUser,
             StatusBarNotification sbn, ExpandableNotificationRow row) {
-        row.setNeedsRedaction(needsRedaction(entry));
+        row.setNeedsRedaction(mLockscreenUserManager.needsRedaction(entry));
         boolean isLowPriority = mNotificationData.isAmbient(sbn.getKey());
         boolean isUpdate = mNotificationData.get(entry.key) != null;
         boolean wasLowPriority = row.isLowPriority();
@@ -6356,7 +6101,7 @@
 
         final boolean afterKeyguardGone = intent.isActivity()
                 && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
-                mCurrentUserId);
+                mLockscreenUserManager.getCurrentUserId());
         dismissKeyguardThenExecute(() -> {
             new Thread(() -> {
                 try {
@@ -6431,7 +6176,7 @@
 
             final boolean afterKeyguardGone = intent.isActivity()
                     && PreviewInflater.wouldLaunchResolverActivity(mContext, intent.getIntent(),
-                            mCurrentUserId);
+                            mLockscreenUserManager.getCurrentUserId());
             dismissKeyguardThenExecute(() -> {
                 if (mHeadsUpManager != null && mHeadsUpManager.isHeadsUp(notificationKey)) {
                     // Release the HUN notification to the shade.
@@ -6685,9 +6430,11 @@
             int userId = entry.notification.getUserId();
             boolean suppressedSummary = mGroupManager.isSummaryOfSuppressedGroup(
                     entry.notification) && !entry.row.isRemoved();
-            boolean showOnKeyguard = shouldShowOnKeyguard(entry.notification);
+            boolean showOnKeyguard = mLockscreenUserManager.shouldShowOnKeyguard(entry
+                    .notification);
             if (suppressedSummary
-                    || (isLockscreenPublicMode(userId) && !mShowLockscreenNotifications)
+                    || (mLockscreenUserManager.isLockscreenPublicMode(userId)
+                            && !mLockscreenUserManager.shouldShowLockscreenNotifications())
                     || (onKeyguard && !showOnKeyguard)) {
                 entry.row.setVisibility(View.GONE);
             } else {
@@ -6737,47 +6484,6 @@
         mScrimController.setNotificationCount(mStackScroller.getNotGoneChildCount());
     }
 
-    public boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
-        return mShowLockscreenNotifications
-                && ((mDisabled2 & DISABLE2_NOTIFICATION_SHADE) == 0)
-                && !mNotificationData.isAmbient(sbn.getKey());
-    }
-
-    // extended in StatusBar
-    protected void setShowLockscreenNotifications(boolean show) {
-        mShowLockscreenNotifications = show;
-    }
-
-    protected void setLockScreenAllowRemoteInput(boolean allowLockscreenRemoteInput) {
-        mAllowLockscreenRemoteInput = allowLockscreenRemoteInput;
-    }
-
-    protected void updateLockscreenNotificationSetting() {
-        final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(),
-                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
-                1,
-                mCurrentUserId) != 0;
-        final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(
-                null /* admin */, mCurrentUserId);
-        final boolean allowedByDpm = (dpmFlags
-                & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
-
-        setShowLockscreenNotifications(show && allowedByDpm);
-
-        if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
-            final boolean remoteInput = Settings.Secure.getIntForUser(mContext.getContentResolver(),
-                    Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT,
-                    0,
-                    mCurrentUserId) != 0;
-            final boolean remoteInputDpm =
-                    (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT) == 0;
-
-            setLockScreenAllowRemoteInput(remoteInput && remoteInputDpm);
-        } else {
-            setLockScreenAllowRemoteInput(false);
-        }
-    }
-
     public void updateNotification(StatusBarNotification notification, RankingMap ranking)
             throws InflationException {
         if (DEBUG) Log.d(TAG, "updateNotification(" + notification + ")");
@@ -6891,9 +6597,11 @@
         }
 
         if (mIsOccluded && !isDozing()) {
-            boolean devicePublic = isLockscreenPublicMode(mCurrentUserId);
-            boolean userPublic = devicePublic || isLockscreenPublicMode(sbn.getUserId());
-            boolean needsRedaction = needsRedaction(entry);
+            boolean devicePublic = mLockscreenUserManager.
+                    isLockscreenPublicMode(mLockscreenUserManager.getCurrentUserId());
+            boolean userPublic = devicePublic
+                    || mLockscreenUserManager.isLockscreenPublicMode(sbn.getUserId());
+            boolean needsRedaction = mLockscreenUserManager.needsRedaction(entry);
             if (userPublic && needsRedaction) {
                 return false;
             }
@@ -7009,13 +6717,13 @@
     }
 
     @Override
-    public int getCurrentUserId() {
-        return mCurrentUserId;
+    public NotificationData getNotificationData() {
+        return mNotificationData;
     }
 
     @Override
-    public NotificationData getNotificationData() {
-        return mNotificationData;
+    public Handler getHandler() {
+        return mHandler;
     }
 
     @Override
@@ -7023,12 +6731,12 @@
         return mLatestRankingMap;
     }
 
-    final NotificationInfo.CheckSaveListener mCheckSaveListener =
+    private final NotificationInfo.CheckSaveListener mCheckSaveListener =
             (Runnable saveImportance, StatusBarNotification sbn) -> {
                 // If the user has security enabled, show challenge if the setting is changed.
-                if (isLockscreenPublicMode(sbn.getUser().getIdentifier()) && (
-                        mState == StatusBarState.KEYGUARD
-                                || mState == StatusBarState.SHADE_LOCKED)) {
+                if (mLockscreenUserManager.isLockscreenPublicMode(sbn.getUser().getIdentifier())
+                        && (mState == StatusBarState.KEYGUARD ||
+                                mState == StatusBarState.SHADE_LOCKED)) {
                     onLockedNotificationImportanceChange(() -> {
                         saveImportance.run();
                         return true;
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
new file mode 100644
index 0000000..4045995
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/NotificationLockscreenUserManagerTest.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.statusbar;
+
+import static android.content.Intent.ACTION_DEVICE_LOCKED_CHANGED;
+import static android.content.Intent.ACTION_USER_SWITCHED;
+
+import static junit.framework.Assert.assertFalse;
+import static junit.framework.Assert.assertTrue;
+
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.app.ActivityManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.UserInfo;
+import android.database.ContentObserver;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.support.test.filters.SmallTest;
+import android.testing.AndroidTestingRunner;
+import android.testing.TestableLooper;
+
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.policy.DeviceProvisionedController;
+
+import com.google.android.collect.Lists;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+@SmallTest
+@RunWith(AndroidTestingRunner.class)
+@TestableLooper.RunWithLooper
+public class NotificationLockscreenUserManagerTest extends SysuiTestCase {
+    private NotificationPresenter mPresenter;
+    private TestNotificationLockscreenUserManager mLockscreenUserManager;
+    private DeviceProvisionedController mDeviceProvisionedController;
+    private int mCurrentUserId;
+    private Handler mHandler;
+    private UserManager mUserManager;
+
+    @Before
+    public void setUp() {
+        mUserManager = mock(UserManager.class);
+        mContext.addMockSystemService(UserManager.class, mUserManager);
+        mHandler = new Handler(Looper.getMainLooper());
+        mDependency.injectMockDependency(DeviceProvisionedController.class);
+        mDeviceProvisionedController = mDependency.get(DeviceProvisionedController.class);
+        mLockscreenUserManager = new TestNotificationLockscreenUserManager(mContext);
+        mDependency.injectTestDependency(NotificationLockscreenUserManager.class,
+                mLockscreenUserManager);
+
+        when(mUserManager.getProfiles(mCurrentUserId)).thenReturn(Lists.newArrayList(
+                new UserInfo(mCurrentUserId, "", 0), new UserInfo(mCurrentUserId + 1, "", 0)));
+
+        mPresenter = mock(NotificationPresenter.class);
+        when(mPresenter.getHandler()).thenReturn(mHandler);
+        mLockscreenUserManager.setUpWithPresenter(mPresenter);
+        mCurrentUserId = ActivityManager.getCurrentUser();
+    }
+
+    @Test
+    public void testLockScreenShowNotificationsChangeUpdatesNotifications() {
+        mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+        verify(mPresenter, times(1)).updateNotifications();
+    }
+
+    @Test
+    public void testLockScreenShowNotificationsFalse() {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0);
+        mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+        assertFalse(mLockscreenUserManager.shouldShowLockscreenNotifications());
+    }
+
+    @Test
+    public void testLockScreenShowNotificationsTrue() {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 1);
+        mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+        assertTrue(mLockscreenUserManager.shouldShowLockscreenNotifications());
+    }
+
+    @Test
+    public void testLockScreenAllowPrivateNotificationsTrue() {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 1);
+        mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+        assertTrue(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUserId));
+    }
+
+    @Test
+    public void testLockScreenAllowPrivateNotificationsFalse() {
+        Settings.Secure.putInt(mContext.getContentResolver(),
+                Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0);
+        mLockscreenUserManager.getLockscreenSettingsObserverForTest().onChange(false);
+        assertFalse(mLockscreenUserManager.userAllowsPrivateNotificationsInPublic(mCurrentUserId));
+    }
+
+    @Test
+    public void testSettingsObserverUpdatesNotifications() {
+        when(mDeviceProvisionedController.isDeviceProvisioned()).thenReturn(true);
+        mLockscreenUserManager.getSettingsObserverForTest().onChange(false);
+        verify(mPresenter, times(1)).updateNotifications();
+    }
+
+    @Test
+    public void testActionDeviceLockedChangedWithDifferentUserIdCallsOnWorkChallengeChanged() {
+        Intent intent = new Intent()
+                .setAction(ACTION_DEVICE_LOCKED_CHANGED)
+                .putExtra(Intent.EXTRA_USER_HANDLE, mCurrentUserId + 1);
+        mLockscreenUserManager.getAllUsersReceiverForTest().onReceive(mContext, intent);
+        verify(mPresenter, times(1)).onWorkChallengeChanged();
+    }
+
+    @Test
+    public void testActionUserSwitchedCallsOnUserSwitched() {
+        Intent intent = new Intent()
+                .setAction(ACTION_USER_SWITCHED)
+                .putExtra(Intent.EXTRA_USER_HANDLE, mCurrentUserId + 1);
+        mLockscreenUserManager.getBaseBroadcastReceiverForTest().onReceive(mContext, intent);
+        verify(mPresenter, times(1)).onUserSwitched(mCurrentUserId + 1);
+    }
+
+    @Test
+    public void testIsLockscreenPublicMode() {
+        assertFalse(mLockscreenUserManager.isLockscreenPublicMode(mCurrentUserId));
+        mLockscreenUserManager.setLockscreenPublicMode(true, mCurrentUserId);
+        assertTrue(mLockscreenUserManager.isLockscreenPublicMode(mCurrentUserId));
+    }
+
+    private class TestNotificationLockscreenUserManager extends NotificationLockscreenUserManager {
+        public TestNotificationLockscreenUserManager(Context context) {
+            super(context);
+        }
+
+        public BroadcastReceiver getAllUsersReceiverForTest() {
+            return mAllUsersReceiver;
+        }
+
+        public BroadcastReceiver getBaseBroadcastReceiverForTest() {
+            return mBaseBroadcastReceiver;
+        }
+
+        public ContentObserver getLockscreenSettingsObserverForTest() {
+            return mLockscreenSettingsObserver;
+        }
+
+        public ContentObserver getSettingsObserverForTest() {
+            return mSettingsObserver;
+        }
+    }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
index 899e873..713f7843 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarTest.java
@@ -54,6 +54,7 @@
 import android.testing.TestableLooper.MessageHandler;
 import android.testing.TestableLooper.RunWithLooper;
 import android.util.DisplayMetrics;
+import android.util.SparseArray;
 import android.view.ViewGroup.LayoutParams;
 
 import com.android.internal.logging.MetricsLogger;
@@ -71,6 +72,7 @@
 import com.android.systemui.statusbar.KeyguardIndicationController;
 import com.android.systemui.statusbar.NotificationData;
 import com.android.systemui.statusbar.NotificationData.Entry;
+import com.android.systemui.statusbar.NotificationLockscreenUserManager;
 import com.android.systemui.statusbar.StatusBarState;
 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
 import com.android.systemui.statusbar.policy.HeadsUpManager;
@@ -524,6 +526,9 @@
         mStatusBar.mState = StatusBarState.KEYGUARD;
         mStatusBar.mDozeScrimController = mock(DozeScrimController.class);
         mStatusBar.mNotificationIconAreaController = mock(NotificationIconAreaController.class);
+        mStatusBar.mLockscreenUserManager = mock(NotificationLockscreenUserManager.class);
+        when(mStatusBar.mLockscreenUserManager.getCurrentProfiles()).thenReturn(
+                new SparseArray<>());
         mStatusBar.updateKeyguardState(false, false);
     }
 
@@ -568,4 +573,4 @@
             mUserSetup = userSetup;
         }
     }
-}
\ No newline at end of file
+}