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 
NotificationLockscreenUserManager(Context context)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 
setUpWithPresenter(NotificationPresenter presenter, NotificationEntryManager entryManager)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 
showForegroundManagedProfileActivityToast()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 
shouldShowLockscreenNotifications()253     public boolean shouldShowLockscreenNotifications() {
254         return mShowLockscreenNotifications;
255     }
256 
shouldAllowLockscreenRemoteInput()257     public boolean shouldAllowLockscreenRemoteInput() {
258         return mAllowLockscreenRemoteInput;
259     }
260 
isCurrentProfile(int userId)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      */
shouldTemporarilyHideNotifications(int userId)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      */
shouldHideNotifications(int userId)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      */
shouldHideNotifications(String key)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 
shouldShowOnKeyguard(StatusBarNotification sbn)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 
setShowLockscreenNotifications(boolean show)311     private void setShowLockscreenNotifications(boolean show) {
312         mShowLockscreenNotifications = show;
313     }
314 
setLockscreenAllowRemoteInput(boolean allowLockscreenRemoteInput)315     private void setLockscreenAllowRemoteInput(boolean allowLockscreenRemoteInput) {
316         mAllowLockscreenRemoteInput = allowLockscreenRemoteInput;
317     }
318 
updateLockscreenNotificationSetting()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      */
userAllowsPrivateNotificationsInPublic(int userHandle)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 
adminAllowsKeyguardFeature(int userHandle, int feature)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      */
setLockscreenPublicMode(boolean publicMode, int userId)380     public void setLockscreenPublicMode(boolean publicMode, int userId) {
381         mLockscreenPublicMode.put(userId, publicMode);
382     }
383 
isLockscreenPublicMode(int userId)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      */
userAllowsNotificationsInPublic(int userHandle)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. */
needsRedaction(NotificationData.Entry ent)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 
packageHasVisibilityOverride(String key)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 
updateCurrentProfilesCache()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 
isAnyProfilePublicMode()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      */
getCurrentUserId()461     public int getCurrentUserId() {
462         return mCurrentUserId;
463     }
464 
getCurrentProfiles()465     public SparseArray<UserInfo> getCurrentProfiles() {
466         return mCurrentProfiles;
467     }
468 
destroy()469     public void destroy() {
470         mContext.unregisterReceiver(mBaseBroadcastReceiver);
471         mContext.unregisterReceiver(mAllUsersReceiver);
472     }
473 
474     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)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