Merge "Clean up history files via job vs multiple alarms" into tm-qpr-dev am: f552a18756
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/19263269 Change-Id: I5984c6a625ebf905e881593e26e4656076bc087f Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
@@ -7064,6 +7064,10 @@
|
|||||||
android:permission="android.permission.BIND_JOB_SERVICE">
|
android:permission="android.permission.BIND_JOB_SERVICE">
|
||||||
</service>
|
</service>
|
||||||
|
|
||||||
|
<service android:name="com.android.server.notification.NotificationHistoryJobService"
|
||||||
|
android:permission="android.permission.BIND_JOB_SERVICE" >
|
||||||
|
</service>
|
||||||
|
|
||||||
<service android:name="com.android.server.pm.PackageManagerShellCommandDataLoader"
|
<service android:name="com.android.server.pm.PackageManagerShellCommandDataLoader"
|
||||||
android:exported="false">
|
android:exported="false">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
|
|||||||
@@ -16,15 +16,8 @@
|
|||||||
|
|
||||||
package com.android.server.notification;
|
package com.android.server.notification;
|
||||||
|
|
||||||
import android.app.AlarmManager;
|
|
||||||
import android.app.NotificationHistory;
|
import android.app.NotificationHistory;
|
||||||
import android.app.NotificationHistory.HistoricalNotification;
|
import android.app.NotificationHistory.HistoricalNotification;
|
||||||
import android.app.PendingIntent;
|
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.IntentFilter;
|
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.util.AtomicFile;
|
import android.util.AtomicFile;
|
||||||
import android.util.Slog;
|
import android.util.Slog;
|
||||||
@@ -60,18 +53,9 @@ public class NotificationHistoryDatabase {
|
|||||||
private static final String TAG = "NotiHistoryDatabase";
|
private static final String TAG = "NotiHistoryDatabase";
|
||||||
private static final boolean DEBUG = NotificationManagerService.DBG;
|
private static final boolean DEBUG = NotificationManagerService.DBG;
|
||||||
private static final int HISTORY_RETENTION_DAYS = 1;
|
private static final int HISTORY_RETENTION_DAYS = 1;
|
||||||
private static final int HISTORY_RETENTION_MS = 24 * 60 * 60 * 1000;
|
|
||||||
private static final long WRITE_BUFFER_INTERVAL_MS = 1000 * 60 * 20;
|
private static final long WRITE_BUFFER_INTERVAL_MS = 1000 * 60 * 20;
|
||||||
private static final long INVALID_FILE_TIME_MS = -1;
|
private static final long INVALID_FILE_TIME_MS = -1;
|
||||||
|
|
||||||
private static final String ACTION_HISTORY_DELETION =
|
|
||||||
NotificationHistoryDatabase.class.getSimpleName() + ".CLEANUP";
|
|
||||||
private static final int REQUEST_CODE_DELETION = 1;
|
|
||||||
private static final String SCHEME_DELETION = "delete";
|
|
||||||
private static final String EXTRA_KEY = "key";
|
|
||||||
|
|
||||||
private final Context mContext;
|
|
||||||
private final AlarmManager mAlarmManager;
|
|
||||||
private final Object mLock = new Object();
|
private final Object mLock = new Object();
|
||||||
private final Handler mFileWriteHandler;
|
private final Handler mFileWriteHandler;
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
@@ -87,9 +71,7 @@ public class NotificationHistoryDatabase {
|
|||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
NotificationHistory mBuffer;
|
NotificationHistory mBuffer;
|
||||||
|
|
||||||
public NotificationHistoryDatabase(Context context, Handler fileWriteHandler, File dir) {
|
public NotificationHistoryDatabase(Handler fileWriteHandler, File dir) {
|
||||||
mContext = context;
|
|
||||||
mAlarmManager = context.getSystemService(AlarmManager.class);
|
|
||||||
mCurrentVersion = DEFAULT_CURRENT_VERSION;
|
mCurrentVersion = DEFAULT_CURRENT_VERSION;
|
||||||
mFileWriteHandler = fileWriteHandler;
|
mFileWriteHandler = fileWriteHandler;
|
||||||
mVersionFile = new File(dir, "version");
|
mVersionFile = new File(dir, "version");
|
||||||
@@ -97,11 +79,6 @@ public class NotificationHistoryDatabase {
|
|||||||
mHistoryFiles = new LinkedList<>();
|
mHistoryFiles = new LinkedList<>();
|
||||||
mBuffer = new NotificationHistory();
|
mBuffer = new NotificationHistory();
|
||||||
mWriteBufferRunnable = new WriteBufferRunnable();
|
mWriteBufferRunnable = new WriteBufferRunnable();
|
||||||
|
|
||||||
IntentFilter deletionFilter = new IntentFilter(ACTION_HISTORY_DELETION);
|
|
||||||
deletionFilter.addDataScheme(SCHEME_DELETION);
|
|
||||||
mContext.registerReceiver(mFileCleanupReceiver, deletionFilter,
|
|
||||||
Context.RECEIVER_EXPORTED_UNAUDITED);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init() {
|
public void init() {
|
||||||
@@ -117,7 +94,7 @@ public class NotificationHistoryDatabase {
|
|||||||
|
|
||||||
checkVersionAndBuildLocked();
|
checkVersionAndBuildLocked();
|
||||||
indexFilesLocked();
|
indexFilesLocked();
|
||||||
prune(HISTORY_RETENTION_DAYS, System.currentTimeMillis());
|
prune();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,7 +223,14 @@ public class NotificationHistoryDatabase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove any files that are too old and schedule jobs to clean up the rest
|
* Remove any files that are too old.
|
||||||
|
*/
|
||||||
|
void prune() {
|
||||||
|
prune(HISTORY_RETENTION_DAYS, System.currentTimeMillis());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Remove any files that are too old.
|
||||||
*/
|
*/
|
||||||
void prune(final int retentionDays, final long currentTimeMillis) {
|
void prune(final int retentionDays, final long currentTimeMillis) {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
@@ -265,10 +249,6 @@ public class NotificationHistoryDatabase {
|
|||||||
|
|
||||||
if (creationTime <= retentionBoundary.getTimeInMillis()) {
|
if (creationTime <= retentionBoundary.getTimeInMillis()) {
|
||||||
deleteFile(currentOldestFile);
|
deleteFile(currentOldestFile);
|
||||||
} else {
|
|
||||||
// all remaining files are newer than the cut off; schedule jobs to delete
|
|
||||||
scheduleDeletion(
|
|
||||||
currentOldestFile.getBaseFile(), creationTime, retentionDays);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -306,26 +286,6 @@ public class NotificationHistoryDatabase {
|
|||||||
removeFilePathFromHistory(file.getBaseFile().getAbsolutePath());
|
removeFilePathFromHistory(file.getBaseFile().getAbsolutePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scheduleDeletion(File file, long creationTime, int retentionDays) {
|
|
||||||
final long deletionTime = creationTime + (retentionDays * HISTORY_RETENTION_MS);
|
|
||||||
scheduleDeletion(file, deletionTime);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void scheduleDeletion(File file, long deletionTime) {
|
|
||||||
if (DEBUG) {
|
|
||||||
Slog.d(TAG, "Scheduling deletion for " + file.getName() + " at " + deletionTime);
|
|
||||||
}
|
|
||||||
final PendingIntent pi = PendingIntent.getBroadcast(mContext,
|
|
||||||
REQUEST_CODE_DELETION,
|
|
||||||
new Intent(ACTION_HISTORY_DELETION)
|
|
||||||
.setData(new Uri.Builder().scheme(SCHEME_DELETION)
|
|
||||||
.appendPath(file.getAbsolutePath()).build())
|
|
||||||
.addFlags(Intent.FLAG_RECEIVER_FOREGROUND)
|
|
||||||
.putExtra(EXTRA_KEY, file.getAbsolutePath()),
|
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
|
|
||||||
mAlarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, deletionTime, pi);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void writeLocked(AtomicFile file, NotificationHistory notifications)
|
private void writeLocked(AtomicFile file, NotificationHistory notifications)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
FileOutputStream fos = file.startWrite();
|
FileOutputStream fos = file.startWrite();
|
||||||
@@ -355,12 +315,6 @@ public class NotificationHistoryDatabase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unregisterFileCleanupReceiver() {
|
|
||||||
if(mContext != null) {
|
|
||||||
mContext.unregisterReceiver(mFileCleanupReceiver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static long safeParseLong(String fileName) {
|
private static long safeParseLong(String fileName) {
|
||||||
// AtomicFile will create copies of the numeric files with ".new" and ".bak"
|
// AtomicFile will create copies of the numeric files with ".new" and ".bak"
|
||||||
// over the course of its processing. If these files still exist on boot we need to clean
|
// over the course of its processing. If these files still exist on boot we need to clean
|
||||||
@@ -372,40 +326,15 @@ public class NotificationHistoryDatabase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private final BroadcastReceiver mFileCleanupReceiver = new BroadcastReceiver() {
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
String action = intent.getAction();
|
|
||||||
if (action == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (ACTION_HISTORY_DELETION.equals(action)) {
|
|
||||||
try {
|
|
||||||
synchronized (mLock) {
|
|
||||||
final String filePath = intent.getStringExtra(EXTRA_KEY);
|
|
||||||
AtomicFile fileToDelete = new AtomicFile(new File(filePath));
|
|
||||||
if (DEBUG) {
|
|
||||||
Slog.d(TAG, "Removed " + fileToDelete.getBaseFile().getName());
|
|
||||||
}
|
|
||||||
fileToDelete.delete();
|
|
||||||
removeFilePathFromHistory(filePath);
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
Slog.e(TAG, "Failed to delete notification history file", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
final class WriteBufferRunnable implements Runnable {
|
final class WriteBufferRunnable implements Runnable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
long time = System.currentTimeMillis();
|
long time = System.currentTimeMillis();
|
||||||
run(time, new AtomicFile(new File(mHistoryDir, String.valueOf(time))));
|
run(new AtomicFile(new File(mHistoryDir, String.valueOf(time))));
|
||||||
}
|
}
|
||||||
|
|
||||||
void run(long time, AtomicFile file) {
|
void run(AtomicFile file) {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
if (DEBUG) Slog.d(TAG, "WriteBufferRunnable "
|
if (DEBUG) Slog.d(TAG, "WriteBufferRunnable "
|
||||||
+ file.getBaseFile().getAbsolutePath());
|
+ file.getBaseFile().getAbsolutePath());
|
||||||
@@ -413,8 +342,6 @@ public class NotificationHistoryDatabase {
|
|||||||
writeLocked(file, mBuffer);
|
writeLocked(file, mBuffer);
|
||||||
mHistoryFiles.addFirst(file);
|
mHistoryFiles.addFirst(file);
|
||||||
mBuffer = new NotificationHistory();
|
mBuffer = new NotificationHistory();
|
||||||
|
|
||||||
scheduleDeletion(file.getBaseFile(), time, HISTORY_RETENTION_DAYS);
|
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Slog.e(TAG, "Failed to write buffer to disk. not flushing buffer", e);
|
Slog.e(TAG, "Failed to write buffer to disk. not flushing buffer", e);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -35,6 +35,6 @@ public class NotificationHistoryDatabaseFactory {
|
|||||||
if(sTestingNotificationHistoryDb != null) {
|
if(sTestingNotificationHistoryDb != null) {
|
||||||
return sTestingNotificationHistoryDb;
|
return sTestingNotificationHistoryDb;
|
||||||
}
|
}
|
||||||
return new NotificationHistoryDatabase(context, handler, rootDir);
|
return new NotificationHistoryDatabase(handler, rootDir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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.app.job.JobScheduler.RESULT_SUCCESS;
|
||||||
|
|
||||||
|
import android.app.job.JobInfo;
|
||||||
|
import android.app.job.JobParameters;
|
||||||
|
import android.app.job.JobScheduler;
|
||||||
|
import android.app.job.JobService;
|
||||||
|
import android.content.ComponentName;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.CancellationSignal;
|
||||||
|
import android.util.Slog;
|
||||||
|
|
||||||
|
import com.android.server.LocalServices;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This service runs every twenty minutes to ensure the retention policy for notification history
|
||||||
|
* data.
|
||||||
|
*/
|
||||||
|
public class NotificationHistoryJobService extends JobService {
|
||||||
|
private final static String TAG = "NotificationHistoryJob";
|
||||||
|
private static final long JOB_RUN_INTERVAL = TimeUnit.MINUTES.toMillis(20);
|
||||||
|
|
||||||
|
static final int BASE_JOB_ID = 237039804;
|
||||||
|
|
||||||
|
static void scheduleJob(Context context) {
|
||||||
|
JobScheduler jobScheduler = context.getSystemService(JobScheduler.class);
|
||||||
|
if (jobScheduler.getPendingJob(BASE_JOB_ID) == null) {
|
||||||
|
ComponentName component =
|
||||||
|
new ComponentName(context, NotificationHistoryJobService.class);
|
||||||
|
JobInfo newJob = new JobInfo.Builder(BASE_JOB_ID, component)
|
||||||
|
.setRequiresDeviceIdle(false)
|
||||||
|
.setPeriodic(JOB_RUN_INTERVAL)
|
||||||
|
.build();
|
||||||
|
if (jobScheduler.schedule(newJob) != RESULT_SUCCESS) {
|
||||||
|
Slog.w(TAG, "Failed to schedule history cleanup job");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private CancellationSignal mSignal;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onStartJob(JobParameters params) {
|
||||||
|
mSignal = new CancellationSignal();
|
||||||
|
new Thread(() -> {
|
||||||
|
NotificationManagerInternal nmInternal =
|
||||||
|
LocalServices.getService(NotificationManagerInternal.class);
|
||||||
|
nmInternal.cleanupHistoryFiles();
|
||||||
|
jobFinished(params, mSignal.isCanceled());
|
||||||
|
}).start();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onStopJob(JobParameters params) {
|
||||||
|
if (mSignal != null) {
|
||||||
|
mSignal.cancel();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -84,6 +84,12 @@ public class NotificationHistoryManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void onBootPhaseAppsCanStart() {
|
void onBootPhaseAppsCanStart() {
|
||||||
|
try {
|
||||||
|
Slog.d("julia", "trying to schedule job");
|
||||||
|
NotificationHistoryJobService.scheduleJob(mContext);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
Slog.e(TAG, "Failed to schedule cleanup job", e);
|
||||||
|
}
|
||||||
mSettingsObserver.observe();
|
mSettingsObserver.observe();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,6 +157,24 @@ public class NotificationHistoryManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void cleanupHistoryFiles() {
|
||||||
|
synchronized (mLock) {
|
||||||
|
int n = mUserUnlockedStates.size();
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
// cleanup old files for currently unlocked users. User are additionally cleaned
|
||||||
|
// on unlock in NotificationHistoryDatabase.init().
|
||||||
|
if (mUserUnlockedStates.valueAt(i)) {
|
||||||
|
final NotificationHistoryDatabase userHistory =
|
||||||
|
mUserState.get(mUserUnlockedStates.keyAt(i));
|
||||||
|
if (userHistory == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
userHistory.prune();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void deleteNotificationHistoryItem(String pkg, int uid, long postedTime) {
|
public void deleteNotificationHistoryItem(String pkg, int uid, long postedTime) {
|
||||||
synchronized (mLock) {
|
synchronized (mLock) {
|
||||||
int userId = UserHandle.getUserId(uid);
|
int userId = UserHandle.getUserId(uid);
|
||||||
@@ -288,7 +312,6 @@ public class NotificationHistoryManager {
|
|||||||
|
|
||||||
private void disableHistory(NotificationHistoryDatabase userHistory, @UserIdInt int userId) {
|
private void disableHistory(NotificationHistoryDatabase userHistory, @UserIdInt int userId) {
|
||||||
userHistory.disableHistory();
|
userHistory.disableHistory();
|
||||||
userHistory.unregisterFileCleanupReceiver();
|
|
||||||
|
|
||||||
mUserPendingHistoryDisables.put(userId, false);
|
mUserPendingHistoryDisables.put(userId, false);
|
||||||
mHistoryEnabled.put(userId, false);
|
mHistoryEnabled.put(userId, false);
|
||||||
|
|||||||
@@ -45,4 +45,6 @@ public interface NotificationManagerInternal {
|
|||||||
|
|
||||||
/** Send a notification to the user prompting them to review their notification permissions. */
|
/** Send a notification to the user prompting them to review their notification permissions. */
|
||||||
void sendReviewPermissionsNotification();
|
void sendReviewPermissionsNotification();
|
||||||
|
|
||||||
|
void cleanupHistoryFiles();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2723,6 +2723,7 @@ public class NotificationManagerService extends SystemService {
|
|||||||
mListeners.onBootPhaseAppsCanStart();
|
mListeners.onBootPhaseAppsCanStart();
|
||||||
mAssistants.onBootPhaseAppsCanStart();
|
mAssistants.onBootPhaseAppsCanStart();
|
||||||
mConditionProviders.onBootPhaseAppsCanStart();
|
mConditionProviders.onBootPhaseAppsCanStart();
|
||||||
|
Slog.d("julia", "boot phase");
|
||||||
mHistoryManager.onBootPhaseAppsCanStart();
|
mHistoryManager.onBootPhaseAppsCanStart();
|
||||||
registerDeviceConfigChange();
|
registerDeviceConfigChange();
|
||||||
migrateDefaultNAS();
|
migrateDefaultNAS();
|
||||||
@@ -2733,7 +2734,7 @@ public class NotificationManagerService extends SystemService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUserUnlocking(@NonNull TargetUser user) {
|
public void onUserUnlocked(@NonNull TargetUser user) {
|
||||||
mHandler.post(() -> {
|
mHandler.post(() -> {
|
||||||
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "notifHistoryUnlockUser");
|
Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "notifHistoryUnlockUser");
|
||||||
try {
|
try {
|
||||||
@@ -6345,6 +6346,12 @@ public class NotificationManagerService extends SystemService {
|
|||||||
Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE,
|
Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE,
|
||||||
NotificationManagerService.REVIEW_NOTIF_STATE_RESHOWN);
|
NotificationManagerService.REVIEW_NOTIF_STATE_RESHOWN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void cleanupHistoryFiles() {
|
||||||
|
checkCallerIsSystem();
|
||||||
|
mHistoryManager.cleanupHistoryFiles();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted) {
|
int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted) {
|
||||||
|
|||||||
@@ -18,7 +18,6 @@ package com.android.server.notification;
|
|||||||
import static com.google.common.truth.Truth.assertThat;
|
import static com.google.common.truth.Truth.assertThat;
|
||||||
|
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyLong;
|
import static org.mockito.ArgumentMatchers.anyLong;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
@@ -26,7 +25,6 @@ import static org.mockito.Mockito.times;
|
|||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import android.app.AlarmManager;
|
|
||||||
import android.app.NotificationHistory;
|
import android.app.NotificationHistory;
|
||||||
import android.app.NotificationHistory.HistoricalNotification;
|
import android.app.NotificationHistory.HistoricalNotification;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
@@ -60,8 +58,6 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase {
|
|||||||
Handler mFileWriteHandler;
|
Handler mFileWriteHandler;
|
||||||
@Mock
|
@Mock
|
||||||
Context mContext;
|
Context mContext;
|
||||||
@Mock
|
|
||||||
AlarmManager mAlarmManager;
|
|
||||||
|
|
||||||
NotificationHistoryDatabase mDataBase;
|
NotificationHistoryDatabase mDataBase;
|
||||||
|
|
||||||
@@ -96,21 +92,15 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase {
|
|||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
MockitoAnnotations.initMocks(this);
|
MockitoAnnotations.initMocks(this);
|
||||||
when(mContext.getSystemService(AlarmManager.class)).thenReturn(mAlarmManager);
|
|
||||||
when(mContext.getUser()).thenReturn(getContext().getUser());
|
when(mContext.getUser()).thenReturn(getContext().getUser());
|
||||||
when(mContext.getPackageName()).thenReturn(getContext().getPackageName());
|
when(mContext.getPackageName()).thenReturn(getContext().getPackageName());
|
||||||
|
|
||||||
mRootDir = new File(mContext.getFilesDir(), "NotificationHistoryDatabaseTest");
|
mRootDir = new File(mContext.getFilesDir(), "NotificationHistoryDatabaseTest");
|
||||||
|
|
||||||
mDataBase = new NotificationHistoryDatabase(mContext, mFileWriteHandler, mRootDir);
|
mDataBase = new NotificationHistoryDatabase(mFileWriteHandler, mRootDir);
|
||||||
mDataBase.init();
|
mDataBase.init();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
public void testDeletionReceiver() {
|
|
||||||
verify(mContext, times(1)).registerReceiver(any(), any(), anyInt());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testPrune() throws Exception {
|
public void testPrune() throws Exception {
|
||||||
GregorianCalendar cal = new GregorianCalendar();
|
GregorianCalendar cal = new GregorianCalendar();
|
||||||
@@ -144,8 +134,6 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase {
|
|||||||
mDataBase.prune(retainDays, cal.getTimeInMillis());
|
mDataBase.prune(retainDays, cal.getTimeInMillis());
|
||||||
|
|
||||||
assertThat(mDataBase.mHistoryFiles).containsExactlyElementsIn(expectedFiles);
|
assertThat(mDataBase.mHistoryFiles).containsExactlyElementsIn(expectedFiles);
|
||||||
|
|
||||||
verify(mAlarmManager, times(6)).setExactAndAllowWhileIdle(anyInt(), anyLong(), any());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -412,15 +400,14 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase {
|
|||||||
when(file.getName()).thenReturn("5");
|
when(file.getName()).thenReturn("5");
|
||||||
when(af.getBaseFile()).thenReturn(file);
|
when(af.getBaseFile()).thenReturn(file);
|
||||||
|
|
||||||
wbr.run(5, af);
|
wbr.run(af);
|
||||||
|
|
||||||
assertThat(mDataBase.mHistoryFiles.size()).isEqualTo(1);
|
assertThat(mDataBase.mHistoryFiles.size()).isEqualTo(1);
|
||||||
assertThat(mDataBase.mBuffer).isNotEqualTo(nh);
|
assertThat(mDataBase.mBuffer).isNotEqualTo(nh);
|
||||||
verify(mAlarmManager, times(1)).setExactAndAllowWhileIdle(anyInt(), anyLong(), any());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRemoveFilePathFromHistory_hasMatch() throws Exception {
|
public void testRemoveFilePathFromHistory_hasMatch() {
|
||||||
for (int i = 0; i < 5; i++) {
|
for (int i = 0; i < 5; i++) {
|
||||||
AtomicFile af = mock(AtomicFile.class);
|
AtomicFile af = mock(AtomicFile.class);
|
||||||
when(af.getBaseFile()).thenReturn(new File(mRootDir, "af" + i));
|
when(af.getBaseFile()).thenReturn(new File(mRootDir, "af" + i));
|
||||||
@@ -436,7 +423,7 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testRemoveFilePathFromHistory_noMatch() throws Exception {
|
public void testRemoveFilePathFromHistory_noMatch() {
|
||||||
for (int i = 0; i < 5; i++) {
|
for (int i = 0; i < 5; i++) {
|
||||||
AtomicFile af = mock(AtomicFile.class);
|
AtomicFile af = mock(AtomicFile.class);
|
||||||
when(af.getBaseFile()).thenReturn(new File(mRootDir, "af" + i));
|
when(af.getBaseFile()).thenReturn(new File(mRootDir, "af" + i));
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2022 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 junit.framework.Assert.assertEquals;
|
||||||
|
import static junit.framework.Assert.assertTrue;
|
||||||
|
import static junit.framework.TestCase.assertFalse;
|
||||||
|
|
||||||
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.timeout;
|
||||||
|
import static org.mockito.Mockito.times;
|
||||||
|
import static org.mockito.Mockito.verify;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
import android.app.job.JobInfo;
|
||||||
|
import android.app.job.JobParameters;
|
||||||
|
import android.app.job.JobScheduler;
|
||||||
|
import android.app.job.JobService;
|
||||||
|
import android.app.job.JobServiceEngine;
|
||||||
|
import android.testing.AndroidTestingRunner;
|
||||||
|
|
||||||
|
import androidx.test.rule.ServiceTestRule;
|
||||||
|
|
||||||
|
import com.android.server.LocalServices;
|
||||||
|
import com.android.server.UiServiceTestCase;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import org.mockito.ArgumentCaptor;
|
||||||
|
import org.mockito.Captor;
|
||||||
|
import org.mockito.Mock;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
@RunWith(AndroidTestingRunner.class)
|
||||||
|
public class NotificationHistoryJobServiceTest extends UiServiceTestCase {
|
||||||
|
private NotificationHistoryJobService mJobService;
|
||||||
|
private JobParameters mJobParams = new JobParameters(null,
|
||||||
|
NotificationHistoryJobService.BASE_JOB_ID, null, null, null,
|
||||||
|
0, false, false, null, null, null);
|
||||||
|
|
||||||
|
@Captor
|
||||||
|
ArgumentCaptor<JobInfo> mJobInfoCaptor;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private JobScheduler mMockJobScheduler;
|
||||||
|
|
||||||
|
@Mock
|
||||||
|
private NotificationManagerInternal mMockNotificationManagerInternal;
|
||||||
|
|
||||||
|
@Rule
|
||||||
|
public final ServiceTestRule mServiceRule = new ServiceTestRule();
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setUp() throws Exception {
|
||||||
|
mJobService = new NotificationHistoryJobService();
|
||||||
|
|
||||||
|
final Field field = JobService.class.getDeclaredField("mEngine");
|
||||||
|
field.setAccessible(true);
|
||||||
|
field.set(mJobService, mock(JobServiceEngine.class));
|
||||||
|
mContext.addMockSystemService(JobScheduler.class, mMockJobScheduler);
|
||||||
|
|
||||||
|
// add NotificationManagerInternal to LocalServices
|
||||||
|
LocalServices.removeServiceForTest(NotificationManagerInternal.class);
|
||||||
|
LocalServices.addService(NotificationManagerInternal.class,
|
||||||
|
mMockNotificationManagerInternal);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testScheduleJob() {
|
||||||
|
// if asked, the job doesn't currently exist yet
|
||||||
|
when(mMockJobScheduler.getPendingJob(anyInt())).thenReturn(null);
|
||||||
|
|
||||||
|
// attempt to schedule the job
|
||||||
|
NotificationHistoryJobService.scheduleJob(mContext);
|
||||||
|
verify(mMockJobScheduler, times(1)).schedule(mJobInfoCaptor.capture());
|
||||||
|
|
||||||
|
// verify various properties of the job that is passed in to the job scheduler
|
||||||
|
JobInfo jobInfo = mJobInfoCaptor.getValue();
|
||||||
|
assertEquals(NotificationHistoryJobService.BASE_JOB_ID, jobInfo.getId());
|
||||||
|
assertFalse(jobInfo.isPersisted());
|
||||||
|
assertTrue(jobInfo.isPeriodic());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testOnStartJob() {
|
||||||
|
assertTrue(mJobService.onStartJob(mJobParams));
|
||||||
|
|
||||||
|
verify(mMockNotificationManagerInternal, timeout(500).atLeastOnce()).cleanupHistoryFiles();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8369,7 +8369,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
|
|||||||
public void testOnUnlockUser() {
|
public void testOnUnlockUser() {
|
||||||
UserInfo ui = new UserInfo();
|
UserInfo ui = new UserInfo();
|
||||||
ui.id = 10;
|
ui.id = 10;
|
||||||
mService.onUserUnlocking(new TargetUser(ui));
|
mService.onUserUnlocked(new TargetUser(ui));
|
||||||
waitForIdle();
|
waitForIdle();
|
||||||
|
|
||||||
verify(mHistoryManager, timeout(MAX_POST_DELAY).times(1)).onUserUnlocked(ui.id);
|
verify(mHistoryManager, timeout(MAX_POST_DELAY).times(1)).onUserUnlocked(ui.id);
|
||||||
|
|||||||
Reference in New Issue
Block a user