Home | History | Annotate | Download | only in statusbar
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License
     15  */
     16 package com.android.systemui.statusbar;
     17 
     18 import android.app.ActivityManager;
     19 import android.app.Notification;
     20 import android.app.admin.DevicePolicyManager;
     21 import android.content.BroadcastReceiver;
     22 import android.content.Context;
     23 import android.content.Intent;
     24 import android.content.IntentFilter;
     25 import android.content.IntentSender;
     26 import android.content.pm.UserInfo;
     27 import android.database.ContentObserver;
     28 import android.os.RemoteException;
     29 import android.os.ServiceManager;
     30 import android.os.UserHandle;
     31 import android.os.UserManager;
     32 import android.provider.Settings;
     33 import android.service.notification.StatusBarNotification;
     34 import android.util.Log;
     35 import android.util.SparseArray;
     36 import android.util.SparseBooleanArray;
     37 import android.widget.TextView;
     38 import android.widget.Toast;
     39 
     40 import com.android.internal.statusbar.IStatusBarService;
     41 import com.android.internal.statusbar.NotificationVisibility;
     42 import com.android.keyguard.KeyguardUpdateMonitor;
     43 import com.android.systemui.Dependency;
     44 import com.android.systemui.Dumpable;
     45 import com.android.systemui.OverviewProxyService;
     46 import com.android.systemui.R;
     47 import com.android.systemui.statusbar.policy.DeviceProvisionedController;
     48 
     49 import java.io.FileDescriptor;
     50 import java.io.PrintWriter;
     51 
     52 /**
     53  * Handles keeping track of the current user, profiles, and various things related to hiding
     54  * contents, redacting notifications, and the lockscreen.
     55  */
     56 public class NotificationLockscreenUserManager implements Dumpable {
     57     private static final String TAG = "LockscreenUserManager";
     58     private static final boolean ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT = false;
     59     public static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
     60     public static final String NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION
     61             = "com.android.systemui.statusbar.work_challenge_unlocked_notification_action";
     62 
     63     private final DevicePolicyManager mDevicePolicyManager;
     64     private final SparseBooleanArray mLockscreenPublicMode = new SparseBooleanArray();
     65     private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
     66     private final SparseBooleanArray mUsersAllowingNotifications = new SparseBooleanArray();
     67     private final DeviceProvisionedController mDeviceProvisionedController =
     68             Dependency.get(DeviceProvisionedController.class);
     69     private final UserManager mUserManager;
     70     private final IStatusBarService mBarService;
     71 
     72     private boolean mShowLockscreenNotifications;
     73     private boolean mAllowLockscreenRemoteInput;
     74 
     75     protected final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() {
     76         @Override
     77         public void onReceive(Context context, Intent intent) {
     78             final String action = intent.getAction();
     79             final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
     80 
     81             if (DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action) &&
     82                     isCurrentProfile(getSendingUserId())) {
     83                 mUsersAllowingPrivateNotifications.clear();
     84                 updateLockscreenNotificationSetting();
     85                 mEntryManager.updateNotifications();
     86             } else if (Intent.ACTION_DEVICE_LOCKED_CHANGED.equals(action)) {
     87                 if (userId != mCurrentUserId && isCurrentProfile(userId)) {
     88                     mPresenter.onWorkChallengeChanged();
     89                 }
     90             }
     91         }
     92     };
     93 
     94     protected final BroadcastReceiver mBaseBroadcastReceiver = new BroadcastReceiver() {
     95         @Override
     96         public void onReceive(Context context, Intent intent) {
     97             String action = intent.getAction();
     98             if (Intent.ACTION_USER_SWITCHED.equals(action)) {
     99                 mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
    100                 updateCurrentProfilesCache();
    101                 Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
    102 
    103                 updateLockscreenNotificationSetting();
    104 
    105                 mPresenter.onUserSwitched(mCurrentUserId);
    106             } else if (Intent.ACTION_USER_ADDED.equals(action)) {
    107                 updateCurrentProfilesCache();
    108             } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
    109                 // Start the overview connection to the launcher service
    110                 Dependency.get(OverviewProxyService.class).startConnectionToCurrentUser();
    111             } else if (Intent.ACTION_USER_PRESENT.equals(action)) {
    112                 try {
    113                     final int lastResumedActivityUserId =
    114                             ActivityManager.getService().getLastResumedActivityUserId();
    115                     if (mUserManager.isManagedProfile(lastResumedActivityUserId)) {
    116                         showForegroundManagedProfileActivityToast();
    117                     }
    118                 } catch (RemoteException e) {
    119                     // Abandon hope activity manager not running.
    120                 }
    121             } else if (NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION.equals(action)) {
    122                 final IntentSender intentSender = intent.getParcelableExtra(Intent.EXTRA_INTENT);
    123                 final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX);
    124                 if (intentSender != null) {
    125                     try {
    126                         mContext.startIntentSender(intentSender, null, 0, 0, 0);
    127                     } catch (IntentSender.SendIntentException e) {
    128                         /* ignore */
    129                     }
    130                 }
    131                 if (notificationKey != null) {
    132                     final int count =
    133                             mEntryManager.getNotificationData().getActiveNotifications().size();
    134                     final int rank = mEntryManager.getNotificationData().getRank(notificationKey);
    135                     final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey,
    136                             rank, count, true);
    137                     try {
    138                         mBarService.onNotificationClick(notificationKey, nv);
    139                     } catch (RemoteException e) {
    140                         /* ignore */
    141                     }
    142                 }
    143             }
    144         }
    145     };
    146 
    147     protected final Context mContext;
    148     protected final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<>();
    149 
    150     protected int mCurrentUserId = 0;
    151     protected NotificationPresenter mPresenter;
    152     protected NotificationEntryManager mEntryManager;
    153     protected ContentObserver mLockscreenSettingsObserver;
    154     protected ContentObserver mSettingsObserver;
    155 
    156     public NotificationLockscreenUserManager(Context context) {
    157         mContext = context;
    158         mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
    159                 Context.DEVICE_POLICY_SERVICE);
    160         mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
    161         mCurrentUserId = ActivityManager.getCurrentUser();
    162         mBarService = IStatusBarService.Stub.asInterface(
    163                 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
    164     }
    165 
    166     public void setUpWithPresenter(NotificationPresenter presenter,
    167             NotificationEntryManager entryManager) {
    168         mPresenter = presenter;
    169         mEntryManager = entryManager;
    170 
    171         mLockscreenSettingsObserver = new ContentObserver(mPresenter.getHandler()) {
    172             @Override
    173             public void onChange(boolean selfChange) {
    174                 // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS or
    175                 // LOCK_SCREEN_SHOW_NOTIFICATIONS, so we just dump our cache ...
    176                 mUsersAllowingPrivateNotifications.clear();
    177                 mUsersAllowingNotifications.clear();
    178                 // ... and refresh all the notifications
    179                 updateLockscreenNotificationSetting();
    180                 mEntryManager.updateNotifications();
    181             }
    182         };
    183 
    184         mSettingsObserver = new ContentObserver(mPresenter.getHandler()) {
    185             @Override
    186             public void onChange(boolean selfChange) {
    187                 updateLockscreenNotificationSetting();
    188                 if (mDeviceProvisionedController.isDeviceProvisioned()) {
    189                     mEntryManager.updateNotifications();
    190                 }
    191             }
    192         };
    193 
    194         mContext.getContentResolver().registerContentObserver(
    195                 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
    196                 mLockscreenSettingsObserver,
    197                 UserHandle.USER_ALL);
    198 
    199         mContext.getContentResolver().registerContentObserver(
    200                 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
    201                 true,
    202                 mLockscreenSettingsObserver,
    203                 UserHandle.USER_ALL);
    204 
    205         mContext.getContentResolver().registerContentObserver(
    206                 Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
    207                 mSettingsObserver);
    208 
    209         if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
    210             mContext.getContentResolver().registerContentObserver(
    211                     Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT),
    212                     false,
    213                     mSettingsObserver,
    214                     UserHandle.USER_ALL);
    215         }
    216 
    217         IntentFilter allUsersFilter = new IntentFilter();
    218         allUsersFilter.addAction(
    219                 DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED);
    220         allUsersFilter.addAction(Intent.ACTION_DEVICE_LOCKED_CHANGED);
    221         mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL, allUsersFilter,
    222                 null, null);
    223 
    224         IntentFilter filter = new IntentFilter();
    225         filter.addAction(Intent.ACTION_USER_SWITCHED);
    226         filter.addAction(Intent.ACTION_USER_ADDED);
    227         filter.addAction(Intent.ACTION_USER_PRESENT);
    228         filter.addAction(Intent.ACTION_USER_UNLOCKED);
    229         mContext.registerReceiver(mBaseBroadcastReceiver, filter);
    230 
    231         IntentFilter internalFilter = new IntentFilter();
    232         internalFilter.addAction(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
    233         mContext.registerReceiver(mBaseBroadcastReceiver, internalFilter, PERMISSION_SELF, null);
    234 
    235         updateCurrentProfilesCache();
    236 
    237         mSettingsObserver.onChange(false);  // set up
    238     }
    239 
    240     private void showForegroundManagedProfileActivityToast() {
    241         Toast toast = Toast.makeText(mContext,
    242                 R.string.managed_profile_foreground_toast,
    243                 Toast.LENGTH_SHORT);
    244         TextView text = toast.getView().findViewById(android.R.id.message);
    245         text.setCompoundDrawablesRelativeWithIntrinsicBounds(
    246                 R.drawable.stat_sys_managed_profile_status, 0, 0, 0);
    247         int paddingPx = mContext.getResources().getDimensionPixelSize(
    248                 R.dimen.managed_profile_toast_padding);
    249         text.setCompoundDrawablePadding(paddingPx);
    250         toast.show();
    251     }
    252 
    253     public boolean shouldShowLockscreenNotifications() {
    254         return mShowLockscreenNotifications;
    255     }
    256 
    257     public boolean shouldAllowLockscreenRemoteInput() {
    258         return mAllowLockscreenRemoteInput;
    259     }
    260 
    261     public boolean isCurrentProfile(int userId) {
    262         synchronized (mCurrentProfiles) {
    263             return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null;
    264         }
    265     }
    266 
    267     /**
    268      * Returns true if notifications are temporarily disabled for this user for security reasons,
    269      * regardless of the normal settings for that user.
    270      */
    271     private boolean shouldTemporarilyHideNotifications(int userId) {
    272         if (userId == UserHandle.USER_ALL) {
    273             userId = mCurrentUserId;
    274         }
    275         return KeyguardUpdateMonitor.getInstance(mContext).isUserInLockdown(userId);
    276     }
    277 
    278     /**
    279      * Returns true if we're on a secure lockscreen and the user wants to hide notification data.
    280      * If so, notifications should be hidden.
    281      */
    282     public boolean shouldHideNotifications(int userId) {
    283         return isLockscreenPublicMode(userId) && !userAllowsNotificationsInPublic(userId)
    284                 || (userId != mCurrentUserId && shouldHideNotifications(mCurrentUserId))
    285                 || shouldTemporarilyHideNotifications(userId);
    286     }
    287 
    288     /**
    289      * Returns true if we're on a secure lockscreen and the user wants to hide notifications via
    290      * package-specific override.
    291      */
    292     public boolean shouldHideNotifications(String key) {
    293         if (mEntryManager == null) {
    294             Log.wtf(TAG, "mEntryManager was null!", new Throwable());
    295             return true;
    296         }
    297         return isLockscreenPublicMode(mCurrentUserId)
    298                 && mEntryManager.getNotificationData().getVisibilityOverride(key) ==
    299                         Notification.VISIBILITY_SECRET;
    300     }
    301 
    302     public boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
    303         if (mEntryManager == null) {
    304             Log.wtf(TAG, "mEntryManager was null!", new Throwable());
    305             return false;
    306         }
    307         return mShowLockscreenNotifications
    308                 && !mEntryManager.getNotificationData().isAmbient(sbn.getKey());
    309     }
    310 
    311     private void setShowLockscreenNotifications(boolean show) {
    312         mShowLockscreenNotifications = show;
    313     }
    314 
    315     private void setLockscreenAllowRemoteInput(boolean allowLockscreenRemoteInput) {
    316         mAllowLockscreenRemoteInput = allowLockscreenRemoteInput;
    317     }
    318 
    319     protected void updateLockscreenNotificationSetting() {
    320         final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(),
    321                 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
    322                 1,
    323                 mCurrentUserId) != 0;
    324         final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(
    325                 null /* admin */, mCurrentUserId);
    326         final boolean allowedByDpm = (dpmFlags
    327                 & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
    328 
    329         setShowLockscreenNotifications(show && allowedByDpm);
    330 
    331         if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
    332             final boolean remoteInput = Settings.Secure.getIntForUser(mContext.getContentResolver(),
    333                     Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT,
    334                     0,
    335                     mCurrentUserId) != 0;
    336             final boolean remoteInputDpm =
    337                     (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT) == 0;
    338 
    339             setLockscreenAllowRemoteInput(remoteInput && remoteInputDpm);
    340         } else {
    341             setLockscreenAllowRemoteInput(false);
    342         }
    343     }
    344 
    345     /**
    346      * Has the given user chosen to allow their private (full) notifications to be shown even
    347      * when the lockscreen is in "public" (secure & locked) mode?
    348      */
    349     public boolean userAllowsPrivateNotificationsInPublic(int userHandle) {
    350         if (userHandle == UserHandle.USER_ALL) {
    351             return true;
    352         }
    353 
    354         if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
    355             final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
    356                     mContext.getContentResolver(),
    357                     Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
    358             final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle,
    359                     DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
    360             final boolean allowed = allowedByUser && allowedByDpm;
    361             mUsersAllowingPrivateNotifications.append(userHandle, allowed);
    362             return allowed;
    363         }
    364 
    365         return mUsersAllowingPrivateNotifications.get(userHandle);
    366     }
    367 
    368     private boolean adminAllowsKeyguardFeature(int userHandle, int feature) {
    369         if (userHandle == UserHandle.USER_ALL) {
    370             return true;
    371         }
    372         final int dpmFlags =
    373                 mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */, userHandle);
    374         return (dpmFlags & feature) == 0;
    375     }
    376 
    377     /**
    378      * Save the current "public" (locked and secure) state of the lockscreen.
    379      */
    380     public void setLockscreenPublicMode(boolean publicMode, int userId) {
    381         mLockscreenPublicMode.put(userId, publicMode);
    382     }
    383 
    384     public boolean isLockscreenPublicMode(int userId) {
    385         if (userId == UserHandle.USER_ALL) {
    386             return mLockscreenPublicMode.get(mCurrentUserId, false);
    387         }
    388         return mLockscreenPublicMode.get(userId, false);
    389     }
    390 
    391     /**
    392      * Has the given user chosen to allow notifications to be shown even when the lockscreen is in
    393      * "public" (secure & locked) mode?
    394      */
    395     private boolean userAllowsNotificationsInPublic(int userHandle) {
    396         if (isCurrentProfile(userHandle) && userHandle != mCurrentUserId) {
    397             return true;
    398         }
    399 
    400         if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
    401             final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
    402                     mContext.getContentResolver(),
    403                     Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle);
    404             final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle,
    405                     DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
    406             final boolean allowed = allowedByUser && allowedByDpm;
    407             mUsersAllowingNotifications.append(userHandle, allowed);
    408             return allowed;
    409         }
    410 
    411         return mUsersAllowingNotifications.get(userHandle);
    412     }
    413 
    414     /** @return true if the entry needs redaction when on the lockscreen. */
    415     public boolean needsRedaction(NotificationData.Entry ent) {
    416         int userId = ent.notification.getUserId();
    417 
    418         boolean currentUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(mCurrentUserId);
    419         boolean notiUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(userId);
    420         boolean redactedLockscreen = currentUserWantsRedaction || notiUserWantsRedaction;
    421 
    422         boolean notificationRequestsRedaction =
    423                 ent.notification.getNotification().visibility == Notification.VISIBILITY_PRIVATE;
    424         boolean userForcesRedaction = packageHasVisibilityOverride(ent.notification.getKey());
    425 
    426         return userForcesRedaction || notificationRequestsRedaction && redactedLockscreen;
    427     }
    428 
    429     private boolean packageHasVisibilityOverride(String key) {
    430         if (mEntryManager == null) {
    431             Log.wtf(TAG, "mEntryManager was null!", new Throwable());
    432             return true;
    433         }
    434         return mEntryManager.getNotificationData().getVisibilityOverride(key) ==
    435                 Notification.VISIBILITY_PRIVATE;
    436     }
    437 
    438     private void updateCurrentProfilesCache() {
    439         synchronized (mCurrentProfiles) {
    440             mCurrentProfiles.clear();
    441             if (mUserManager != null) {
    442                 for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) {
    443                     mCurrentProfiles.put(user.id, user);
    444                 }
    445             }
    446         }
    447     }
    448 
    449     public boolean isAnyProfilePublicMode() {
    450         for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
    451             if (isLockscreenPublicMode(mCurrentProfiles.valueAt(i).id)) {
    452                 return true;
    453             }
    454         }
    455         return false;
    456     }
    457 
    458     /**
    459      * Returns the current user id. This can change if the user is switched.
    460      */
    461     public int getCurrentUserId() {
    462         return mCurrentUserId;
    463     }
    464 
    465     public SparseArray<UserInfo> getCurrentProfiles() {
    466         return mCurrentProfiles;
    467     }
    468 
    469     public void destroy() {
    470         mContext.unregisterReceiver(mBaseBroadcastReceiver);
    471         mContext.unregisterReceiver(mAllUsersReceiver);
    472     }
    473 
    474     @Override
    475     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    476         pw.println("NotificationLockscreenUserManager state:");
    477         pw.print("  mCurrentUserId=");
    478         pw.println(mCurrentUserId);
    479         pw.print("  mShowLockscreenNotifications=");
    480         pw.println(mShowLockscreenNotifications);
    481         pw.print("  mAllowLockscreenRemoteInput=");
    482         pw.println(mAllowLockscreenRemoteInput);
    483         pw.print("  mCurrentProfiles=");
    484         for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
    485             final int userId = mCurrentProfiles.valueAt(i).id;
    486             pw.print("" + userId + " ");
    487         }
    488         pw.println();
    489     }
    490 }
    491