Merge "Add multiuser support for notification history"

This commit is contained in:
TreeHugger Robot
2019-11-19 20:20:28 +00:00
committed by Android (Google) Code Review
7 changed files with 647 additions and 16 deletions

View File

@@ -311,6 +311,14 @@ public final class NotificationHistory implements Parcelable {
mHistoryCount++;
}
public void addNotificationsToWrite(@NonNull NotificationHistory notificationHistory) {
for (HistoricalNotification hn : notificationHistory.getNotificationsToWrite()) {
// TODO: consider merging by date
addNotificationToWrite(hn);
}
poolStringsFromNotifications();
}
/**
* Removes a package's historical notifications and regenerates the string pool
*/

View File

@@ -113,6 +113,33 @@ public class NotificationHistoryTest {
assertThat(history.getHistoryCount()).isEqualTo(2);
}
@Test
public void testAddNotificationsToWrite() {
NotificationHistory history = new NotificationHistory();
HistoricalNotification n = getHistoricalNotification(0);
HistoricalNotification n2 = getHistoricalNotification(1);
history.addNotificationToWrite(n2);
history.addNotificationToWrite(n);
NotificationHistory secondHistory = new NotificationHistory();
HistoricalNotification n3 = getHistoricalNotification(2);
HistoricalNotification n4 = getHistoricalNotification(3);
secondHistory.addNotificationToWrite(n4);
secondHistory.addNotificationToWrite(n3);
history.addNotificationsToWrite(secondHistory);
assertThat(history.getNotificationsToWrite().size()).isEqualTo(4);
assertThat(history.getNotificationsToWrite().get(0)).isSameAs(n2);
assertThat(history.getNotificationsToWrite().get(1)).isSameAs(n);
assertThat(history.getNotificationsToWrite().get(2)).isSameAs(n4);
assertThat(history.getNotificationsToWrite().get(3)).isSameAs(n3);
assertThat(history.getHistoryCount()).isEqualTo(4);
assertThat(history.getPooledStringsToWrite()).asList().contains(n2.getChannelName());
assertThat(history.getPooledStringsToWrite()).asList().contains(n4.getPackage());
}
@Test
public void testPoolStringsFromNotifications() {
NotificationHistory history = new NotificationHistory();

View File

@@ -75,7 +75,7 @@ public class NotificationHistoryDatabase {
private final Context mContext;
private final AlarmManager mAlarmManager;
private final Object mLock = new Object();
private Handler mFileWriteHandler;
private final Handler mFileWriteHandler;
@VisibleForTesting
// List of files holding history information, sorted newest to oldest
final LinkedList<AtomicFile> mHistoryFiles;
@@ -90,11 +90,12 @@ public class NotificationHistoryDatabase {
@VisibleForTesting
NotificationHistory mBuffer;
public NotificationHistoryDatabase(Context context, File dir,
public NotificationHistoryDatabase(Context context, Handler fileWriteHandler, File dir,
FileAttrProvider fileAttrProvider) {
mContext = context;
mAlarmManager = context.getSystemService(AlarmManager.class);
mCurrentVersion = DEFAULT_CURRENT_VERSION;
mFileWriteHandler = fileWriteHandler;
mVersionFile = new File(dir, "version");
mHistoryDir = new File(dir, "history");
mHistoryFiles = new LinkedList<>();
@@ -107,10 +108,8 @@ public class NotificationHistoryDatabase {
mContext.registerReceiver(mFileCleaupReceiver, deletionFilter);
}
public void init(Handler fileWriteHandler) {
public void init() {
synchronized (mLock) {
mFileWriteHandler = fileWriteHandler;
try {
mHistoryDir.mkdir();
mVersionFile.createNewFile();
@@ -160,13 +159,13 @@ public class NotificationHistoryDatabase {
}
}
void forceWriteToDisk() {
public void forceWriteToDisk() {
if (!mFileWriteHandler.hasCallbacks(mWriteBufferRunnable)) {
mFileWriteHandler.post(mWriteBufferRunnable);
}
}
void onPackageRemoved(String packageName) {
public void onPackageRemoved(String packageName) {
RemovePackageRunnable rpr = new RemovePackageRunnable(packageName);
mFileWriteHandler.post(rpr);
}
@@ -227,7 +226,7 @@ public class NotificationHistoryDatabase {
/**
* Remove any files that are too old and schedule jobs to clean up the rest
*/
public void prune(final int retentionDays, final long currentTimeMillis) {
void prune(final int retentionDays, final long currentTimeMillis) {
synchronized (mLock) {
GregorianCalendar retentionBoundary = new GregorianCalendar();
retentionBoundary.setTimeInMillis(currentTimeMillis);
@@ -252,7 +251,7 @@ public class NotificationHistoryDatabase {
}
}
void scheduleDeletion(File file, long deletionTime) {
private void scheduleDeletion(File file, long deletionTime) {
if (DEBUG) {
Slog.d(TAG, "Scheduling deletion for " + file.getName() + " at " + deletionTime);
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.notification;
import android.annotation.NonNull;
import android.content.Context;
import android.os.Handler;
import java.io.File;
public class NotificationHistoryDatabaseFactory {
private static NotificationHistoryDatabase sTestingNotificationHistoryDb;
public static void setTestingNotificationHistoryDatabase(NotificationHistoryDatabase db) {
sTestingNotificationHistoryDb = db;
}
public static NotificationHistoryDatabase create(@NonNull Context context,
@NonNull Handler handler, @NonNull File rootDir,
@NonNull NotificationHistoryDatabase.FileAttrProvider fileAttrProvider) {
if(sTestingNotificationHistoryDb != null) {
return sTestingNotificationHistoryDb;
}
return new NotificationHistoryDatabase(context, handler, rootDir, fileAttrProvider);
}
}

View File

@@ -0,0 +1,245 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.notification;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.app.NotificationHistory;
import android.app.NotificationHistory.HistoricalNotification;
import android.content.Context;
import android.os.Environment;
import android.os.UserManager;
import android.util.Slog;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.IoThread;
import com.android.server.notification.NotificationHistoryDatabase.NotificationHistoryFileAttrProvider;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
/**
* Keeps track of per-user notification histories.
*/
public class NotificationHistoryManager {
private static final String TAG = "NotificationHistory";
private static final boolean DEBUG = NotificationManagerService.DBG;
@VisibleForTesting
static final String DIRECTORY_PER_USER = "notification_history";
private final Context mContext;
private final UserManager mUserManager;
private final Object mLock = new Object();
@GuardedBy("mLock")
private final SparseArray<NotificationHistoryDatabase> mUserState = new SparseArray<>();
@GuardedBy("mLock")
private final SparseBooleanArray mUserUnlockedStates = new SparseBooleanArray();
// TODO: does this need to be persisted across reboots?
@GuardedBy("mLock")
private final SparseArray<List<String>> mUserPendingPackageRemovals = new SparseArray<>();
public NotificationHistoryManager(Context context) {
mContext = context;
mUserManager = context.getSystemService(UserManager.class);
}
public 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);
return;
}
// remove any packages that were deleted while the user was locked
final List<String> pendingPackageRemovals = mUserPendingPackageRemovals.get(userId);
if (pendingPackageRemovals != null) {
for (int i = 0; i < pendingPackageRemovals.size(); i++) {
userHistory.onPackageRemoved(pendingPackageRemovals.get(i));
}
mUserPendingPackageRemovals.put(userId, null);
}
}
}
public void onUserStopped(@UserIdInt int userId) {
synchronized (mLock) {
mUserUnlockedStates.put(userId, false);
mUserState.put(userId, null); // release the service (mainly for GC)
}
}
void onUserRemoved(@UserIdInt int userId) {
synchronized (mLock) {
// 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);
onUserStopped(userId);
}
}
void onPackageRemoved(int userId, String packageName) {
synchronized (mLock) {
if (!mUserUnlockedStates.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);
if (userHistory == null) {
return;
}
userHistory.onPackageRemoved(packageName);
}
}
void triggerWriteToDisk() {
synchronized (mLock) {
final int userCount = mUserState.size();
for (int i = 0; i < userCount; i++) {
final int userId = mUserState.keyAt(i);
if (!mUserUnlockedStates.get(userId)) {
continue;
}
NotificationHistoryDatabase userHistory = mUserState.get(userId);
if (userHistory != null) {
userHistory.forceWriteToDisk();
}
}
}
}
public void addNotification(@NonNull final HistoricalNotification notification) {
synchronized (mLock) {
final NotificationHistoryDatabase userHistory =
getUserHistoryAndInitializeIfNeededLocked(notification.getUserId());
if (userHistory == null) {
Slog.w(TAG, "Attempted to add notif for locked/gone user "
+ notification.getUserId());
return;
}
userHistory.addNotification(notification);
}
}
public @NonNull NotificationHistory readNotificationHistory(@UserIdInt int[] userIds) {
synchronized (mLock) {
NotificationHistory mergedHistory = new NotificationHistory();
if (userIds == null) {
return mergedHistory;
}
for (int userId : userIds) {
final NotificationHistoryDatabase userHistory =
getUserHistoryAndInitializeIfNeededLocked(userId);
if (userHistory == null) {
Slog.i(TAG, "Attempted to read history for locked/gone user " +userId);
continue;
}
mergedHistory.addNotificationsToWrite(userHistory.readNotificationHistory());
}
return mergedHistory;
}
}
public @NonNull android.app.NotificationHistory readFilteredNotificationHistory(
@UserIdInt int userId, String packageName, String channelId, int maxNotifications) {
synchronized (mLock) {
final NotificationHistoryDatabase userHistory =
getUserHistoryAndInitializeIfNeededLocked(userId);
if (userHistory == null) {
Slog.i(TAG, "Attempted to read history for locked/gone user " +userId);
return new android.app.NotificationHistory();
}
return userHistory.readNotificationHistory(packageName, channelId, maxNotifications);
}
}
@GuardedBy("mLock")
private @Nullable NotificationHistoryDatabase getUserHistoryAndInitializeIfNeededLocked(
int userId) {
NotificationHistoryDatabase userHistory = mUserState.get(userId);
if (userHistory == null) {
final File historyDir = new File(Environment.getDataSystemCeDirectory(userId),
DIRECTORY_PER_USER);
userHistory = NotificationHistoryDatabaseFactory.create(mContext, IoThread.getHandler(),
historyDir, new NotificationHistoryFileAttrProvider());
if (mUserUnlockedStates.get(userId)) {
try {
userHistory.init();
} catch (Exception e) {
if (mUserManager.isUserUnlocked(userId)) {
throw e; // rethrow exception - user is unlocked
} else {
Slog.w(TAG, "Attempted to initialize service for "
+ "stopped or removed user " + userId);
return null;
}
}
} else {
// locked! data unavailable
Slog.w(TAG, "Attempted to initialize service for "
+ "stopped or removed user " + userId);
return null;
}
mUserState.put(userId, userHistory);
}
return userHistory;
}
@VisibleForTesting
boolean isUserUnlocked(@UserIdInt int userId) {
synchronized (mLock) {
return mUserUnlockedStates.get(userId);
}
}
@VisibleForTesting
boolean doesHistoryExistForUser(@UserIdInt int userId) {
synchronized (mLock) {
return mUserState.get(userId) != null;
}
}
@VisibleForTesting
void replaceNotificationHistoryDatabase(@UserIdInt int userId,
NotificationHistoryDatabase replacement) {
synchronized (mLock) {
if (mUserState.get(userId) != null) {
mUserState.put(userId, replacement);
}
}
}
@VisibleForTesting
List<String> getPendingPackageRemovalsForUser(@UserIdInt int userId) {
synchronized (mLock) {
return mUserPendingPackageRemovals.get(userId);
}
}
}

View File

@@ -45,11 +45,6 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.io.File;
import java.nio.file.FileSystem;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
@@ -109,8 +104,9 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase {
mFileAttrProvider = new TestFileAttrProvider();
mRootDir = new File(mContext.getFilesDir(), "NotificationHistoryDatabaseTest");
mDataBase = new NotificationHistoryDatabase(mContext, mRootDir, mFileAttrProvider);
mDataBase.init(mFileWriteHandler);
mDataBase = new NotificationHistoryDatabase(
mContext, mFileWriteHandler, mRootDir, mFileAttrProvider);
mDataBase.init();
}
@Test

View File

@@ -0,0 +1,315 @@
/*
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.notification;
import static android.os.UserHandle.USER_ALL;
import static android.os.UserHandle.USER_SYSTEM;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.app.NotificationHistory;
import android.app.NotificationHistory.HistoricalNotification;
import android.content.Context;
import android.graphics.drawable.Icon;
import android.os.UserManager;
import androidx.test.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.android.server.UiServiceTestCase;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
@RunWith(AndroidJUnit4.class)
public class NotificationHistoryManagerTest extends UiServiceTestCase {
@Mock
Context mContext;
@Mock
UserManager mUserManager;
@Mock
NotificationHistoryDatabase mDb;
NotificationHistoryManager mHistoryManager;
private HistoricalNotification getHistoricalNotification(int index) {
return getHistoricalNotification("package" + index, index);
}
private HistoricalNotification getHistoricalNotification(String packageName, int index) {
String expectedChannelName = "channelName" + index;
String expectedChannelId = "channelId" + index;
int expectedUid = 1123456 + index;
int expectedUserId = index;
long expectedPostTime = 987654321 + index;
String expectedTitle = "title" + index;
String expectedText = "text" + index;
Icon expectedIcon = Icon.createWithResource(InstrumentationRegistry.getContext(),
index);
return new HistoricalNotification.Builder()
.setPackage(packageName)
.setChannelName(expectedChannelName)
.setChannelId(expectedChannelId)
.setUid(expectedUid)
.setUserId(expectedUserId)
.setPostedTimeMs(expectedPostTime)
.setTitle(expectedTitle)
.setText(expectedText)
.setIcon(expectedIcon)
.build();
}
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
when(mContext.getSystemService(UserManager.class)).thenReturn(mUserManager);
when(mContext.getUser()).thenReturn(getContext().getUser());
when(mContext.getPackageName()).thenReturn(getContext().getPackageName());
NotificationHistoryDatabaseFactory.setTestingNotificationHistoryDatabase(mDb);
mHistoryManager = new NotificationHistoryManager(mContext);
}
@Test
public void testOnUserUnlocked() {
assertThat(mHistoryManager.doesHistoryExistForUser(USER_SYSTEM)).isFalse();
assertThat(mHistoryManager.isUserUnlocked(USER_SYSTEM)).isFalse();
mHistoryManager.onUserUnlocked(USER_SYSTEM);
assertThat(mHistoryManager.doesHistoryExistForUser(USER_SYSTEM)).isTrue();
assertThat(mHistoryManager.isUserUnlocked(USER_SYSTEM)).isTrue();
verify(mDb, times(1)).init();
}
@Test
public void testOnUserUnlocked_cleansUpRemovedPackages() {
String pkg = "pkg";
mHistoryManager.onPackageRemoved(USER_SYSTEM, pkg);
assertThat(mHistoryManager.doesHistoryExistForUser(USER_SYSTEM)).isFalse();
mHistoryManager.onUserUnlocked(USER_SYSTEM);
assertThat(mHistoryManager.doesHistoryExistForUser(USER_SYSTEM)).isTrue();
assertThat(mHistoryManager.isUserUnlocked(USER_SYSTEM)).isTrue();
verify(mDb, times(1)).onPackageRemoved(pkg);
}
@Test
public void testOnUserStopped_userExists() {
mHistoryManager.onUserUnlocked(USER_SYSTEM);
mHistoryManager.onUserStopped(USER_SYSTEM);
assertThat(mHistoryManager.doesHistoryExistForUser(USER_SYSTEM)).isFalse();
assertThat(mHistoryManager.isUserUnlocked(USER_SYSTEM)).isFalse();
}
@Test
public void testOnUserStopped_userDoesNotExist() {
mHistoryManager.onUserStopped(USER_SYSTEM);
// no crash
assertThat(mHistoryManager.doesHistoryExistForUser(USER_SYSTEM)).isFalse();
assertThat(mHistoryManager.isUserUnlocked(USER_SYSTEM)).isFalse();
}
@Test
public void testOnUserRemoved_userExists() {
mHistoryManager.onUserUnlocked(USER_SYSTEM);
mHistoryManager.onUserRemoved(USER_SYSTEM);
assertThat(mHistoryManager.doesHistoryExistForUser(USER_SYSTEM)).isFalse();
assertThat(mHistoryManager.isUserUnlocked(USER_SYSTEM)).isFalse();
}
@Test
public void testOnUserRemoved_userDoesNotExist() {
mHistoryManager.onUserRemoved(USER_SYSTEM);
// no crash
assertThat(mHistoryManager.doesHistoryExistForUser(USER_SYSTEM)).isFalse();
assertThat(mHistoryManager.isUserUnlocked(USER_SYSTEM)).isFalse();
}
@Test
public void testOnUserRemoved_cleanupPendingPackages() {
mHistoryManager.onUserUnlocked(USER_SYSTEM);
mHistoryManager.onUserStopped(USER_SYSTEM);
String pkg = "pkg";
mHistoryManager.onPackageRemoved(USER_SYSTEM, pkg);
mHistoryManager.onUserRemoved(USER_SYSTEM);
assertThat(mHistoryManager.getPendingPackageRemovalsForUser(USER_SYSTEM)).isNull();
}
@Test
public void testOnPackageRemoved_userUnlocked() {
String pkg = "pkg";
NotificationHistoryDatabase userHistory = mock(NotificationHistoryDatabase.class);
mHistoryManager.onUserUnlocked(USER_SYSTEM);
mHistoryManager.replaceNotificationHistoryDatabase(USER_SYSTEM, userHistory);
mHistoryManager.onPackageRemoved(USER_SYSTEM, pkg);
verify(userHistory, times(1)).onPackageRemoved(pkg);
}
@Test
public void testOnPackageRemoved_userLocked() {
String pkg = "pkg";
mHistoryManager.onPackageRemoved(USER_SYSTEM, pkg);
assertThat(mHistoryManager.getPendingPackageRemovalsForUser(USER_SYSTEM)).contains(pkg);
}
@Test
public void testOnPackageRemoved_multiUser() {
String pkg = "pkg";
NotificationHistoryDatabase userHistorySystem = mock(NotificationHistoryDatabase.class);
NotificationHistoryDatabase userHistoryAll = mock(NotificationHistoryDatabase.class);
mHistoryManager.onUserUnlocked(USER_SYSTEM);
mHistoryManager.replaceNotificationHistoryDatabase(USER_SYSTEM, userHistorySystem);
mHistoryManager.onUserUnlocked(USER_ALL);
mHistoryManager.replaceNotificationHistoryDatabase(USER_ALL, userHistoryAll);
mHistoryManager.onPackageRemoved(USER_SYSTEM, pkg);
verify(userHistorySystem, times(1)).onPackageRemoved(pkg);
verify(userHistoryAll, never()).onPackageRemoved(pkg);
}
@Test
public void testTriggerWriteToDisk() {
NotificationHistoryDatabase userHistorySystem = mock(NotificationHistoryDatabase.class);
NotificationHistoryDatabase userHistoryAll = mock(NotificationHistoryDatabase.class);
mHistoryManager.onUserUnlocked(USER_SYSTEM);
mHistoryManager.replaceNotificationHistoryDatabase(USER_SYSTEM, userHistorySystem);
mHistoryManager.onUserUnlocked(USER_ALL);
mHistoryManager.replaceNotificationHistoryDatabase(USER_ALL, userHistoryAll);
mHistoryManager.triggerWriteToDisk();
verify(userHistorySystem, times(1)).forceWriteToDisk();
verify(userHistoryAll, times(1)).forceWriteToDisk();
}
@Test
public void testTriggerWriteToDisk_onlyUnlockedUsers() {
NotificationHistoryDatabase userHistorySystem = mock(NotificationHistoryDatabase.class);
NotificationHistoryDatabase userHistoryAll = mock(NotificationHistoryDatabase.class);
mHistoryManager.onUserUnlocked(USER_SYSTEM);
mHistoryManager.replaceNotificationHistoryDatabase(USER_SYSTEM, userHistorySystem);
mHistoryManager.onUserUnlocked(USER_ALL);
mHistoryManager.replaceNotificationHistoryDatabase(USER_ALL, userHistoryAll);
mHistoryManager.onUserStopped(USER_ALL);
mHistoryManager.triggerWriteToDisk();
verify(userHistorySystem, times(1)).forceWriteToDisk();
verify(userHistoryAll, never()).forceWriteToDisk();
}
@Test
public void testAddNotification_userLocked_noCrash() {
HistoricalNotification hn = getHistoricalNotification("pkg", 1);
mHistoryManager.addNotification(hn);
}
@Test
public void testAddNotification() {
HistoricalNotification hnSystem = getHistoricalNotification("pkg", USER_SYSTEM);
HistoricalNotification hnAll = getHistoricalNotification("pkg", USER_ALL);
NotificationHistoryDatabase userHistorySystem = mock(NotificationHistoryDatabase.class);
NotificationHistoryDatabase userHistoryAll = mock(NotificationHistoryDatabase.class);
mHistoryManager.onUserUnlocked(USER_SYSTEM);
mHistoryManager.replaceNotificationHistoryDatabase(USER_SYSTEM, userHistorySystem);
mHistoryManager.onUserUnlocked(USER_ALL);
mHistoryManager.replaceNotificationHistoryDatabase(USER_ALL, userHistoryAll);
mHistoryManager.addNotification(hnSystem);
mHistoryManager.addNotification(hnAll);
verify(userHistorySystem, times(1)).addNotification(hnSystem);
verify(userHistoryAll, times(1)).addNotification(hnAll);
}
@Test
public void testReadNotificationHistory() {
HistoricalNotification hnSystem = getHistoricalNotification("pkg", USER_SYSTEM);
HistoricalNotification hnAll = getHistoricalNotification("pkg", USER_ALL);
NotificationHistoryDatabase userHistorySystem = mock(NotificationHistoryDatabase.class);
NotificationHistoryDatabase userHistoryAll = mock(NotificationHistoryDatabase.class);
mHistoryManager.onUserUnlocked(USER_SYSTEM);
mHistoryManager.replaceNotificationHistoryDatabase(USER_SYSTEM, userHistorySystem);
NotificationHistory nhSystem = mock(NotificationHistory.class);
ArrayList<HistoricalNotification> nhSystemList = new ArrayList<>();
nhSystemList.add(hnSystem);
when(nhSystem.getNotificationsToWrite()).thenReturn(nhSystemList);
when(userHistorySystem.readNotificationHistory()).thenReturn(nhSystem);
mHistoryManager.onUserUnlocked(USER_ALL);
mHistoryManager.replaceNotificationHistoryDatabase(USER_ALL, userHistoryAll);
NotificationHistory nhAll = mock(NotificationHistory.class);
ArrayList<HistoricalNotification> nhAllList = new ArrayList<>();
nhAllList.add(hnAll);
when(nhAll.getNotificationsToWrite()).thenReturn(nhAllList);
when(userHistoryAll.readNotificationHistory()).thenReturn(nhAll);
// ensure read history returns both historical notifs
NotificationHistory nh = mHistoryManager.readNotificationHistory(
new int[] {USER_SYSTEM, USER_ALL});
assertThat(nh.getNotificationsToWrite()).contains(hnSystem);
assertThat(nh.getNotificationsToWrite()).contains(hnAll);
}
@Test
public void readFilteredNotificationHistory_userUnlocked() {
NotificationHistory nh =
mHistoryManager.readFilteredNotificationHistory(USER_SYSTEM, "", "", 1000);
assertThat(nh.getNotificationsToWrite()).isEmpty();
}
@Test
public void readFilteredNotificationHistory() {
mHistoryManager.onUserUnlocked(USER_SYSTEM);
mHistoryManager.readFilteredNotificationHistory(USER_SYSTEM, "pkg", "chn", 1000);
verify(mDb, times(1)).readNotificationHistory("pkg", "chn", 1000);
}
}