From e39ca6e271d28ad4a425a60d626e4ee072ffaf47 Mon Sep 17 00:00:00 2001 From: Kevin Han Date: Fri, 16 Apr 2021 16:21:45 -0700 Subject: [PATCH] Send boot complete broadcast when unhibernated Apps that have been force-stopped may want to register job/alarms when they are no longer force-stopped, so resend the boot complete broadcast when an app is unhibernated. Bug: 185261245 Test: atest AppHibernationServiceTest Test: manual Change-Id: Ia60f7b566d2f03746b232e0ea5a8002d33703f09 --- .../apphibernation/AppHibernationService.java | 46 +++++++++++++++++++ .../AppHibernationServiceTest.java | 20 ++++++++ 2 files changed, 66 insertions(+) diff --git a/services/core/java/com/android/server/apphibernation/AppHibernationService.java b/services/core/java/com/android/server/apphibernation/AppHibernationService.java index 881a047402158..efb0f4a9343cc 100644 --- a/services/core/java/com/android/server/apphibernation/AppHibernationService.java +++ b/services/core/java/com/android/server/apphibernation/AppHibernationService.java @@ -16,6 +16,7 @@ package com.android.server.apphibernation; +import static android.app.AppOpsManager.OP_NONE; import static android.content.Intent.ACTION_PACKAGE_ADDED; import static android.content.Intent.ACTION_PACKAGE_REMOVED; import static android.content.Intent.EXTRA_REMOVED_FOR_ALL_USERS; @@ -25,8 +26,10 @@ import static android.provider.DeviceConfig.NAMESPACE_APP_HIBERNATION; import static com.android.server.apphibernation.AppHibernationConstants.KEY_APP_HIBERNATION_ENABLED; +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; +import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityThread; import android.app.IActivityManager; @@ -366,6 +369,49 @@ public final class AppHibernationService extends SystemService { Trace.traceBegin(Trace.TRACE_TAG_SYSTEM_SERVER, "unhibernatePackage"); pkgState.hibernated = false; pkgState.lastUnhibernatedMs = System.currentTimeMillis(); + // Deliver LOCKED_BOOT_COMPLETE AND BOOT_COMPLETE broadcast so app can re-register + // their alarms/jobs/etc. + try { + Intent lockedBcIntent = new Intent(Intent.ACTION_LOCKED_BOOT_COMPLETED) + .setPackage(packageName); + final String[] requiredPermissions = {Manifest.permission.RECEIVE_BOOT_COMPLETED}; + mIActivityManager.broadcastIntentWithFeature( + null /* caller */, + null /* callingFeatureId */, + lockedBcIntent, + null /* resolvedType */, + null /* resultTo */, + Activity.RESULT_OK, + null /* resultData */, + null /* resultExtras */, + requiredPermissions, + null /* excludedPermissions */, + OP_NONE, + null /* bOptions */, + false /* serialized */, + false /* sticky */, + userId); + + Intent bcIntent = new Intent(Intent.ACTION_BOOT_COMPLETED).setPackage(packageName); + mIActivityManager.broadcastIntentWithFeature( + null /* caller */, + null /* callingFeatureId */, + bcIntent, + null /* resolvedType */, + null /* resultTo */, + Activity.RESULT_OK, + null /* resultData */, + null /* resultExtras */, + requiredPermissions, + null /* excludedPermissions */, + OP_NONE, + null /* bOptions */, + false /* serialized */, + false /* sticky */, + userId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } Trace.traceEnd(Trace.TRACE_TAG_SYSTEM_SERVER); } diff --git a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java index 6d76ad895d4a9..4a6c9be58c11d 100644 --- a/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/apphibernation/AppHibernationServiceTest.java @@ -30,6 +30,7 @@ import static org.mockito.ArgumentMatchers.intThat; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import android.app.IActivityManager; @@ -264,6 +265,25 @@ public final class AppHibernationServiceTest { assertFalse(mAppHibernationService.isHibernatingGlobally(PACKAGE_NAME_1)); } + @Test + public void testUnhibernatingPackageForUserSendsBootCompleteBroadcast() + throws RemoteException { + // GIVEN a hibernating package for a user + mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, true); + + // WHEN we unhibernate the package + mAppHibernationService.setHibernatingForUser(PACKAGE_NAME_1, USER_ID_1, false); + + // THEN we send the boot complete broadcasts + ArgumentCaptor intentArgumentCaptor = ArgumentCaptor.forClass(Intent.class); + verify(mIActivityManager, times(2)).broadcastIntentWithFeature(any(), any(), + intentArgumentCaptor.capture(), any(), any(), anyInt(), any(), any(), any(), any(), + anyInt(), any(), anyBoolean(), anyBoolean(), eq(USER_ID_1)); + List capturedIntents = intentArgumentCaptor.getAllValues(); + assertEquals(capturedIntents.get(0).getAction(), Intent.ACTION_LOCKED_BOOT_COMPLETED); + assertEquals(capturedIntents.get(1).getAction(), Intent.ACTION_BOOT_COMPLETED); + } + /** * Add a mock user with one package. */