Loading core/java/android/provider/Settings.java +9 −0 Original line number Diff line number Diff line Loading @@ -8155,6 +8155,15 @@ public final class Settings { @TestApi public static final String NOTIFICATION_BADGING = "notification_badging"; /** * When enabled the system will maintain a rolling history of received notifications. When * disabled the history will be disabled and deleted. * * The value 1 - enable, 0 - disable * @hide */ public static final String NOTIFICATION_HISTORY_ENABLED = "notification_history_enabled"; /** * Whether notifications are dismissed by a right-to-left swipe (instead of a left-to-right * swipe). Loading packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +1 −0 Original line number Diff line number Diff line Loading @@ -126,6 +126,7 @@ public class SecureSettings { Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, Settings.Secure.SHOW_NOTIFICATION_SNOOZE, Settings.Secure.NOTIFICATION_HISTORY_ENABLED, Settings.Secure.ZEN_DURATION, Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, Settings.Secure.SHOW_ZEN_SETTINGS_SUGGESTION, Loading packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +1 −0 Original line number Diff line number Diff line Loading @@ -182,6 +182,7 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.SHOW_NOTIFICATION_SNOOZE, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.NOTIFICATION_HISTORY_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ZEN_DURATION, ANY_INTEGER_VALIDATOR); VALIDATORS.put(Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.SHOW_ZEN_SETTINGS_SUGGESTION, BOOLEAN_VALIDATOR); Loading services/core/java/com/android/server/notification/NotificationHistoryDatabase.java +8 −0 Original line number Diff line number Diff line Loading @@ -223,6 +223,13 @@ public class NotificationHistoryDatabase { } } public void disableHistory() { synchronized (mLock) { mHistoryDir.delete(); mHistoryFiles.clear(); } } /** * Remove any files that are too old and schedule jobs to clean up the rest */ Loading @@ -241,6 +248,7 @@ public class NotificationHistoryDatabase { Slog.d(TAG, "Removed " + currentOldestFile.getBaseFile().getName()); } currentOldestFile.delete(); // TODO: delete all relevant bitmaps, once they exist mHistoryFiles.removeLast(); } else { // all remaining files are newer than the cut off; schedule jobs to delete Loading services/core/java/com/android/server/notification/NotificationHistoryManager.java +98 −10 Original line number Diff line number Diff line Loading @@ -21,9 +21,16 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.NotificationHistory; import android.app.NotificationHistory.HistoricalNotification; import android.content.ContentResolver; import android.content.Context; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.net.Uri; import android.os.Environment; import android.os.Handler; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; Loading @@ -49,6 +56,8 @@ public class NotificationHistoryManager { private final Context mContext; private final UserManager mUserManager; @VisibleForTesting final SettingsObserver mSettingsObserver; private final Object mLock = new Object(); @GuardedBy("mLock") private final SparseArray<NotificationHistoryDatabase> mUserState = new SparseArray<>(); Loading @@ -57,19 +66,26 @@ public class NotificationHistoryManager { // TODO: does this need to be persisted across reboots? @GuardedBy("mLock") private final SparseArray<List<String>> mUserPendingPackageRemovals = new SparseArray<>(); @GuardedBy("mLock") private final SparseBooleanArray mHistoryEnabled = new SparseBooleanArray(); public NotificationHistoryManager(Context context) { public NotificationHistoryManager(Context context, Handler handler) { mContext = context; mUserManager = context.getSystemService(UserManager.class); mSettingsObserver = new SettingsObserver(handler); } public void onUserUnlocked(@UserIdInt int userId) { void onBootPhaseAppsCanStart() { mSettingsObserver.observe(); } void onUserUnlocked(@UserIdInt int userId) { synchronized (mLock) { mUserUnlockedStates.put(userId, true); final NotificationHistoryDatabase userHistory = getUserHistoryAndInitializeIfNeededLocked(userId); if (userHistory == null) { Slog.i(TAG, "Attempted to unlock stopped or removed user " + userId); Slog.i(TAG, "Attempted to unlock gone/disabled user " + userId); return; } Loading @@ -81,6 +97,11 @@ public class NotificationHistoryManager { } mUserPendingPackageRemovals.put(userId, null); } // delete history if it was disabled when the user was locked if (!mHistoryEnabled.get(userId)) { userHistory.disableHistory(); } } } Loading @@ -96,6 +117,7 @@ public class NotificationHistoryManager { // Actual data deletion is handled by other parts of the system (the entire directory is // removed) - we just need clean up our internal state for GC mUserPendingPackageRemovals.put(userId, null); mHistoryEnabled.put(userId, false); onUserStopped(userId); } } Loading @@ -103,10 +125,12 @@ public class NotificationHistoryManager { void onPackageRemoved(int userId, String packageName) { synchronized (mLock) { if (!mUserUnlockedStates.get(userId, false)) { if (mHistoryEnabled.get(userId, false)) { List<String> userPendingRemovals = mUserPendingPackageRemovals.get(userId, new ArrayList<>()); userPendingRemovals.add(packageName); mUserPendingPackageRemovals.put(userId, userPendingRemovals); } return; } final NotificationHistoryDatabase userHistory = mUserState.get(userId); Loading Loading @@ -139,7 +163,7 @@ public class NotificationHistoryManager { final NotificationHistoryDatabase userHistory = getUserHistoryAndInitializeIfNeededLocked(notification.getUserId()); if (userHistory == null) { Slog.w(TAG, "Attempted to add notif for locked/gone user " Slog.w(TAG, "Attempted to add notif for locked/gone/disabled user " + notification.getUserId()); return; } Loading @@ -157,7 +181,7 @@ public class NotificationHistoryManager { final NotificationHistoryDatabase userHistory = getUserHistoryAndInitializeIfNeededLocked(userId); if (userHistory == null) { Slog.i(TAG, "Attempted to read history for locked/gone user " +userId); Slog.i(TAG, "Attempted to read history for locked/gone/disabled user " +userId); continue; } mergedHistory.addNotificationsToWrite(userHistory.readNotificationHistory()); Loading @@ -172,7 +196,7 @@ public class NotificationHistoryManager { final NotificationHistoryDatabase userHistory = getUserHistoryAndInitializeIfNeededLocked(userId); if (userHistory == null) { Slog.i(TAG, "Attempted to read history for locked/gone user " +userId); Slog.i(TAG, "Attempted to read history for locked/gone/disabled user " +userId); return new android.app.NotificationHistory(); } Loading @@ -180,9 +204,38 @@ public class NotificationHistoryManager { } } public boolean isHistoryEnabled(@UserIdInt int userId) { synchronized (mLock) { return mHistoryEnabled.get(userId); } } void onHistoryEnabledChanged(@UserIdInt int userId, boolean historyEnabled) { synchronized (mLock) { mHistoryEnabled.put(userId, historyEnabled); // These requests might fail if the user is locked; onUserUnlocked will pick up those // cases final NotificationHistoryDatabase userHistory = getUserHistoryAndInitializeIfNeededLocked(userId); if (userHistory != null) { if (!historyEnabled) { userHistory.disableHistory(); } } } } @GuardedBy("mLock") private @Nullable NotificationHistoryDatabase getUserHistoryAndInitializeIfNeededLocked( int userId) { if (!mHistoryEnabled.get(userId)) { if (DEBUG) { Slog.i(TAG, "History disabled for user " + userId); } mUserState.put(userId, null); return null; } NotificationHistoryDatabase userHistory = mUserState.get(userId); if (userHistory == null) { final File historyDir = new File(Environment.getDataSystemCeDirectory(userId), Loading Loading @@ -242,4 +295,39 @@ public class NotificationHistoryManager { return mUserPendingPackageRemovals.get(userId); } } final class SettingsObserver extends ContentObserver { private final Uri NOTIFICATION_HISTORY_URI = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_HISTORY_ENABLED); SettingsObserver(Handler handler) { super(handler); } void observe() { ContentResolver resolver = mContext.getContentResolver(); resolver.registerContentObserver(NOTIFICATION_HISTORY_URI, false, this, UserHandle.USER_ALL); synchronized (mLock) { for (UserInfo userInfo : mUserManager.getUsers()) { update(null, userInfo.id); } } } @Override public void onChange(boolean selfChange, Uri uri, int userId) { update(uri, userId); } public void update(Uri uri, int userId) { ContentResolver resolver = mContext.getContentResolver(); if (uri == null || NOTIFICATION_HISTORY_URI.equals(uri)) { boolean historyEnabled = Settings.Secure.getIntForUser(resolver, Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, userId) != 0; onHistoryEnabledChanged(userId, historyEnabled); } } } } Loading
core/java/android/provider/Settings.java +9 −0 Original line number Diff line number Diff line Loading @@ -8155,6 +8155,15 @@ public final class Settings { @TestApi public static final String NOTIFICATION_BADGING = "notification_badging"; /** * When enabled the system will maintain a rolling history of received notifications. When * disabled the history will be disabled and deleted. * * The value 1 - enable, 0 - disable * @hide */ public static final String NOTIFICATION_HISTORY_ENABLED = "notification_history_enabled"; /** * Whether notifications are dismissed by a right-to-left swipe (instead of a left-to-right * swipe). Loading
packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java +1 −0 Original line number Diff line number Diff line Loading @@ -126,6 +126,7 @@ public class SecureSettings { Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, Settings.Secure.SHOW_NOTIFICATION_SNOOZE, Settings.Secure.NOTIFICATION_HISTORY_ENABLED, Settings.Secure.ZEN_DURATION, Settings.Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, Settings.Secure.SHOW_ZEN_SETTINGS_SUGGESTION, Loading
packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java +1 −0 Original line number Diff line number Diff line Loading @@ -182,6 +182,7 @@ public class SecureSettingsValidators { VALIDATORS.put(Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.SHOW_NOTIFICATION_SNOOZE, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.NOTIFICATION_HISTORY_ENABLED, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.ZEN_DURATION, ANY_INTEGER_VALIDATOR); VALIDATORS.put(Secure.SHOW_ZEN_UPGRADE_NOTIFICATION, BOOLEAN_VALIDATOR); VALIDATORS.put(Secure.SHOW_ZEN_SETTINGS_SUGGESTION, BOOLEAN_VALIDATOR); Loading
services/core/java/com/android/server/notification/NotificationHistoryDatabase.java +8 −0 Original line number Diff line number Diff line Loading @@ -223,6 +223,13 @@ public class NotificationHistoryDatabase { } } public void disableHistory() { synchronized (mLock) { mHistoryDir.delete(); mHistoryFiles.clear(); } } /** * Remove any files that are too old and schedule jobs to clean up the rest */ Loading @@ -241,6 +248,7 @@ public class NotificationHistoryDatabase { Slog.d(TAG, "Removed " + currentOldestFile.getBaseFile().getName()); } currentOldestFile.delete(); // TODO: delete all relevant bitmaps, once they exist mHistoryFiles.removeLast(); } else { // all remaining files are newer than the cut off; schedule jobs to delete Loading
services/core/java/com/android/server/notification/NotificationHistoryManager.java +98 −10 Original line number Diff line number Diff line Loading @@ -21,9 +21,16 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.NotificationHistory; import android.app.NotificationHistory.HistoricalNotification; import android.content.ContentResolver; import android.content.Context; import android.content.pm.UserInfo; import android.database.ContentObserver; import android.net.Uri; import android.os.Environment; import android.os.Handler; import android.os.UserHandle; import android.os.UserManager; import android.provider.Settings; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; Loading @@ -49,6 +56,8 @@ public class NotificationHistoryManager { private final Context mContext; private final UserManager mUserManager; @VisibleForTesting final SettingsObserver mSettingsObserver; private final Object mLock = new Object(); @GuardedBy("mLock") private final SparseArray<NotificationHistoryDatabase> mUserState = new SparseArray<>(); Loading @@ -57,19 +66,26 @@ public class NotificationHistoryManager { // TODO: does this need to be persisted across reboots? @GuardedBy("mLock") private final SparseArray<List<String>> mUserPendingPackageRemovals = new SparseArray<>(); @GuardedBy("mLock") private final SparseBooleanArray mHistoryEnabled = new SparseBooleanArray(); public NotificationHistoryManager(Context context) { public NotificationHistoryManager(Context context, Handler handler) { mContext = context; mUserManager = context.getSystemService(UserManager.class); mSettingsObserver = new SettingsObserver(handler); } public void onUserUnlocked(@UserIdInt int userId) { void onBootPhaseAppsCanStart() { mSettingsObserver.observe(); } void onUserUnlocked(@UserIdInt int userId) { synchronized (mLock) { mUserUnlockedStates.put(userId, true); final NotificationHistoryDatabase userHistory = getUserHistoryAndInitializeIfNeededLocked(userId); if (userHistory == null) { Slog.i(TAG, "Attempted to unlock stopped or removed user " + userId); Slog.i(TAG, "Attempted to unlock gone/disabled user " + userId); return; } Loading @@ -81,6 +97,11 @@ public class NotificationHistoryManager { } mUserPendingPackageRemovals.put(userId, null); } // delete history if it was disabled when the user was locked if (!mHistoryEnabled.get(userId)) { userHistory.disableHistory(); } } } Loading @@ -96,6 +117,7 @@ public class NotificationHistoryManager { // Actual data deletion is handled by other parts of the system (the entire directory is // removed) - we just need clean up our internal state for GC mUserPendingPackageRemovals.put(userId, null); mHistoryEnabled.put(userId, false); onUserStopped(userId); } } Loading @@ -103,10 +125,12 @@ public class NotificationHistoryManager { void onPackageRemoved(int userId, String packageName) { synchronized (mLock) { if (!mUserUnlockedStates.get(userId, false)) { if (mHistoryEnabled.get(userId, false)) { List<String> userPendingRemovals = mUserPendingPackageRemovals.get(userId, new ArrayList<>()); userPendingRemovals.add(packageName); mUserPendingPackageRemovals.put(userId, userPendingRemovals); } return; } final NotificationHistoryDatabase userHistory = mUserState.get(userId); Loading Loading @@ -139,7 +163,7 @@ public class NotificationHistoryManager { final NotificationHistoryDatabase userHistory = getUserHistoryAndInitializeIfNeededLocked(notification.getUserId()); if (userHistory == null) { Slog.w(TAG, "Attempted to add notif for locked/gone user " Slog.w(TAG, "Attempted to add notif for locked/gone/disabled user " + notification.getUserId()); return; } Loading @@ -157,7 +181,7 @@ public class NotificationHistoryManager { final NotificationHistoryDatabase userHistory = getUserHistoryAndInitializeIfNeededLocked(userId); if (userHistory == null) { Slog.i(TAG, "Attempted to read history for locked/gone user " +userId); Slog.i(TAG, "Attempted to read history for locked/gone/disabled user " +userId); continue; } mergedHistory.addNotificationsToWrite(userHistory.readNotificationHistory()); Loading @@ -172,7 +196,7 @@ public class NotificationHistoryManager { final NotificationHistoryDatabase userHistory = getUserHistoryAndInitializeIfNeededLocked(userId); if (userHistory == null) { Slog.i(TAG, "Attempted to read history for locked/gone user " +userId); Slog.i(TAG, "Attempted to read history for locked/gone/disabled user " +userId); return new android.app.NotificationHistory(); } Loading @@ -180,9 +204,38 @@ public class NotificationHistoryManager { } } public boolean isHistoryEnabled(@UserIdInt int userId) { synchronized (mLock) { return mHistoryEnabled.get(userId); } } void onHistoryEnabledChanged(@UserIdInt int userId, boolean historyEnabled) { synchronized (mLock) { mHistoryEnabled.put(userId, historyEnabled); // These requests might fail if the user is locked; onUserUnlocked will pick up those // cases final NotificationHistoryDatabase userHistory = getUserHistoryAndInitializeIfNeededLocked(userId); if (userHistory != null) { if (!historyEnabled) { userHistory.disableHistory(); } } } } @GuardedBy("mLock") private @Nullable NotificationHistoryDatabase getUserHistoryAndInitializeIfNeededLocked( int userId) { if (!mHistoryEnabled.get(userId)) { if (DEBUG) { Slog.i(TAG, "History disabled for user " + userId); } mUserState.put(userId, null); return null; } NotificationHistoryDatabase userHistory = mUserState.get(userId); if (userHistory == null) { final File historyDir = new File(Environment.getDataSystemCeDirectory(userId), Loading Loading @@ -242,4 +295,39 @@ public class NotificationHistoryManager { return mUserPendingPackageRemovals.get(userId); } } final class SettingsObserver extends ContentObserver { private final Uri NOTIFICATION_HISTORY_URI = Settings.Secure.getUriFor(Settings.Secure.NOTIFICATION_HISTORY_ENABLED); SettingsObserver(Handler handler) { super(handler); } void observe() { ContentResolver resolver = mContext.getContentResolver(); resolver.registerContentObserver(NOTIFICATION_HISTORY_URI, false, this, UserHandle.USER_ALL); synchronized (mLock) { for (UserInfo userInfo : mUserManager.getUsers()) { update(null, userInfo.id); } } } @Override public void onChange(boolean selfChange, Uri uri, int userId) { update(uri, userId); } public void update(Uri uri, int userId) { ContentResolver resolver = mContext.getContentResolver(); if (uri == null || NOTIFICATION_HISTORY_URI.equals(uri)) { boolean historyEnabled = Settings.Secure.getIntForUser(resolver, Settings.Secure.NOTIFICATION_HISTORY_ENABLED, 0, userId) != 0; onHistoryEnabledChanged(userId, historyEnabled); } } } }