Loading apex/jobscheduler/service/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ java_library { static_libs: [ "modules-utils-fastxmlserializer", "service-jobscheduler-alarm.flags-aconfig-java", "service-jobscheduler-job.flags-aconfig-java", ], Loading apex/jobscheduler/service/aconfig/Android.bp +12 −0 Original line number Diff line number Diff line Loading @@ -27,3 +27,15 @@ java_aconfig_library { aconfig_declarations: "service-job.flags-aconfig", visibility: ["//frameworks/base:__subpackages__"], } // Alarm aconfig_declarations { name: "alarm_flags", package: "com.android.server.alarm", srcs: ["alarm.aconfig"], } java_aconfig_library { name: "service-jobscheduler-alarm.flags-aconfig-java", aconfig_declarations: "alarm_flags", } apex/jobscheduler/service/aconfig/alarm.aconfig 0 → 100644 +11 −0 Original line number Diff line number Diff line package: "com.android.server.alarm" flag { name: "use_frozen_state_to_drop_listener_alarms" namespace: "backstage_power" description: "Use frozen state callback to drop listener alarms for cached apps" bug: "324470945" metadata { purpose: PURPOSE_BUGFIX } } apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +65 −13 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.alarm; import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_FROZEN; import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; import static android.app.AlarmManager.ELAPSED_REALTIME; import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP; Loading Loading @@ -75,6 +76,7 @@ import android.annotation.NonNull; import android.annotation.SuppressLint; import android.annotation.UserIdInt; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.ActivityOptions; import android.app.AlarmManager; Loading Loading @@ -103,6 +105,7 @@ import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.HandlerExecutor; import android.os.IBinder; import android.os.Looper; import android.os.Message; Loading Loading @@ -145,6 +148,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsService; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.LocalLog; Loading Loading @@ -293,6 +297,7 @@ public class AlarmManagerService extends SystemService { private final Injector mInjector; int mBroadcastRefCount = 0; boolean mUseFrozenStateToDropListenerAlarms; MetricsHelper mMetricsHelper; PowerManager.WakeLock mWakeLock; SparseIntArray mAlarmsPerUid = new SparseIntArray(); Loading Loading @@ -1856,15 +1861,47 @@ public class AlarmManagerService extends SystemService { @Override public void onStart() { mInjector.init(); mHandler = new AlarmHandler(); mOptsWithFgs.setPendingIntentBackgroundActivityLaunchAllowed(false); mOptsWithFgsForAlarmClock.setPendingIntentBackgroundActivityLaunchAllowed(false); mOptsWithoutFgs.setPendingIntentBackgroundActivityLaunchAllowed(false); mOptsTimeBroadcast.setPendingIntentBackgroundActivityLaunchAllowed(false); mActivityOptsRestrictBal.setPendingIntentBackgroundActivityLaunchAllowed(false); mBroadcastOptsRestrictBal.setPendingIntentBackgroundActivityLaunchAllowed(false); mMetricsHelper = new MetricsHelper(getContext(), mLock); mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); mUseFrozenStateToDropListenerAlarms = Flags.useFrozenStateToDropListenerAlarms(); if (mUseFrozenStateToDropListenerAlarms) { final ActivityManager.UidFrozenStateChangedCallback callback = (uids, frozenStates) -> { final int size = frozenStates.length; if (uids.length != size) { Slog.wtf(TAG, "Got different length arrays in frozen state callback!" + " uids.length: " + uids.length + " frozenStates.length: " + size); // Cannot process received data in any meaningful way. return; } final IntArray affectedUids = new IntArray(); for (int i = 0; i < size; i++) { if (frozenStates[i] != UID_FROZEN_STATE_FROZEN) { continue; } if (!CompatChanges.isChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED, uids[i])) { continue; } affectedUids.add(uids[i]); } if (affectedUids.size() > 0) { removeExactListenerAlarms(affectedUids.toArray()); } }; final ActivityManager am = getContext().getSystemService(ActivityManager.class); am.registerUidFrozenStateChangedCallback(new HandlerExecutor(mHandler), callback); } mListenerDeathRecipient = new IBinder.DeathRecipient() { @Override public void binderDied() { Loading @@ -1880,7 +1917,6 @@ public class AlarmManagerService extends SystemService { }; synchronized (mLock) { mHandler = new AlarmHandler(); mConstants = new Constants(mHandler); mAlarmStore = new LazyAlarmStore(); Loading Loading @@ -1960,6 +1996,21 @@ public class AlarmManagerService extends SystemService { publishBinderService(Context.ALARM_SERVICE, mService); } private void removeExactListenerAlarms(int... whichUids) { synchronized (mLock) { removeAlarmsInternalLocked(a -> { if (!ArrayUtils.contains(whichUids, a.uid) || a.listener == null || a.windowLength != 0) { return false; } Slog.w(TAG, "Alarm " + a.listenerTag + " being removed for " + UserHandle.formatUid(a.uid) + ":" + a.packageName + " because the app got frozen"); return true; }, REMOVE_REASON_LISTENER_CACHED); } } void refreshExactAlarmCandidates() { final String[] candidates = mLocalPermissionManager.getAppOpPermissionPackages( Manifest.permission.SCHEDULE_EXACT_ALARM); Loading Loading @@ -3074,6 +3125,14 @@ public class AlarmManagerService extends SystemService { mConstants.dump(pw); pw.println(); pw.println("Feature Flags:"); pw.increaseIndent(); pw.print(Flags.FLAG_USE_FROZEN_STATE_TO_DROP_LISTENER_ALARMS, mUseFrozenStateToDropListenerAlarms); pw.decreaseIndent(); pw.println(); pw.println(); if (mConstants.USE_TARE_POLICY == EconomyManager.ENABLED_MODE_ON) { pw.println("TARE details:"); pw.increaseIndent(); Loading Loading @@ -4959,18 +5018,7 @@ public class AlarmManagerService extends SystemService { break; case REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED: uid = (Integer) msg.obj; synchronized (mLock) { removeAlarmsInternalLocked(a -> { if (a.uid != uid || a.listener == null || a.windowLength != 0) { return false; } // TODO (b/265195908): Change to .w once we have some data on breakages. Slog.wtf(TAG, "Alarm " + a.listenerTag + " being removed for " + UserHandle.formatUid(a.uid) + ":" + a.packageName + " because the app went into cached state"); return true; }, REMOVE_REASON_LISTENER_CACHED); } removeExactListenerAlarms(uid); break; default: // nope, just ignore it Loading Loading @@ -5322,6 +5370,10 @@ public class AlarmManagerService extends SystemService { @Override public void handleUidCachedChanged(int uid, boolean cached) { if (mUseFrozenStateToDropListenerAlarms) { // Use ActivityManager#UidFrozenStateChangedCallback instead. return; } if (!CompatChanges.isChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED, uid)) { return; } Loading services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +118 −3 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.server.alarm; import static android.Manifest.permission.SCHEDULE_EXACT_ALARM; import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_FROZEN; import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_UNFROZEN; import static android.app.AlarmManager.ELAPSED_REALTIME; import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP; import static android.app.AlarmManager.EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED; Loading @@ -42,6 +44,7 @@ import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET; import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED; import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED; import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.NULL_DEFAULT; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod; Loading Loading @@ -135,6 +138,7 @@ import android.net.Uri; import android.os.BatteryManager; import android.os.Bundle; import android.os.Handler; import android.os.HandlerExecutor; import android.os.IBinder; import android.os.Looper; import android.os.Message; Loading @@ -145,9 +149,11 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserHandle; import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; import android.platform.test.flag.junit.SetFlagsRule; import android.platform.test.flag.util.FlagSetException; import android.provider.DeviceConfig; import android.provider.Settings; import android.text.format.DateFormat; import android.util.ArraySet; import android.util.Log; Loading Loading @@ -215,6 +221,7 @@ public final class AlarmManagerServiceTest { private AppStateTrackerImpl.Listener mListener; private AlarmManagerService.UninstallReceiver mPackageChangesReceiver; private AlarmManagerService.ChargingReceiver mChargingReceiver; private ActivityManager.UidFrozenStateChangedCallback mUidFrozenStateCallback; private IAppOpsCallback mIAppOpsCallback; private IAlarmManager mBinder; @Mock Loading @@ -240,6 +247,8 @@ public final class AlarmManagerServiceTest { @Mock private ActivityManagerInternal mActivityManagerInternal; @Mock private ActivityManager mActivityManager; @Mock private PackageManagerInternal mPackageManagerInternal; @Mock private AppStateTrackerImpl mAppStateTracker; Loading Loading @@ -403,15 +412,31 @@ public final class AlarmManagerServiceTest { .mockStatic(PermissionChecker.class) .mockStatic(PermissionManagerService.class) .mockStatic(ServiceManager.class) .mockStatic(Settings.Global.class) .mockStatic(SystemProperties.class) .spyStatic(UserHandle.class) .afterSessionFinished( () -> LocalServices.removeServiceForTest(AlarmManagerInternal.class)) .build(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(NULL_DEFAULT); /** * Have to do this to switch the {@link Flags} implementation to {@link FakeFeatureFlagsImpl}. * All methods that need any flag enabled should use the * {@link android.platform.test.annotations.EnableFlags} annotation, in which case disabling * the flag will fail with an exception that we will swallow here. */ private void disableFlagsNotSetByAnnotation() { try { mSetFlagsRule.disableFlags(Flags.FLAG_USE_FROZEN_STATE_TO_DROP_LISTENER_ALARMS); } catch (FlagSetException fse) { // Expected if the test about to be run requires this enabled. } } @Before public final void setUp() { public void setUp() { doReturn(mIActivityManager).when(ActivityManager::getService); doReturn(mDeviceIdleInternal).when( () -> LocalServices.getService(DeviceIdleInternal.class)); Loading Loading @@ -469,6 +494,7 @@ public final class AlarmManagerServiceTest { when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager); when(mMockContext.getSystemService(BatteryManager.class)).thenReturn(mBatteryManager); when(mMockContext.getSystemService(ActivityManager.class)).thenReturn(mActivityManager); registerAppIds(new String[]{TEST_CALLING_PACKAGE}, new Integer[]{UserHandle.getAppId(TEST_CALLING_UID)}); Loading @@ -479,7 +505,18 @@ public final class AlarmManagerServiceTest { mService = new AlarmManagerService(mMockContext, mInjector); spyOn(mService); disableFlagsNotSetByAnnotation(); mService.onStart(); if (Flags.useFrozenStateToDropListenerAlarms()) { final ArgumentCaptor<ActivityManager.UidFrozenStateChangedCallback> frozenCaptor = ArgumentCaptor.forClass(ActivityManager.UidFrozenStateChangedCallback.class); verify(mActivityManager).registerUidFrozenStateChangedCallback( any(HandlerExecutor.class), frozenCaptor.capture()); mUidFrozenStateCallback = frozenCaptor.getValue(); } // Unable to mock mMockContext to return a mock stats manager. // So just mocking the whole MetricsHelper instance. mService.mMetricsHelper = mock(MetricsHelper.class); Loading Loading @@ -3741,9 +3778,87 @@ public final class AlarmManagerServiceTest { mListener.handleUidCachedChanged(TEST_CALLING_UID, true); assertAndHandleMessageSync(REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED); assertEquals(3, mService.mAlarmsPerUid.get(TEST_CALLING_UID)); assertEquals(numExactListenerUid2 + 2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2)); mListener.handleUidCachedChanged(TEST_CALLING_UID_2, true); assertAndHandleMessageSync(REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED); assertEquals(3, mService.mAlarmsPerUid.get(TEST_CALLING_UID)); assertEquals(2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2)); } private void executeUidFrozenStateCallback(int[] uids, int[] frozenStates) { assertNotNull(mUidFrozenStateCallback); mUidFrozenStateCallback.onUidFrozenStateChanged(uids, frozenStates); } @EnableFlags(Flags.FLAG_USE_FROZEN_STATE_TO_DROP_LISTENER_ALARMS) @Test public void exactListenerAlarmsRemovedOnFrozen() { mockChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED, true); setTestAlarmWithListener(ELAPSED_REALTIME, 31, getNewListener(() -> {}), WINDOW_EXACT, TEST_CALLING_UID); setTestAlarmWithListener(RTC, 42, getNewListener(() -> {}), 56, TEST_CALLING_UID); setTestAlarm(ELAPSED_REALTIME, 54, WINDOW_EXACT, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID, null); setTestAlarm(RTC, 49, 154, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID, null); setTestAlarmWithListener(ELAPSED_REALTIME, 21, getNewListener(() -> {}), WINDOW_EXACT, TEST_CALLING_UID_2); setTestAlarmWithListener(RTC, 412, getNewListener(() -> {}), 561, TEST_CALLING_UID_2); setTestAlarm(ELAPSED_REALTIME, 26, WINDOW_EXACT, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID_2, null); setTestAlarm(RTC, 549, 234, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID_2, null); assertEquals(8, mService.mAlarmStore.size()); executeUidFrozenStateCallback( new int[] {TEST_CALLING_UID, TEST_CALLING_UID_2}, new int[] {UID_FROZEN_STATE_FROZEN, UID_FROZEN_STATE_UNFROZEN}); assertEquals(7, mService.mAlarmStore.size()); executeUidFrozenStateCallback( new int[] {TEST_CALLING_UID_2}, new int[] {UID_FROZEN_STATE_FROZEN}); assertEquals(6, mService.mAlarmStore.size()); } @EnableFlags(Flags.FLAG_USE_FROZEN_STATE_TO_DROP_LISTENER_ALARMS) @Test public void alarmCountOnListenerFrozen() { mockChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED, true); // Set some alarms for TEST_CALLING_UID. final int numExactListenerUid1 = 17; for (int i = 0; i < numExactListenerUid1; i++) { setTestAlarmWithListener(ALARM_TYPES[i % 4], mNowElapsedTest + i, getNewListener(() -> {})); } setTestAlarmWithListener(RTC, 42, getNewListener(() -> {}), 56, TEST_CALLING_UID); setTestAlarm(ELAPSED_REALTIME, 54, getNewMockPendingIntent()); setTestAlarm(RTC, 49, 154, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID, null); // Set some alarms for TEST_CALLING_UID_2. final int numExactListenerUid2 = 11; for (int i = 0; i < numExactListenerUid2; i++) { setTestAlarmWithListener(ALARM_TYPES[i % 4], mNowElapsedTest + i, getNewListener(() -> {}), WINDOW_EXACT, TEST_CALLING_UID_2); } setTestAlarmWithListener(RTC, 412, getNewListener(() -> {}), 561, TEST_CALLING_UID_2); setTestAlarm(RTC_WAKEUP, 26, WINDOW_EXACT, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID_2, null); assertEquals(numExactListenerUid1 + 3, mService.mAlarmsPerUid.get(TEST_CALLING_UID)); assertEquals(numExactListenerUid2 + 2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2)); executeUidFrozenStateCallback( new int[] {TEST_CALLING_UID, TEST_CALLING_UID_2}, new int[] {UID_FROZEN_STATE_FROZEN, UID_FROZEN_STATE_UNFROZEN}); assertEquals(3, mService.mAlarmsPerUid.get(TEST_CALLING_UID)); assertEquals(numExactListenerUid2 + 2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2)); executeUidFrozenStateCallback( new int[] {TEST_CALLING_UID_2}, new int[] {UID_FROZEN_STATE_FROZEN}); assertEquals(3, mService.mAlarmsPerUid.get(TEST_CALLING_UID)); assertEquals(2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2)); } Loading Loading
apex/jobscheduler/service/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -28,6 +28,7 @@ java_library { static_libs: [ "modules-utils-fastxmlserializer", "service-jobscheduler-alarm.flags-aconfig-java", "service-jobscheduler-job.flags-aconfig-java", ], Loading
apex/jobscheduler/service/aconfig/Android.bp +12 −0 Original line number Diff line number Diff line Loading @@ -27,3 +27,15 @@ java_aconfig_library { aconfig_declarations: "service-job.flags-aconfig", visibility: ["//frameworks/base:__subpackages__"], } // Alarm aconfig_declarations { name: "alarm_flags", package: "com.android.server.alarm", srcs: ["alarm.aconfig"], } java_aconfig_library { name: "service-jobscheduler-alarm.flags-aconfig-java", aconfig_declarations: "alarm_flags", }
apex/jobscheduler/service/aconfig/alarm.aconfig 0 → 100644 +11 −0 Original line number Diff line number Diff line package: "com.android.server.alarm" flag { name: "use_frozen_state_to_drop_listener_alarms" namespace: "backstage_power" description: "Use frozen state callback to drop listener alarms for cached apps" bug: "324470945" metadata { purpose: PURPOSE_BUGFIX } }
apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +65 −13 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.alarm; import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_FROZEN; import static android.app.ActivityManagerInternal.ALLOW_NON_FULL; import static android.app.AlarmManager.ELAPSED_REALTIME; import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP; Loading Loading @@ -75,6 +76,7 @@ import android.annotation.NonNull; import android.annotation.SuppressLint; import android.annotation.UserIdInt; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.ActivityOptions; import android.app.AlarmManager; Loading Loading @@ -103,6 +105,7 @@ import android.os.Binder; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.HandlerExecutor; import android.os.IBinder; import android.os.Looper; import android.os.Message; Loading Loading @@ -145,6 +148,7 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.annotations.VisibleForTesting; import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsService; import com.android.internal.util.ArrayUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.FrameworkStatsLog; import com.android.internal.util.LocalLog; Loading Loading @@ -293,6 +297,7 @@ public class AlarmManagerService extends SystemService { private final Injector mInjector; int mBroadcastRefCount = 0; boolean mUseFrozenStateToDropListenerAlarms; MetricsHelper mMetricsHelper; PowerManager.WakeLock mWakeLock; SparseIntArray mAlarmsPerUid = new SparseIntArray(); Loading Loading @@ -1856,15 +1861,47 @@ public class AlarmManagerService extends SystemService { @Override public void onStart() { mInjector.init(); mHandler = new AlarmHandler(); mOptsWithFgs.setPendingIntentBackgroundActivityLaunchAllowed(false); mOptsWithFgsForAlarmClock.setPendingIntentBackgroundActivityLaunchAllowed(false); mOptsWithoutFgs.setPendingIntentBackgroundActivityLaunchAllowed(false); mOptsTimeBroadcast.setPendingIntentBackgroundActivityLaunchAllowed(false); mActivityOptsRestrictBal.setPendingIntentBackgroundActivityLaunchAllowed(false); mBroadcastOptsRestrictBal.setPendingIntentBackgroundActivityLaunchAllowed(false); mMetricsHelper = new MetricsHelper(getContext(), mLock); mActivityManagerInternal = LocalServices.getService(ActivityManagerInternal.class); mUseFrozenStateToDropListenerAlarms = Flags.useFrozenStateToDropListenerAlarms(); if (mUseFrozenStateToDropListenerAlarms) { final ActivityManager.UidFrozenStateChangedCallback callback = (uids, frozenStates) -> { final int size = frozenStates.length; if (uids.length != size) { Slog.wtf(TAG, "Got different length arrays in frozen state callback!" + " uids.length: " + uids.length + " frozenStates.length: " + size); // Cannot process received data in any meaningful way. return; } final IntArray affectedUids = new IntArray(); for (int i = 0; i < size; i++) { if (frozenStates[i] != UID_FROZEN_STATE_FROZEN) { continue; } if (!CompatChanges.isChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED, uids[i])) { continue; } affectedUids.add(uids[i]); } if (affectedUids.size() > 0) { removeExactListenerAlarms(affectedUids.toArray()); } }; final ActivityManager am = getContext().getSystemService(ActivityManager.class); am.registerUidFrozenStateChangedCallback(new HandlerExecutor(mHandler), callback); } mListenerDeathRecipient = new IBinder.DeathRecipient() { @Override public void binderDied() { Loading @@ -1880,7 +1917,6 @@ public class AlarmManagerService extends SystemService { }; synchronized (mLock) { mHandler = new AlarmHandler(); mConstants = new Constants(mHandler); mAlarmStore = new LazyAlarmStore(); Loading Loading @@ -1960,6 +1996,21 @@ public class AlarmManagerService extends SystemService { publishBinderService(Context.ALARM_SERVICE, mService); } private void removeExactListenerAlarms(int... whichUids) { synchronized (mLock) { removeAlarmsInternalLocked(a -> { if (!ArrayUtils.contains(whichUids, a.uid) || a.listener == null || a.windowLength != 0) { return false; } Slog.w(TAG, "Alarm " + a.listenerTag + " being removed for " + UserHandle.formatUid(a.uid) + ":" + a.packageName + " because the app got frozen"); return true; }, REMOVE_REASON_LISTENER_CACHED); } } void refreshExactAlarmCandidates() { final String[] candidates = mLocalPermissionManager.getAppOpPermissionPackages( Manifest.permission.SCHEDULE_EXACT_ALARM); Loading Loading @@ -3074,6 +3125,14 @@ public class AlarmManagerService extends SystemService { mConstants.dump(pw); pw.println(); pw.println("Feature Flags:"); pw.increaseIndent(); pw.print(Flags.FLAG_USE_FROZEN_STATE_TO_DROP_LISTENER_ALARMS, mUseFrozenStateToDropListenerAlarms); pw.decreaseIndent(); pw.println(); pw.println(); if (mConstants.USE_TARE_POLICY == EconomyManager.ENABLED_MODE_ON) { pw.println("TARE details:"); pw.increaseIndent(); Loading Loading @@ -4959,18 +5018,7 @@ public class AlarmManagerService extends SystemService { break; case REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED: uid = (Integer) msg.obj; synchronized (mLock) { removeAlarmsInternalLocked(a -> { if (a.uid != uid || a.listener == null || a.windowLength != 0) { return false; } // TODO (b/265195908): Change to .w once we have some data on breakages. Slog.wtf(TAG, "Alarm " + a.listenerTag + " being removed for " + UserHandle.formatUid(a.uid) + ":" + a.packageName + " because the app went into cached state"); return true; }, REMOVE_REASON_LISTENER_CACHED); } removeExactListenerAlarms(uid); break; default: // nope, just ignore it Loading Loading @@ -5322,6 +5370,10 @@ public class AlarmManagerService extends SystemService { @Override public void handleUidCachedChanged(int uid, boolean cached) { if (mUseFrozenStateToDropListenerAlarms) { // Use ActivityManager#UidFrozenStateChangedCallback instead. return; } if (!CompatChanges.isChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED, uid)) { return; } Loading
services/tests/mockingservicestests/src/com/android/server/alarm/AlarmManagerServiceTest.java +118 −3 Original line number Diff line number Diff line Loading @@ -16,6 +16,8 @@ package com.android.server.alarm; import static android.Manifest.permission.SCHEDULE_EXACT_ALARM; import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_FROZEN; import static android.app.ActivityManager.UidFrozenStateChangedCallback.UID_FROZEN_STATE_UNFROZEN; import static android.app.AlarmManager.ELAPSED_REALTIME; import static android.app.AlarmManager.ELAPSED_REALTIME_WAKEUP; import static android.app.AlarmManager.EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED; Loading @@ -42,6 +44,7 @@ import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_RESTRICTED; import static android.app.usage.UsageStatsManager.STANDBY_BUCKET_WORKING_SET; import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_ALLOWED; import static android.os.PowerWhitelistManager.TEMPORARY_ALLOWLIST_TYPE_FOREGROUND_SERVICE_NOT_ALLOWED; import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.NULL_DEFAULT; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer; import static com.android.dx.mockito.inline.extended.ExtendedMockito.doCallRealMethod; Loading Loading @@ -135,6 +138,7 @@ import android.net.Uri; import android.os.BatteryManager; import android.os.Bundle; import android.os.Handler; import android.os.HandlerExecutor; import android.os.IBinder; import android.os.Looper; import android.os.Message; Loading @@ -145,9 +149,11 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.os.SystemProperties; import android.os.UserHandle; import android.platform.test.annotations.EnableFlags; import android.platform.test.annotations.Presubmit; import android.platform.test.flag.junit.SetFlagsRule; import android.platform.test.flag.util.FlagSetException; import android.provider.DeviceConfig; import android.provider.Settings; import android.text.format.DateFormat; import android.util.ArraySet; import android.util.Log; Loading Loading @@ -215,6 +221,7 @@ public final class AlarmManagerServiceTest { private AppStateTrackerImpl.Listener mListener; private AlarmManagerService.UninstallReceiver mPackageChangesReceiver; private AlarmManagerService.ChargingReceiver mChargingReceiver; private ActivityManager.UidFrozenStateChangedCallback mUidFrozenStateCallback; private IAppOpsCallback mIAppOpsCallback; private IAlarmManager mBinder; @Mock Loading @@ -240,6 +247,8 @@ public final class AlarmManagerServiceTest { @Mock private ActivityManagerInternal mActivityManagerInternal; @Mock private ActivityManager mActivityManager; @Mock private PackageManagerInternal mPackageManagerInternal; @Mock private AppStateTrackerImpl mAppStateTracker; Loading Loading @@ -403,15 +412,31 @@ public final class AlarmManagerServiceTest { .mockStatic(PermissionChecker.class) .mockStatic(PermissionManagerService.class) .mockStatic(ServiceManager.class) .mockStatic(Settings.Global.class) .mockStatic(SystemProperties.class) .spyStatic(UserHandle.class) .afterSessionFinished( () -> LocalServices.removeServiceForTest(AlarmManagerInternal.class)) .build(); @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(NULL_DEFAULT); /** * Have to do this to switch the {@link Flags} implementation to {@link FakeFeatureFlagsImpl}. * All methods that need any flag enabled should use the * {@link android.platform.test.annotations.EnableFlags} annotation, in which case disabling * the flag will fail with an exception that we will swallow here. */ private void disableFlagsNotSetByAnnotation() { try { mSetFlagsRule.disableFlags(Flags.FLAG_USE_FROZEN_STATE_TO_DROP_LISTENER_ALARMS); } catch (FlagSetException fse) { // Expected if the test about to be run requires this enabled. } } @Before public final void setUp() { public void setUp() { doReturn(mIActivityManager).when(ActivityManager::getService); doReturn(mDeviceIdleInternal).when( () -> LocalServices.getService(DeviceIdleInternal.class)); Loading Loading @@ -469,6 +494,7 @@ public final class AlarmManagerServiceTest { when(mMockContext.getSystemService(Context.APP_OPS_SERVICE)).thenReturn(mAppOpsManager); when(mMockContext.getSystemService(BatteryManager.class)).thenReturn(mBatteryManager); when(mMockContext.getSystemService(ActivityManager.class)).thenReturn(mActivityManager); registerAppIds(new String[]{TEST_CALLING_PACKAGE}, new Integer[]{UserHandle.getAppId(TEST_CALLING_UID)}); Loading @@ -479,7 +505,18 @@ public final class AlarmManagerServiceTest { mService = new AlarmManagerService(mMockContext, mInjector); spyOn(mService); disableFlagsNotSetByAnnotation(); mService.onStart(); if (Flags.useFrozenStateToDropListenerAlarms()) { final ArgumentCaptor<ActivityManager.UidFrozenStateChangedCallback> frozenCaptor = ArgumentCaptor.forClass(ActivityManager.UidFrozenStateChangedCallback.class); verify(mActivityManager).registerUidFrozenStateChangedCallback( any(HandlerExecutor.class), frozenCaptor.capture()); mUidFrozenStateCallback = frozenCaptor.getValue(); } // Unable to mock mMockContext to return a mock stats manager. // So just mocking the whole MetricsHelper instance. mService.mMetricsHelper = mock(MetricsHelper.class); Loading Loading @@ -3741,9 +3778,87 @@ public final class AlarmManagerServiceTest { mListener.handleUidCachedChanged(TEST_CALLING_UID, true); assertAndHandleMessageSync(REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED); assertEquals(3, mService.mAlarmsPerUid.get(TEST_CALLING_UID)); assertEquals(numExactListenerUid2 + 2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2)); mListener.handleUidCachedChanged(TEST_CALLING_UID_2, true); assertAndHandleMessageSync(REMOVE_EXACT_LISTENER_ALARMS_ON_CACHED); assertEquals(3, mService.mAlarmsPerUid.get(TEST_CALLING_UID)); assertEquals(2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2)); } private void executeUidFrozenStateCallback(int[] uids, int[] frozenStates) { assertNotNull(mUidFrozenStateCallback); mUidFrozenStateCallback.onUidFrozenStateChanged(uids, frozenStates); } @EnableFlags(Flags.FLAG_USE_FROZEN_STATE_TO_DROP_LISTENER_ALARMS) @Test public void exactListenerAlarmsRemovedOnFrozen() { mockChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED, true); setTestAlarmWithListener(ELAPSED_REALTIME, 31, getNewListener(() -> {}), WINDOW_EXACT, TEST_CALLING_UID); setTestAlarmWithListener(RTC, 42, getNewListener(() -> {}), 56, TEST_CALLING_UID); setTestAlarm(ELAPSED_REALTIME, 54, WINDOW_EXACT, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID, null); setTestAlarm(RTC, 49, 154, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID, null); setTestAlarmWithListener(ELAPSED_REALTIME, 21, getNewListener(() -> {}), WINDOW_EXACT, TEST_CALLING_UID_2); setTestAlarmWithListener(RTC, 412, getNewListener(() -> {}), 561, TEST_CALLING_UID_2); setTestAlarm(ELAPSED_REALTIME, 26, WINDOW_EXACT, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID_2, null); setTestAlarm(RTC, 549, 234, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID_2, null); assertEquals(8, mService.mAlarmStore.size()); executeUidFrozenStateCallback( new int[] {TEST_CALLING_UID, TEST_CALLING_UID_2}, new int[] {UID_FROZEN_STATE_FROZEN, UID_FROZEN_STATE_UNFROZEN}); assertEquals(7, mService.mAlarmStore.size()); executeUidFrozenStateCallback( new int[] {TEST_CALLING_UID_2}, new int[] {UID_FROZEN_STATE_FROZEN}); assertEquals(6, mService.mAlarmStore.size()); } @EnableFlags(Flags.FLAG_USE_FROZEN_STATE_TO_DROP_LISTENER_ALARMS) @Test public void alarmCountOnListenerFrozen() { mockChangeEnabled(EXACT_LISTENER_ALARMS_DROPPED_ON_CACHED, true); // Set some alarms for TEST_CALLING_UID. final int numExactListenerUid1 = 17; for (int i = 0; i < numExactListenerUid1; i++) { setTestAlarmWithListener(ALARM_TYPES[i % 4], mNowElapsedTest + i, getNewListener(() -> {})); } setTestAlarmWithListener(RTC, 42, getNewListener(() -> {}), 56, TEST_CALLING_UID); setTestAlarm(ELAPSED_REALTIME, 54, getNewMockPendingIntent()); setTestAlarm(RTC, 49, 154, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID, null); // Set some alarms for TEST_CALLING_UID_2. final int numExactListenerUid2 = 11; for (int i = 0; i < numExactListenerUid2; i++) { setTestAlarmWithListener(ALARM_TYPES[i % 4], mNowElapsedTest + i, getNewListener(() -> {}), WINDOW_EXACT, TEST_CALLING_UID_2); } setTestAlarmWithListener(RTC, 412, getNewListener(() -> {}), 561, TEST_CALLING_UID_2); setTestAlarm(RTC_WAKEUP, 26, WINDOW_EXACT, getNewMockPendingIntent(), 0, 0, TEST_CALLING_UID_2, null); assertEquals(numExactListenerUid1 + 3, mService.mAlarmsPerUid.get(TEST_CALLING_UID)); assertEquals(numExactListenerUid2 + 2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2)); executeUidFrozenStateCallback( new int[] {TEST_CALLING_UID, TEST_CALLING_UID_2}, new int[] {UID_FROZEN_STATE_FROZEN, UID_FROZEN_STATE_UNFROZEN}); assertEquals(3, mService.mAlarmsPerUid.get(TEST_CALLING_UID)); assertEquals(numExactListenerUid2 + 2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2)); executeUidFrozenStateCallback( new int[] {TEST_CALLING_UID_2}, new int[] {UID_FROZEN_STATE_FROZEN}); assertEquals(3, mService.mAlarmsPerUid.get(TEST_CALLING_UID)); assertEquals(2, mService.mAlarmsPerUid.get(TEST_CALLING_UID_2)); } Loading