blob: 427d169d6b908c726c912352fe85913fd84555ad [file] [log] [blame]
Jason Monk297c04e2018-08-23 17:16:59 -04001/*
2 * Copyright (C) 2018 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 */
16package com.android.systemui.statusbar;
17
Gus Prevasc71e53c2018-10-19 13:38:34 -040018import static android.app.NotificationManager.IMPORTANCE_DEFAULT;
Jason Monk297c04e2018-08-23 17:16:59 -040019import static android.app.admin.DevicePolicyManager.ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED;
20
21import android.app.ActivityManager;
22import android.app.KeyguardManager;
23import android.app.Notification;
24import android.app.admin.DevicePolicyManager;
25import android.content.BroadcastReceiver;
26import android.content.Context;
27import android.content.Intent;
28import android.content.IntentFilter;
29import android.content.IntentSender;
30import android.content.pm.UserInfo;
31import android.database.ContentObserver;
32import android.os.RemoteException;
33import android.os.ServiceManager;
34import android.os.UserHandle;
35import android.os.UserManager;
36import android.provider.Settings;
37import android.service.notification.StatusBarNotification;
38import android.util.Log;
39import android.util.SparseArray;
40import android.util.SparseBooleanArray;
41
42import com.android.internal.statusbar.IStatusBarService;
43import com.android.internal.statusbar.NotificationVisibility;
44import com.android.internal.widget.LockPatternUtils;
45import com.android.keyguard.KeyguardUpdateMonitor;
46import com.android.systemui.Dependency;
47import com.android.systemui.Dumpable;
Winson Chung2dbcf092018-10-24 13:00:41 -070048import com.android.systemui.recents.OverviewProxyService;
Jason Monk297c04e2018-08-23 17:16:59 -040049import com.android.systemui.statusbar.StatusBarStateController.StateListener;
50import com.android.systemui.statusbar.notification.NotificationData;
51import com.android.systemui.statusbar.notification.NotificationEntryManager;
Jason Monk297c04e2018-08-23 17:16:59 -040052import com.android.systemui.statusbar.policy.DeviceProvisionedController;
53import com.android.systemui.statusbar.policy.KeyguardMonitor;
54
55import java.io.FileDescriptor;
56import java.io.PrintWriter;
57
58/**
59 * Handles keeping track of the current user, profiles, and various things related to hiding
60 * contents, redacting notifications, and the lockscreen.
61 */
62public class NotificationLockscreenUserManagerImpl implements
63 Dumpable, NotificationLockscreenUserManager, StateListener {
64 private static final String TAG = "LockscreenUserManager";
65 private static final boolean ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT = false;
66
67 private final DeviceProvisionedController mDeviceProvisionedController =
68 Dependency.get(DeviceProvisionedController.class);
69 private final KeyguardMonitor mKeyguardMonitor = Dependency.get(KeyguardMonitor.class);
70
71 // Lazy
72 private NotificationEntryManager mEntryManager;
73
74 private final DevicePolicyManager mDevicePolicyManager;
75 private final SparseBooleanArray mLockscreenPublicMode = new SparseBooleanArray();
76 private final SparseBooleanArray mUsersAllowingPrivateNotifications = new SparseBooleanArray();
77 private final SparseBooleanArray mUsersAllowingNotifications = new SparseBooleanArray();
78 private final UserManager mUserManager;
79 private final IStatusBarService mBarService;
80
81 private boolean mShowLockscreenNotifications;
82 private boolean mAllowLockscreenRemoteInput;
83 private LockPatternUtils mLockPatternUtils;
84 protected KeyguardManager mKeyguardManager;
85 private int mState = StatusBarState.SHADE;
86
87 protected final BroadcastReceiver mAllUsersReceiver = new BroadcastReceiver() {
88 @Override
89 public void onReceive(Context context, Intent intent) {
90 final String action = intent.getAction();
91
92 if (ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED.equals(action) &&
93 isCurrentProfile(getSendingUserId())) {
94 mUsersAllowingPrivateNotifications.clear();
95 updateLockscreenNotificationSetting();
96 getEntryManager().updateNotifications();
97 }
98 }
99 };
100
101 protected final BroadcastReceiver mBaseBroadcastReceiver = new BroadcastReceiver() {
102 @Override
103 public void onReceive(Context context, Intent intent) {
104 String action = intent.getAction();
105 if (Intent.ACTION_USER_SWITCHED.equals(action)) {
106 mCurrentUserId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
107 updateCurrentProfilesCache();
108 Log.v(TAG, "userId " + mCurrentUserId + " is in the house");
109
110 updateLockscreenNotificationSetting();
111 updatePublicMode();
112 mPresenter.onUserSwitched(mCurrentUserId);
113 getEntryManager().getNotificationData().filterAndSort();
114 } else if (Intent.ACTION_USER_ADDED.equals(action)) {
115 updateCurrentProfilesCache();
116 } else if (Intent.ACTION_USER_UNLOCKED.equals(action)) {
117 // Start the overview connection to the launcher service
118 Dependency.get(OverviewProxyService.class).startConnectionToCurrentUser();
119 } else if (NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION.equals(action)) {
120 final IntentSender intentSender = intent.getParcelableExtra(Intent.EXTRA_INTENT);
121 final String notificationKey = intent.getStringExtra(Intent.EXTRA_INDEX);
122 if (intentSender != null) {
123 try {
124 mContext.startIntentSender(intentSender, null, 0, 0, 0);
125 } catch (IntentSender.SendIntentException e) {
126 /* ignore */
127 }
128 }
129 if (notificationKey != null) {
130 final int count =
131 getEntryManager().getNotificationData().getActiveNotifications().size();
132 final int rank = getEntryManager().getNotificationData().getRank(notificationKey);
133 final NotificationVisibility nv = NotificationVisibility.obtain(notificationKey,
134 rank, count, true);
135 try {
136 mBarService.onNotificationClick(notificationKey, nv);
137 } catch (RemoteException e) {
138 /* ignore */
139 }
140 }
141 }
142 }
143 };
144
145 protected final Context mContext;
146 protected final SparseArray<UserInfo> mCurrentProfiles = new SparseArray<>();
147
148 protected int mCurrentUserId = 0;
149 protected NotificationPresenter mPresenter;
150 protected ContentObserver mLockscreenSettingsObserver;
151 protected ContentObserver mSettingsObserver;
152
153 private NotificationEntryManager getEntryManager() {
154 if (mEntryManager == null) {
155 mEntryManager = Dependency.get(NotificationEntryManager.class);
156 }
157 return mEntryManager;
158 }
159
160 public NotificationLockscreenUserManagerImpl(Context context) {
161 mContext = context;
162 mDevicePolicyManager = (DevicePolicyManager) mContext.getSystemService(
163 Context.DEVICE_POLICY_SERVICE);
164 mUserManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
165 mCurrentUserId = ActivityManager.getCurrentUser();
166 mBarService = IStatusBarService.Stub.asInterface(
167 ServiceManager.getService(Context.STATUS_BAR_SERVICE));
168 Dependency.get(StatusBarStateController.class).addListener(this);
169 mLockPatternUtils = new LockPatternUtils(context);
170 mKeyguardManager = context.getSystemService(KeyguardManager.class);
171 }
172
173 public void setUpWithPresenter(NotificationPresenter presenter) {
174 mPresenter = presenter;
175
176 mLockscreenSettingsObserver = new ContentObserver(Dependency.get(Dependency.MAIN_HANDLER)) {
177 @Override
178 public void onChange(boolean selfChange) {
179 // We don't know which user changed LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS or
180 // LOCK_SCREEN_SHOW_NOTIFICATIONS, so we just dump our cache ...
181 mUsersAllowingPrivateNotifications.clear();
182 mUsersAllowingNotifications.clear();
183 // ... and refresh all the notifications
184 updateLockscreenNotificationSetting();
185 getEntryManager().updateNotifications();
186 }
187 };
188
189 mSettingsObserver = new ContentObserver(Dependency.get(Dependency.MAIN_HANDLER)) {
190 @Override
191 public void onChange(boolean selfChange) {
192 updateLockscreenNotificationSetting();
193 if (mDeviceProvisionedController.isDeviceProvisioned()) {
194 getEntryManager().updateNotifications();
195 }
196 }
197 };
198
199 mContext.getContentResolver().registerContentObserver(
200 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS), false,
201 mLockscreenSettingsObserver,
202 UserHandle.USER_ALL);
203
204 mContext.getContentResolver().registerContentObserver(
205 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS),
206 true,
207 mLockscreenSettingsObserver,
208 UserHandle.USER_ALL);
209
210 mContext.getContentResolver().registerContentObserver(
211 Settings.Global.getUriFor(Settings.Global.ZEN_MODE), false,
212 mSettingsObserver);
213
214 if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
215 mContext.getContentResolver().registerContentObserver(
216 Settings.Secure.getUriFor(Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT),
217 false,
218 mSettingsObserver,
219 UserHandle.USER_ALL);
220 }
221
222 mContext.registerReceiverAsUser(mAllUsersReceiver, UserHandle.ALL,
223 new IntentFilter(ACTION_DEVICE_POLICY_MANAGER_STATE_CHANGED),
224 null, null);
225
226 IntentFilter filter = new IntentFilter();
227 filter.addAction(Intent.ACTION_USER_SWITCHED);
228 filter.addAction(Intent.ACTION_USER_ADDED);
229 filter.addAction(Intent.ACTION_USER_UNLOCKED);
230 mContext.registerReceiver(mBaseBroadcastReceiver, filter);
231
232 IntentFilter internalFilter = new IntentFilter();
233 internalFilter.addAction(NOTIFICATION_UNLOCKED_BY_WORK_CHALLENGE_ACTION);
234 mContext.registerReceiver(mBaseBroadcastReceiver, internalFilter, PERMISSION_SELF, null);
235
236 updateCurrentProfilesCache();
237
238 mSettingsObserver.onChange(false); // set up
239 }
240
241 public boolean shouldShowLockscreenNotifications() {
242 return mShowLockscreenNotifications;
243 }
244
245 public boolean shouldAllowLockscreenRemoteInput() {
246 return mAllowLockscreenRemoteInput;
247 }
248
249 public boolean isCurrentProfile(int userId) {
250 synchronized (mCurrentProfiles) {
251 return userId == UserHandle.USER_ALL || mCurrentProfiles.get(userId) != null;
252 }
253 }
254
255 /**
256 * Returns true if notifications are temporarily disabled for this user for security reasons,
257 * regardless of the normal settings for that user.
258 */
259 private boolean shouldTemporarilyHideNotifications(int userId) {
260 if (userId == UserHandle.USER_ALL) {
261 userId = mCurrentUserId;
262 }
263 return KeyguardUpdateMonitor.getInstance(mContext).isUserInLockdown(userId);
264 }
265
266 /**
267 * Returns true if we're on a secure lockscreen and the user wants to hide notification data.
268 * If so, notifications should be hidden.
269 */
270 public boolean shouldHideNotifications(int userId) {
271 return isLockscreenPublicMode(userId) && !userAllowsNotificationsInPublic(userId)
272 || (userId != mCurrentUserId && shouldHideNotifications(mCurrentUserId))
273 || shouldTemporarilyHideNotifications(userId);
274 }
275
276 /**
277 * Returns true if we're on a secure lockscreen and the user wants to hide notifications via
278 * package-specific override.
279 */
280 public boolean shouldHideNotifications(String key) {
281 if (getEntryManager() == null) {
282 Log.wtf(TAG, "mEntryManager was null!", new Throwable());
283 return true;
284 }
285 return isLockscreenPublicMode(mCurrentUserId)
286 && getEntryManager().getNotificationData().getVisibilityOverride(key) ==
287 Notification.VISIBILITY_SECRET;
288 }
289
290 public boolean shouldShowOnKeyguard(StatusBarNotification sbn) {
291 if (getEntryManager() == null) {
292 Log.wtf(TAG, "mEntryManager was null!", new Throwable());
293 return false;
294 }
295 return mShowLockscreenNotifications
Gus Prevasc71e53c2018-10-19 13:38:34 -0400296 && getEntryManager().getNotificationData().getImportance(sbn.getKey())
297 >= IMPORTANCE_DEFAULT;
Jason Monk297c04e2018-08-23 17:16:59 -0400298 }
299
300 private void setShowLockscreenNotifications(boolean show) {
301 mShowLockscreenNotifications = show;
302 }
303
304 private void setLockscreenAllowRemoteInput(boolean allowLockscreenRemoteInput) {
305 mAllowLockscreenRemoteInput = allowLockscreenRemoteInput;
306 }
307
308 protected void updateLockscreenNotificationSetting() {
309 final boolean show = Settings.Secure.getIntForUser(mContext.getContentResolver(),
310 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
311 1,
312 mCurrentUserId) != 0;
313 final int dpmFlags = mDevicePolicyManager.getKeyguardDisabledFeatures(
314 null /* admin */, mCurrentUserId);
315 final boolean allowedByDpm = (dpmFlags
316 & DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS) == 0;
317
318 setShowLockscreenNotifications(show && allowedByDpm);
319
320 if (ENABLE_LOCK_SCREEN_ALLOW_REMOTE_INPUT) {
321 final boolean remoteInput = Settings.Secure.getIntForUser(mContext.getContentResolver(),
322 Settings.Secure.LOCK_SCREEN_ALLOW_REMOTE_INPUT,
323 0,
324 mCurrentUserId) != 0;
325 final boolean remoteInputDpm =
326 (dpmFlags & DevicePolicyManager.KEYGUARD_DISABLE_REMOTE_INPUT) == 0;
327
328 setLockscreenAllowRemoteInput(remoteInput && remoteInputDpm);
329 } else {
330 setLockscreenAllowRemoteInput(false);
331 }
332 }
333
334 /**
335 * Has the given user chosen to allow their private (full) notifications to be shown even
336 * when the lockscreen is in "public" (secure & locked) mode?
337 */
338 public boolean userAllowsPrivateNotificationsInPublic(int userHandle) {
339 if (userHandle == UserHandle.USER_ALL) {
340 return true;
341 }
342
343 if (mUsersAllowingPrivateNotifications.indexOfKey(userHandle) < 0) {
344 final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
345 mContext.getContentResolver(),
346 Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS, 0, userHandle);
347 final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle,
348 DevicePolicyManager.KEYGUARD_DISABLE_UNREDACTED_NOTIFICATIONS);
349 final boolean allowed = allowedByUser && allowedByDpm;
350 mUsersAllowingPrivateNotifications.append(userHandle, allowed);
351 return allowed;
352 }
353
354 return mUsersAllowingPrivateNotifications.get(userHandle);
355 }
356
357 private boolean adminAllowsKeyguardFeature(int userHandle, int feature) {
358 if (userHandle == UserHandle.USER_ALL) {
359 return true;
360 }
361 final int dpmFlags =
362 mDevicePolicyManager.getKeyguardDisabledFeatures(null /* admin */, userHandle);
363 return (dpmFlags & feature) == 0;
364 }
365
366 /**
367 * Save the current "public" (locked and secure) state of the lockscreen.
368 */
369 public void setLockscreenPublicMode(boolean publicMode, int userId) {
370 mLockscreenPublicMode.put(userId, publicMode);
371 }
372
373 public boolean isLockscreenPublicMode(int userId) {
374 if (userId == UserHandle.USER_ALL) {
375 return mLockscreenPublicMode.get(mCurrentUserId, false);
376 }
377 return mLockscreenPublicMode.get(userId, false);
378 }
379
380 /**
381 * Has the given user chosen to allow notifications to be shown even when the lockscreen is in
382 * "public" (secure & locked) mode?
383 */
384 private boolean userAllowsNotificationsInPublic(int userHandle) {
385 if (isCurrentProfile(userHandle) && userHandle != mCurrentUserId) {
386 return true;
387 }
388
389 if (mUsersAllowingNotifications.indexOfKey(userHandle) < 0) {
390 final boolean allowedByUser = 0 != Settings.Secure.getIntForUser(
391 mContext.getContentResolver(),
392 Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, 0, userHandle);
393 final boolean allowedByDpm = adminAllowsKeyguardFeature(userHandle,
394 DevicePolicyManager.KEYGUARD_DISABLE_SECURE_NOTIFICATIONS);
395 final boolean allowed = allowedByUser && allowedByDpm;
396 mUsersAllowingNotifications.append(userHandle, allowed);
397 return allowed;
398 }
399
400 return mUsersAllowingNotifications.get(userHandle);
401 }
402
403 /** @return true if the entry needs redaction when on the lockscreen. */
404 public boolean needsRedaction(NotificationData.Entry ent) {
405 int userId = ent.notification.getUserId();
406
407 boolean currentUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(mCurrentUserId);
408 boolean notiUserWantsRedaction = !userAllowsPrivateNotificationsInPublic(userId);
409 boolean redactedLockscreen = currentUserWantsRedaction || notiUserWantsRedaction;
410
411 boolean notificationRequestsRedaction =
412 ent.notification.getNotification().visibility == Notification.VISIBILITY_PRIVATE;
413 boolean userForcesRedaction = packageHasVisibilityOverride(ent.notification.getKey());
414
415 return userForcesRedaction || notificationRequestsRedaction && redactedLockscreen;
416 }
417
418 private boolean packageHasVisibilityOverride(String key) {
419 if (getEntryManager() == null) {
420 Log.wtf(TAG, "mEntryManager was null!", new Throwable());
421 return true;
422 }
423 return getEntryManager().getNotificationData().getVisibilityOverride(key) ==
424 Notification.VISIBILITY_PRIVATE;
425 }
426
427 private void updateCurrentProfilesCache() {
428 synchronized (mCurrentProfiles) {
429 mCurrentProfiles.clear();
430 if (mUserManager != null) {
431 for (UserInfo user : mUserManager.getProfiles(mCurrentUserId)) {
432 mCurrentProfiles.put(user.id, user);
433 }
434 }
435 }
436 }
437
438 public boolean isAnyProfilePublicMode() {
439 for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
440 if (isLockscreenPublicMode(mCurrentProfiles.valueAt(i).id)) {
441 return true;
442 }
443 }
444 return false;
445 }
446
447 /**
448 * Returns the current user id. This can change if the user is switched.
449 */
450 public int getCurrentUserId() {
451 return mCurrentUserId;
452 }
453
454 public SparseArray<UserInfo> getCurrentProfiles() {
455 return mCurrentProfiles;
456 }
457
458 @Override
459 public void onStateChanged(int newState) {
460 mState = newState;
461 updatePublicMode();
462 }
463
464 public void updatePublicMode() {
465 //TODO: I think there may be a race condition where mKeyguardViewManager.isShowing() returns
466 // false when it should be true. Therefore, if we are not on the SHADE, don't even bother
467 // asking if the keyguard is showing. We still need to check it though because showing the
468 // camera on the keyguard has a state of SHADE but the keyguard is still showing.
469 final boolean showingKeyguard = mState != StatusBarState.SHADE
470 || mKeyguardMonitor.isShowing();
471 final boolean devicePublic = showingKeyguard && isSecure(getCurrentUserId());
472
473
474 // Look for public mode users. Users are considered public in either case of:
475 // - device keyguard is shown in secure mode;
476 // - profile is locked with a work challenge.
477 SparseArray<UserInfo> currentProfiles = getCurrentProfiles();
478 for (int i = currentProfiles.size() - 1; i >= 0; i--) {
479 final int userId = currentProfiles.valueAt(i).id;
480 boolean isProfilePublic = devicePublic;
481 if (!devicePublic && userId != getCurrentUserId()) {
482 // We can't rely on KeyguardManager#isDeviceLocked() for unified profile challenge
483 // due to a race condition where this code could be called before
484 // TrustManagerService updates its internal records, resulting in an incorrect
485 // state being cached in mLockscreenPublicMode. (b/35951989)
486 if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
487 && isSecure(userId)) {
488 isProfilePublic = mKeyguardManager.isDeviceLocked(userId);
489 }
490 }
491 setLockscreenPublicMode(isProfilePublic, userId);
492 }
493 }
494
495
496// public void updatePublicMode() {
497// //TODO: I think there may be a race condition where mKeyguardViewManager.isShowing() returns
498// // false when it should be true. Therefore, if we are not on the SHADE, don't even bother
499// // asking if the keyguard is showing. We still need to check it though because showing the
500// // camera on the keyguard has a state of SHADE but the keyguard is still showing.
501// final boolean showingKeyguard = mState != StatusBarState.SHADE
502// || mKeyguardMonitor.isShowing();
503// final boolean devicePublic = showingKeyguard && isSecure(getCurrentUserId());
504//
505//
506// // Look for public mode users. Users are considered public in either case of:
507// // - device keyguard is shown in secure mode;
508// // - profile is locked with a work challenge.
509// SparseArray<UserInfo> currentProfiles = getCurrentProfiles();
510// for (int i = currentProfiles.size() - 1; i >= 0; i--) {
511// final int userId = currentProfiles.valueAt(i).id;
512// boolean isProfilePublic = devicePublic;
513// if (!devicePublic && userId != getCurrentUserId()) {
514// // We can't rely on KeyguardManager#isDeviceLocked() for unified profile challenge
515// // due to a race condition where this code could be called before
516// // TrustManagerService updates its internal records, resulting in an incorrect
517// // state being cached in mLockscreenPublicMode. (b/35951989)
518// if (mLockPatternUtils.isSeparateProfileChallengeEnabled(userId)
519// && isSecure(userId)) {
520// isProfilePublic = mKeyguardManager.isDeviceLocked(userId);
521// }
522// }
523// setLockscreenPublicMode(isProfilePublic, userId);
524// }
525// }
526
527 private boolean isSecure(int userId) {
528 return mKeyguardMonitor.isSecure() || mLockPatternUtils.isSecure(userId);
529 }
530
531 public void destroy() {
532 mContext.unregisterReceiver(mBaseBroadcastReceiver);
533 mContext.unregisterReceiver(mAllUsersReceiver);
534 }
535
536 @Override
537 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
538 pw.println("NotificationLockscreenUserManager state:");
539 pw.print(" mCurrentUserId=");
540 pw.println(mCurrentUserId);
541 pw.print(" mShowLockscreenNotifications=");
542 pw.println(mShowLockscreenNotifications);
543 pw.print(" mAllowLockscreenRemoteInput=");
544 pw.println(mAllowLockscreenRemoteInput);
545 pw.print(" mCurrentProfiles=");
546 for (int i = mCurrentProfiles.size() - 1; i >= 0; i--) {
547 final int userId = mCurrentProfiles.valueAt(i).id;
548 pw.print("" + userId + " ");
549 }
550 pw.println();
551 }
552}