From 98546a0e94b82392821b32e028a9f37f7cf2d845 Mon Sep 17 00:00:00 2001 From: Julia Reynolds Date: Tue, 12 Jul 2022 14:40:03 -0400 Subject: [PATCH] Clean up history files via job vs multiple alarms Test: services/tests/uiservicestests/src/com/android/server/notification Test: monitor history files over 24 hours Fixes: 237039804 Change-Id: Ied5db4a63b366b5bfc33b415f041d06a369282ad --- core/res/AndroidManifest.xml | 4 + .../NotificationHistoryDatabase.java | 97 ++-------------- .../NotificationHistoryDatabaseFactory.java | 2 +- .../NotificationHistoryJobService.java | 81 +++++++++++++ .../NotificationHistoryManager.java | 25 +++- .../NotificationManagerInternal.java | 2 + .../NotificationManagerService.java | 9 +- .../NotificationHistoryDatabaseTest.java | 21 +--- .../NotificationHistoryJobServiceTest.java | 108 ++++++++++++++++++ .../NotificationManagerServiceTest.java | 2 +- 10 files changed, 245 insertions(+), 106 deletions(-) create mode 100644 services/core/java/com/android/server/notification/NotificationHistoryJobService.java create mode 100644 services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index 7439b2f0921ff..6fcf728776142 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -7061,6 +7061,10 @@ android:permission="android.permission.BIND_JOB_SERVICE"> + + + diff --git a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java index a9b2570a3ddae..7fe2f81aa231e 100644 --- a/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java +++ b/services/core/java/com/android/server/notification/NotificationHistoryDatabase.java @@ -16,15 +16,8 @@ package com.android.server.notification; -import android.app.AlarmManager; import android.app.NotificationHistory; 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.util.AtomicFile; import android.util.Slog; @@ -60,18 +53,9 @@ public class NotificationHistoryDatabase { private static final String TAG = "NotiHistoryDatabase"; private static final boolean DEBUG = NotificationManagerService.DBG; 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 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 Handler mFileWriteHandler; @VisibleForTesting @@ -87,9 +71,7 @@ public class NotificationHistoryDatabase { @VisibleForTesting NotificationHistory mBuffer; - public NotificationHistoryDatabase(Context context, Handler fileWriteHandler, File dir) { - mContext = context; - mAlarmManager = context.getSystemService(AlarmManager.class); + public NotificationHistoryDatabase(Handler fileWriteHandler, File dir) { mCurrentVersion = DEFAULT_CURRENT_VERSION; mFileWriteHandler = fileWriteHandler; mVersionFile = new File(dir, "version"); @@ -97,11 +79,6 @@ public class NotificationHistoryDatabase { mHistoryFiles = new LinkedList<>(); mBuffer = new NotificationHistory(); 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() { @@ -117,7 +94,7 @@ public class NotificationHistoryDatabase { checkVersionAndBuildLocked(); 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) { synchronized (mLock) { @@ -265,10 +249,6 @@ public class NotificationHistoryDatabase { if (creationTime <= retentionBoundary.getTimeInMillis()) { 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()); } - 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) throws IOException { 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) { // 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 @@ -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 { @Override public void run() { 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) { if (DEBUG) Slog.d(TAG, "WriteBufferRunnable " + file.getBaseFile().getAbsolutePath()); @@ -413,8 +342,6 @@ public class NotificationHistoryDatabase { writeLocked(file, mBuffer); mHistoryFiles.addFirst(file); mBuffer = new NotificationHistory(); - - scheduleDeletion(file.getBaseFile(), time, HISTORY_RETENTION_DAYS); } catch (IOException e) { Slog.e(TAG, "Failed to write buffer to disk. not flushing buffer", e); } diff --git a/services/core/java/com/android/server/notification/NotificationHistoryDatabaseFactory.java b/services/core/java/com/android/server/notification/NotificationHistoryDatabaseFactory.java index d9e0d79883c54..0d975cc50d796 100644 --- a/services/core/java/com/android/server/notification/NotificationHistoryDatabaseFactory.java +++ b/services/core/java/com/android/server/notification/NotificationHistoryDatabaseFactory.java @@ -35,6 +35,6 @@ public class NotificationHistoryDatabaseFactory { if(sTestingNotificationHistoryDb != null) { return sTestingNotificationHistoryDb; } - return new NotificationHistoryDatabase(context, handler, rootDir); + return new NotificationHistoryDatabase(handler, rootDir); } } diff --git a/services/core/java/com/android/server/notification/NotificationHistoryJobService.java b/services/core/java/com/android/server/notification/NotificationHistoryJobService.java new file mode 100644 index 0000000000000..3776ad7a0799d --- /dev/null +++ b/services/core/java/com/android/server/notification/NotificationHistoryJobService.java @@ -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; + } +} + diff --git a/services/core/java/com/android/server/notification/NotificationHistoryManager.java b/services/core/java/com/android/server/notification/NotificationHistoryManager.java index 0aacd130c837d..cf878a4c093e8 100644 --- a/services/core/java/com/android/server/notification/NotificationHistoryManager.java +++ b/services/core/java/com/android/server/notification/NotificationHistoryManager.java @@ -84,6 +84,12 @@ public class NotificationHistoryManager { } 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(); } @@ -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) { synchronized (mLock) { int userId = UserHandle.getUserId(uid); @@ -288,7 +312,6 @@ public class NotificationHistoryManager { private void disableHistory(NotificationHistoryDatabase userHistory, @UserIdInt int userId) { userHistory.disableHistory(); - userHistory.unregisterFileCleanupReceiver(); mUserPendingHistoryDisables.put(userId, false); mHistoryEnabled.put(userId, false); diff --git a/services/core/java/com/android/server/notification/NotificationManagerInternal.java b/services/core/java/com/android/server/notification/NotificationManagerInternal.java index 8a627367c1dc4..bc3885605a6c7 100644 --- a/services/core/java/com/android/server/notification/NotificationManagerInternal.java +++ b/services/core/java/com/android/server/notification/NotificationManagerInternal.java @@ -45,4 +45,6 @@ public interface NotificationManagerInternal { /** Send a notification to the user prompting them to review their notification permissions. */ void sendReviewPermissionsNotification(); + + void cleanupHistoryFiles(); } diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java index 9486a45e989e0..e1458e90daa94 100755 --- a/services/core/java/com/android/server/notification/NotificationManagerService.java +++ b/services/core/java/com/android/server/notification/NotificationManagerService.java @@ -2723,6 +2723,7 @@ public class NotificationManagerService extends SystemService { mListeners.onBootPhaseAppsCanStart(); mAssistants.onBootPhaseAppsCanStart(); mConditionProviders.onBootPhaseAppsCanStart(); + Slog.d("julia", "boot phase"); mHistoryManager.onBootPhaseAppsCanStart(); registerDeviceConfigChange(); migrateDefaultNAS(); @@ -2733,7 +2734,7 @@ public class NotificationManagerService extends SystemService { } @Override - public void onUserUnlocking(@NonNull TargetUser user) { + public void onUserUnlocked(@NonNull TargetUser user) { mHandler.post(() -> { Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "notifHistoryUnlockUser"); try { @@ -6345,6 +6346,12 @@ public class NotificationManagerService extends SystemService { Settings.Global.REVIEW_PERMISSIONS_NOTIFICATION_STATE, NotificationManagerService.REVIEW_NOTIF_STATE_RESHOWN); } + + @Override + public void cleanupHistoryFiles() { + checkCallerIsSystem(); + mHistoryManager.cleanupHistoryFiles(); + } }; int getNumNotificationChannelsForPackage(String pkg, int uid, boolean includeDeleted) { diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java index bd7186e74354d..2f922600857a3 100644 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryDatabaseTest.java @@ -18,7 +18,6 @@ package com.android.server.notification; import static com.google.common.truth.Truth.assertThat; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.Mockito.mock; 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.when; -import android.app.AlarmManager; import android.app.NotificationHistory; import android.app.NotificationHistory.HistoricalNotification; import android.content.Context; @@ -60,8 +58,6 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase { Handler mFileWriteHandler; @Mock Context mContext; - @Mock - AlarmManager mAlarmManager; NotificationHistoryDatabase mDataBase; @@ -96,21 +92,15 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase { @Before public void setUp() { MockitoAnnotations.initMocks(this); - when(mContext.getSystemService(AlarmManager.class)).thenReturn(mAlarmManager); when(mContext.getUser()).thenReturn(getContext().getUser()); when(mContext.getPackageName()).thenReturn(getContext().getPackageName()); mRootDir = new File(mContext.getFilesDir(), "NotificationHistoryDatabaseTest"); - mDataBase = new NotificationHistoryDatabase(mContext, mFileWriteHandler, mRootDir); + mDataBase = new NotificationHistoryDatabase(mFileWriteHandler, mRootDir); mDataBase.init(); } - @Test - public void testDeletionReceiver() { - verify(mContext, times(1)).registerReceiver(any(), any(), anyInt()); - } - @Test public void testPrune() throws Exception { GregorianCalendar cal = new GregorianCalendar(); @@ -144,8 +134,6 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase { mDataBase.prune(retainDays, cal.getTimeInMillis()); assertThat(mDataBase.mHistoryFiles).containsExactlyElementsIn(expectedFiles); - - verify(mAlarmManager, times(6)).setExactAndAllowWhileIdle(anyInt(), anyLong(), any()); } @Test @@ -412,15 +400,14 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase { when(file.getName()).thenReturn("5"); when(af.getBaseFile()).thenReturn(file); - wbr.run(5, af); + wbr.run(af); assertThat(mDataBase.mHistoryFiles.size()).isEqualTo(1); assertThat(mDataBase.mBuffer).isNotEqualTo(nh); - verify(mAlarmManager, times(1)).setExactAndAllowWhileIdle(anyInt(), anyLong(), any()); } @Test - public void testRemoveFilePathFromHistory_hasMatch() throws Exception { + public void testRemoveFilePathFromHistory_hasMatch() { for (int i = 0; i < 5; i++) { AtomicFile af = mock(AtomicFile.class); when(af.getBaseFile()).thenReturn(new File(mRootDir, "af" + i)); @@ -436,7 +423,7 @@ public class NotificationHistoryDatabaseTest extends UiServiceTestCase { } @Test - public void testRemoveFilePathFromHistory_noMatch() throws Exception { + public void testRemoveFilePathFromHistory_noMatch() { for (int i = 0; i < 5; i++) { AtomicFile af = mock(AtomicFile.class); when(af.getBaseFile()).thenReturn(new File(mRootDir, "af" + i)); diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java new file mode 100644 index 0000000000000..af10b9dba63fe --- /dev/null +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationHistoryJobServiceTest.java @@ -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 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(); + } +} \ No newline at end of file diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java index b1b323b734bd2..443ed583bd843 100755 --- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java +++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java @@ -8369,7 +8369,7 @@ public class NotificationManagerServiceTest extends UiServiceTestCase { public void testOnUnlockUser() { UserInfo ui = new UserInfo(); ui.id = 10; - mService.onUserUnlocking(new TargetUser(ui)); + mService.onUserUnlocked(new TargetUser(ui)); waitForIdle(); verify(mHistoryManager, timeout(MAX_POST_DELAY).times(1)).onUserUnlocked(ui.id);