Julia Reynolds | a614c27 | 2019-11-15 16:43:29 -0500 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2019 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 | |
| 17 | package com.android.server.notification; |
| 18 | |
| 19 | import android.annotation.NonNull; |
| 20 | import android.annotation.Nullable; |
| 21 | import android.annotation.UserIdInt; |
| 22 | import android.app.NotificationHistory; |
| 23 | import android.app.NotificationHistory.HistoricalNotification; |
Julia Reynolds | e43fac3 | 2019-11-20 13:36:24 -0500 | [diff] [blame] | 24 | import android.content.ContentResolver; |
Julia Reynolds | a614c27 | 2019-11-15 16:43:29 -0500 | [diff] [blame] | 25 | import android.content.Context; |
Julia Reynolds | e43fac3 | 2019-11-20 13:36:24 -0500 | [diff] [blame] | 26 | import android.content.pm.UserInfo; |
| 27 | import android.database.ContentObserver; |
| 28 | import android.net.Uri; |
Julia Reynolds | 037653b | 2020-04-23 12:53:35 -0400 | [diff] [blame] | 29 | import android.os.Binder; |
Julia Reynolds | a614c27 | 2019-11-15 16:43:29 -0500 | [diff] [blame] | 30 | import android.os.Environment; |
Julia Reynolds | e43fac3 | 2019-11-20 13:36:24 -0500 | [diff] [blame] | 31 | import android.os.Handler; |
| 32 | import android.os.UserHandle; |
Julia Reynolds | a614c27 | 2019-11-15 16:43:29 -0500 | [diff] [blame] | 33 | import android.os.UserManager; |
Julia Reynolds | e43fac3 | 2019-11-20 13:36:24 -0500 | [diff] [blame] | 34 | import android.provider.Settings; |
Julia Reynolds | a614c27 | 2019-11-15 16:43:29 -0500 | [diff] [blame] | 35 | import android.util.Slog; |
| 36 | import android.util.SparseArray; |
| 37 | import android.util.SparseBooleanArray; |
| 38 | |
| 39 | import com.android.internal.annotations.GuardedBy; |
| 40 | import com.android.internal.annotations.VisibleForTesting; |
Julia Reynolds | 037653b | 2020-04-23 12:53:35 -0400 | [diff] [blame] | 41 | import com.android.internal.util.FunctionalUtils; |
Julia Reynolds | a614c27 | 2019-11-15 16:43:29 -0500 | [diff] [blame] | 42 | import com.android.server.IoThread; |
Julia Reynolds | a614c27 | 2019-11-15 16:43:29 -0500 | [diff] [blame] | 43 | |
| 44 | import java.io.File; |
| 45 | import java.util.ArrayList; |
| 46 | import java.util.List; |
| 47 | |
| 48 | /** |
| 49 | * Keeps track of per-user notification histories. |
| 50 | */ |
| 51 | public class NotificationHistoryManager { |
| 52 | private static final String TAG = "NotificationHistory"; |
| 53 | private static final boolean DEBUG = NotificationManagerService.DBG; |
| 54 | |
| 55 | @VisibleForTesting |
| 56 | static final String DIRECTORY_PER_USER = "notification_history"; |
| 57 | |
| 58 | private final Context mContext; |
| 59 | private final UserManager mUserManager; |
Julia Reynolds | e43fac3 | 2019-11-20 13:36:24 -0500 | [diff] [blame] | 60 | @VisibleForTesting |
| 61 | final SettingsObserver mSettingsObserver; |
Julia Reynolds | a614c27 | 2019-11-15 16:43:29 -0500 | [diff] [blame] | 62 | private final Object mLock = new Object(); |
| 63 | @GuardedBy("mLock") |
| 64 | private final SparseArray<NotificationHistoryDatabase> mUserState = new SparseArray<>(); |
| 65 | @GuardedBy("mLock") |
| 66 | private final SparseBooleanArray mUserUnlockedStates = new SparseBooleanArray(); |
| 67 | // TODO: does this need to be persisted across reboots? |
| 68 | @GuardedBy("mLock") |
| 69 | private final SparseArray<List<String>> mUserPendingPackageRemovals = new SparseArray<>(); |
Julia Reynolds | e43fac3 | 2019-11-20 13:36:24 -0500 | [diff] [blame] | 70 | @GuardedBy("mLock") |
| 71 | private final SparseBooleanArray mHistoryEnabled = new SparseBooleanArray(); |
Julia Reynolds | 6c11103 | 2020-01-17 12:32:59 -0500 | [diff] [blame] | 72 | @GuardedBy("mLock") |
| 73 | private final SparseBooleanArray mUserPendingHistoryDisables = new SparseBooleanArray(); |
Julia Reynolds | a614c27 | 2019-11-15 16:43:29 -0500 | [diff] [blame] | 74 | |
Julia Reynolds | e43fac3 | 2019-11-20 13:36:24 -0500 | [diff] [blame] | 75 | public NotificationHistoryManager(Context context, Handler handler) { |
Julia Reynolds | a614c27 | 2019-11-15 16:43:29 -0500 | [diff] [blame] | 76 | mContext = context; |
| 77 | mUserManager = context.getSystemService(UserManager.class); |
Julia Reynolds | e43fac3 | 2019-11-20 13:36:24 -0500 | [diff] [blame] | 78 | mSettingsObserver = new SettingsObserver(handler); |
Julia Reynolds | a614c27 | 2019-11-15 16:43:29 -0500 | [diff] [blame] | 79 | } |
| 80 | |
Julia Reynolds | 6c11103 | 2020-01-17 12:32:59 -0500 | [diff] [blame] | 81 | @VisibleForTesting |
| 82 | void onDestroy() { |
| 83 | mSettingsObserver.stopObserving(); |
| 84 | } |
| 85 | |
Julia Reynolds | e43fac3 | 2019-11-20 13:36:24 -0500 | [diff] [blame] | 86 | void onBootPhaseAppsCanStart() { |
| 87 | mSettingsObserver.observe(); |
| 88 | } |
| 89 | |
| 90 | void onUserUnlocked(@UserIdInt int userId) { |
Julia Reynolds | a614c27 | 2019-11-15 16:43:29 -0500 | [diff] [blame] | 91 | synchronized (mLock) { |
| 92 | mUserUnlockedStates.put(userId, true); |
| 93 | final NotificationHistoryDatabase userHistory = |
| 94 | getUserHistoryAndInitializeIfNeededLocked(userId); |
| 95 | if (userHistory == null) { |
Julia Reynolds | e43fac3 | 2019-11-20 13:36:24 -0500 | [diff] [blame] | 96 | Slog.i(TAG, "Attempted to unlock gone/disabled user " + userId); |
Julia Reynolds | a614c27 | 2019-11-15 16:43:29 -0500 | [diff] [blame] | 97 | return; |
| 98 | } |
| 99 | |
| 100 | // remove any packages that were deleted while the user was locked |
| 101 | final List<String> pendingPackageRemovals = mUserPendingPackageRemovals.get(userId); |
| 102 | if (pendingPackageRemovals != null) { |
| 103 | for (int i = 0; i < pendingPackageRemovals.size(); i++) { |
| 104 | userHistory.onPackageRemoved(pendingPackageRemovals.get(i)); |
| 105 | } |
| 106 | mUserPendingPackageRemovals.put(userId, null); |
| 107 | } |
Julia Reynolds | e43fac3 | 2019-11-20 13:36:24 -0500 | [diff] [blame] | 108 | |
| 109 | // delete history if it was disabled when the user was locked |
Julia Reynolds | 6c11103 | 2020-01-17 12:32:59 -0500 | [diff] [blame] | 110 | if (mUserPendingHistoryDisables.get(userId)) { |
| 111 | disableHistory(userHistory, userId); |
Julia Reynolds | e43fac3 | 2019-11-20 13:36:24 -0500 | [diff] [blame] | 112 | } |
Julia Reynolds | a614c27 | 2019-11-15 16:43:29 -0500 | [diff] [blame] | 113 | } |
| 114 | } |
| 115 | |
| 116 | public void onUserStopped(@UserIdInt int userId) { |
| 117 | synchronized (mLock) { |
| 118 | mUserUnlockedStates.put(userId, false); |
| 119 | mUserState.put(userId, null); // release the service (mainly for GC) |
| 120 | } |
| 121 | } |
| 122 | |
Julia Reynolds | b317ff7 | 2019-11-26 14:20:51 -0500 | [diff] [blame] | 123 | public void onUserRemoved(@UserIdInt int userId) { |
Julia Reynolds | a614c27 | 2019-11-15 16:43:29 -0500 | [diff] [blame] | 124 | synchronized (mLock) { |
| 125 | // Actual data deletion is handled by other parts of the system (the entire directory is |
| 126 | // removed) - we just need clean up our internal state for GC |
| 127 | mUserPendingPackageRemovals.put(userId, null); |
Julia Reynolds | e43fac3 | 2019-11-20 13:36:24 -0500 | [diff] [blame] | 128 | mHistoryEnabled.put(userId, false); |
Julia Reynolds | 6c11103 | 2020-01-17 12:32:59 -0500 | [diff] [blame] | 129 | mUserPendingHistoryDisables.put(userId, false); |
Julia Reynolds | a614c27 | 2019-11-15 16:43:29 -0500 | [diff] [blame] | 130 | onUserStopped(userId); |
| 131 | } |
| 132 | } |
| 133 | |
Julia Reynolds | b1a7718 | 2020-02-06 16:38:10 -0500 | [diff] [blame] | 134 | public void onPackageRemoved(@UserIdInt int userId, String packageName) { |
Julia Reynolds | a614c27 | 2019-11-15 16:43:29 -0500 | [diff] [blame] | 135 | synchronized (mLock) { |
| 136 | if (!mUserUnlockedStates.get(userId, false)) { |
Julia Reynolds | e43fac3 | 2019-11-20 13:36:24 -0500 | [diff] [blame] | 137 | if (mHistoryEnabled.get(userId, false)) { |
| 138 | List<String> userPendingRemovals = |
| 139 | mUserPendingPackageRemovals.get(userId, new ArrayList<>()); |
| 140 | userPendingRemovals.add(packageName); |
| 141 | mUserPendingPackageRemovals.put(userId, userPendingRemovals); |
| 142 | } |
Julia Reynolds | a614c27 | 2019-11-15 16:43:29 -0500 | [diff] [blame] | 143 | return; |
| 144 | } |
| 145 | final NotificationHistoryDatabase userHistory = mUserState.get(userId); |
| 146 | if (userHistory == null) { |
| 147 | return; |
| 148 | } |
| 149 | |
| 150 | userHistory.onPackageRemoved(packageName); |
| 151 | } |
| 152 | } |
| 153 | |
Julia Reynolds | b1a7718 | 2020-02-06 16:38:10 -0500 | [diff] [blame] | 154 | public void deleteNotificationHistoryItem(String pkg, int uid, long postedTime) { |
| 155 | synchronized (mLock) { |
| 156 | int userId = UserHandle.getUserId(uid); |
| 157 | final NotificationHistoryDatabase userHistory = |
| 158 | getUserHistoryAndInitializeIfNeededLocked(userId); |
| 159 | // TODO: it shouldn't be possible to delete a notification entry while the user is |
| 160 | // locked but we should handle it |
| 161 | if (userHistory == null) { |
| 162 | Slog.w(TAG, "Attempted to remove notif for locked/gone/disabled user " |
| 163 | + userId); |
| 164 | return; |
| 165 | } |
| 166 | userHistory.deleteNotificationHistoryItem(pkg, postedTime); |
| 167 | } |
| 168 | } |
| 169 | |
Julia Reynolds | a625b94 | 2020-02-15 09:42:23 -0500 | [diff] [blame] | 170 | public void deleteConversation(String pkg, int uid, String conversationId) { |
| 171 | synchronized (mLock) { |
| 172 | int userId = UserHandle.getUserId(uid); |
| 173 | final NotificationHistoryDatabase userHistory = |
| 174 | getUserHistoryAndInitializeIfNeededLocked(userId); |
| 175 | // TODO: it shouldn't be possible to delete a notification entry while the user is |
| 176 | // locked but we should handle it |
| 177 | if (userHistory == null) { |
| 178 | Slog.w(TAG, "Attempted to remove conversation for locked/gone/disabled user " |
| 179 | + userId); |
| 180 | return; |
| 181 | } |
| 182 | userHistory.deleteConversation(pkg, conversationId); |
| 183 | } |
| 184 | } |
| 185 | |
Julia Reynolds | b317ff7 | 2019-11-26 14:20:51 -0500 | [diff] [blame] | 186 | public void triggerWriteToDisk() { |
Julia Reynolds | a614c27 | 2019-11-15 16:43:29 -0500 | [diff] [blame] | 187 | synchronized (mLock) { |
| 188 | final int userCount = mUserState.size(); |
| 189 | for (int i = 0; i < userCount; i++) { |
| 190 | final int userId = mUserState.keyAt(i); |
| 191 | if (!mUserUnlockedStates.get(userId)) { |
| 192 | continue; |
| 193 | } |
| 194 | NotificationHistoryDatabase userHistory = mUserState.get(userId); |
| 195 | if (userHistory != null) { |
| 196 | userHistory.forceWriteToDisk(); |
| 197 | } |
| 198 | } |
| 199 | } |
| 200 | } |
| 201 | |
| 202 | public void addNotification(@NonNull final HistoricalNotification notification) { |
Julia Reynolds | 037653b | 2020-04-23 12:53:35 -0400 | [diff] [blame] | 203 | Binder.withCleanCallingIdentity(() -> { |
| 204 | synchronized (mLock) { |
| 205 | final NotificationHistoryDatabase userHistory = |
| 206 | getUserHistoryAndInitializeIfNeededLocked(notification.getUserId()); |
| 207 | if (userHistory == null) { |
| 208 | Slog.w(TAG, "Attempted to add notif for locked/gone/disabled user " |
| 209 | + notification.getUserId()); |
| 210 | return; |
| 211 | } |
| 212 | userHistory.addNotification(notification); |
Julia Reynolds | a614c27 | 2019-11-15 16:43:29 -0500 | [diff] [blame] | 213 | } |
Julia Reynolds | 037653b | 2020-04-23 12:53:35 -0400 | [diff] [blame] | 214 | }); |
Julia Reynolds | a614c27 | 2019-11-15 16:43:29 -0500 | [diff] [blame] | 215 | } |
| 216 | |
| 217 | public @NonNull NotificationHistory readNotificationHistory(@UserIdInt int[] userIds) { |
| 218 | synchronized (mLock) { |
| 219 | NotificationHistory mergedHistory = new NotificationHistory(); |
| 220 | if (userIds == null) { |
| 221 | return mergedHistory; |
| 222 | } |
| 223 | for (int userId : userIds) { |
| 224 | final NotificationHistoryDatabase userHistory = |
| 225 | getUserHistoryAndInitializeIfNeededLocked(userId); |
| 226 | if (userHistory == null) { |
Julia Reynolds | e43fac3 | 2019-11-20 13:36:24 -0500 | [diff] [blame] | 227 | Slog.i(TAG, "Attempted to read history for locked/gone/disabled user " +userId); |
Julia Reynolds | a614c27 | 2019-11-15 16:43:29 -0500 | [diff] [blame] | 228 | continue; |
| 229 | } |
| 230 | mergedHistory.addNotificationsToWrite(userHistory.readNotificationHistory()); |
| 231 | } |
| 232 | return mergedHistory; |
| 233 | } |
| 234 | } |
| 235 | |
| 236 | public @NonNull android.app.NotificationHistory readFilteredNotificationHistory( |
| 237 | @UserIdInt int userId, String packageName, String channelId, int maxNotifications) { |
| 238 | synchronized (mLock) { |
| 239 | final NotificationHistoryDatabase userHistory = |
| 240 | getUserHistoryAndInitializeIfNeededLocked(userId); |
| 241 | if (userHistory == null) { |
Julia Reynolds | e43fac3 | 2019-11-20 13:36:24 -0500 | [diff] [blame] | 242 | Slog.i(TAG, "Attempted to read history for locked/gone/disabled user " +userId); |
Julia Reynolds | a614c27 | 2019-11-15 16:43:29 -0500 | [diff] [blame] | 243 | return new android.app.NotificationHistory(); |
| 244 | } |
| 245 | |
| 246 | return userHistory.readNotificationHistory(packageName, channelId, maxNotifications); |
| 247 | } |
| 248 | } |
| 249 | |
Julia Reynolds | b317ff7 | 2019-11-26 14:20:51 -0500 | [diff] [blame] | 250 | boolean isHistoryEnabled(@UserIdInt int userId) { |
Julia Reynolds | e43fac3 | 2019-11-20 13:36:24 -0500 | [diff] [blame] | 251 | synchronized (mLock) { |
| 252 | return mHistoryEnabled.get(userId); |
| 253 | } |
| 254 | } |
| 255 | |
| 256 | void onHistoryEnabledChanged(@UserIdInt int userId, boolean historyEnabled) { |
| 257 | synchronized (mLock) { |
Julia Reynolds | 6c11103 | 2020-01-17 12:32:59 -0500 | [diff] [blame] | 258 | if (historyEnabled) { |
| 259 | mHistoryEnabled.put(userId, historyEnabled); |
| 260 | } |
Julia Reynolds | e43fac3 | 2019-11-20 13:36:24 -0500 | [diff] [blame] | 261 | final NotificationHistoryDatabase userHistory = |
| 262 | getUserHistoryAndInitializeIfNeededLocked(userId); |
| 263 | if (userHistory != null) { |
| 264 | if (!historyEnabled) { |
Julia Reynolds | 6c11103 | 2020-01-17 12:32:59 -0500 | [diff] [blame] | 265 | disableHistory(userHistory, userId); |
Julia Reynolds | e43fac3 | 2019-11-20 13:36:24 -0500 | [diff] [blame] | 266 | } |
Julia Reynolds | 6c11103 | 2020-01-17 12:32:59 -0500 | [diff] [blame] | 267 | } else { |
| 268 | mUserPendingHistoryDisables.put(userId, !historyEnabled); |
Julia Reynolds | e43fac3 | 2019-11-20 13:36:24 -0500 | [diff] [blame] | 269 | } |
| 270 | } |
| 271 | } |
| 272 | |
Julia Reynolds | 6c11103 | 2020-01-17 12:32:59 -0500 | [diff] [blame] | 273 | private void disableHistory(NotificationHistoryDatabase userHistory, @UserIdInt int userId) { |
| 274 | userHistory.disableHistory(); |
| 275 | |
| 276 | mUserPendingHistoryDisables.put(userId, false); |
| 277 | mHistoryEnabled.put(userId, false); |
| 278 | mUserState.put(userId, null); |
| 279 | } |
| 280 | |
Julia Reynolds | a614c27 | 2019-11-15 16:43:29 -0500 | [diff] [blame] | 281 | @GuardedBy("mLock") |
| 282 | private @Nullable NotificationHistoryDatabase getUserHistoryAndInitializeIfNeededLocked( |
| 283 | int userId) { |
Julia Reynolds | e43fac3 | 2019-11-20 13:36:24 -0500 | [diff] [blame] | 284 | if (!mHistoryEnabled.get(userId)) { |
| 285 | if (DEBUG) { |
| 286 | Slog.i(TAG, "History disabled for user " + userId); |
| 287 | } |
| 288 | mUserState.put(userId, null); |
| 289 | return null; |
| 290 | } |
Julia Reynolds | a614c27 | 2019-11-15 16:43:29 -0500 | [diff] [blame] | 291 | NotificationHistoryDatabase userHistory = mUserState.get(userId); |
| 292 | if (userHistory == null) { |
| 293 | final File historyDir = new File(Environment.getDataSystemCeDirectory(userId), |
| 294 | DIRECTORY_PER_USER); |
| 295 | userHistory = NotificationHistoryDatabaseFactory.create(mContext, IoThread.getHandler(), |
Julia Reynolds | 575e088 | 2020-02-28 17:02:11 -0500 | [diff] [blame] | 296 | historyDir); |
Julia Reynolds | a614c27 | 2019-11-15 16:43:29 -0500 | [diff] [blame] | 297 | if (mUserUnlockedStates.get(userId)) { |
| 298 | try { |
| 299 | userHistory.init(); |
| 300 | } catch (Exception e) { |
| 301 | if (mUserManager.isUserUnlocked(userId)) { |
| 302 | throw e; // rethrow exception - user is unlocked |
| 303 | } else { |
| 304 | Slog.w(TAG, "Attempted to initialize service for " |
| 305 | + "stopped or removed user " + userId); |
| 306 | return null; |
| 307 | } |
| 308 | } |
| 309 | } else { |
| 310 | // locked! data unavailable |
| 311 | Slog.w(TAG, "Attempted to initialize service for " |
| 312 | + "stopped or removed user " + userId); |
| 313 | return null; |
| 314 | } |
| 315 | mUserState.put(userId, userHistory); |
| 316 | } |
| 317 | return userHistory; |
| 318 | } |
| 319 | |
| 320 | @VisibleForTesting |
| 321 | boolean isUserUnlocked(@UserIdInt int userId) { |
| 322 | synchronized (mLock) { |
| 323 | return mUserUnlockedStates.get(userId); |
| 324 | } |
| 325 | } |
| 326 | |
| 327 | @VisibleForTesting |
| 328 | boolean doesHistoryExistForUser(@UserIdInt int userId) { |
| 329 | synchronized (mLock) { |
| 330 | return mUserState.get(userId) != null; |
| 331 | } |
| 332 | } |
| 333 | |
| 334 | @VisibleForTesting |
| 335 | void replaceNotificationHistoryDatabase(@UserIdInt int userId, |
| 336 | NotificationHistoryDatabase replacement) { |
| 337 | synchronized (mLock) { |
| 338 | if (mUserState.get(userId) != null) { |
| 339 | mUserState.put(userId, replacement); |
| 340 | } |
| 341 | } |
| 342 | } |
| 343 | |
| 344 | @VisibleForTesting |
| 345 | List<String> getPendingPackageRemovalsForUser(@UserIdInt int userId) { |
| 346 | synchronized (mLock) { |
| 347 | return mUserPendingPackageRemovals.get(userId); |
| 348 | } |
| 349 | } |
Julia Reynolds | e43fac3 | 2019-11-20 13:36:24 -0500 | [diff] [blame] | 350 | |
| 351 | final class SettingsObserver extends ContentObserver { |
| 352 | private final Uri NOTIFICATION_HISTORY_URI |
| 353 | = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_HISTORY_ENABLED); |
| 354 | |
| 355 | SettingsObserver(Handler handler) { |
| 356 | super(handler); |
| 357 | } |
| 358 | |
| 359 | void observe() { |
| 360 | ContentResolver resolver = mContext.getContentResolver(); |
| 361 | resolver.registerContentObserver(NOTIFICATION_HISTORY_URI, |
| 362 | false, this, UserHandle.USER_ALL); |
| 363 | synchronized (mLock) { |
| 364 | for (UserInfo userInfo : mUserManager.getUsers()) { |
Julia Reynolds | fd9f834 | 2020-03-06 09:36:00 -0500 | [diff] [blame] | 365 | if (!userInfo.isProfile()) { |
| 366 | update(null, userInfo.id); |
| 367 | } |
Julia Reynolds | e43fac3 | 2019-11-20 13:36:24 -0500 | [diff] [blame] | 368 | } |
| 369 | } |
| 370 | } |
| 371 | |
Julia Reynolds | 6c11103 | 2020-01-17 12:32:59 -0500 | [diff] [blame] | 372 | void stopObserving() { |
| 373 | ContentResolver resolver = mContext.getContentResolver(); |
| 374 | resolver.unregisterContentObserver(this); |
| 375 | } |
| 376 | |
Julia Reynolds | e43fac3 | 2019-11-20 13:36:24 -0500 | [diff] [blame] | 377 | @Override |
| 378 | public void onChange(boolean selfChange, Uri uri, int userId) { |
| 379 | update(uri, userId); |
| 380 | } |
| 381 | |
| 382 | public void update(Uri uri, int userId) { |
| 383 | ContentResolver resolver = mContext.getContentResolver(); |
| 384 | if (uri == null || NOTIFICATION_HISTORY_URI.equals(uri)) { |
| 385 | boolean historyEnabled = Settings.Secure.getIntForUser(resolver, |
| 386 | Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, userId) |
| 387 | != 0; |
Julia Reynolds | fd9f834 | 2020-03-06 09:36:00 -0500 | [diff] [blame] | 388 | int[] profiles = mUserManager.getProfileIds(userId, true); |
| 389 | for (int profileId : profiles) { |
| 390 | onHistoryEnabledChanged(profileId, historyEnabled); |
| 391 | } |
Julia Reynolds | e43fac3 | 2019-11-20 13:36:24 -0500 | [diff] [blame] | 392 | } |
| 393 | } |
| 394 | } |
Julia Reynolds | a614c27 | 2019-11-15 16:43:29 -0500 | [diff] [blame] | 395 | } |