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