Merge "Add multiuser support for notification history"
This commit is contained in:
committed by
Android (Google) Code Review
commit
9d0b195b73
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user