Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eab465f5 authored by Suprabh Shukla's avatar Suprabh Shukla Committed by Android (Google) Code Review
Browse files

Merge "Use frozen state callback to drop listener alarms" into main

parents 75928d9a 12f5c369
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ java_library {

    static_libs: [
        "modules-utils-fastxmlserializer",
        "service-jobscheduler-alarm.flags-aconfig-java",
        "service-jobscheduler-job.flags-aconfig-java",
    ],

+12 −0
Original line number Diff line number Diff line
@@ -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",
}
+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
    }
}
+65 −13
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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;
@@ -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;
@@ -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();
@@ -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() {
@@ -1880,7 +1917,6 @@ public class AlarmManagerService extends SystemService {
        };

        synchronized (mLock) {
            mHandler = new AlarmHandler();
            mConstants = new Constants(mHandler);

            mAlarmStore = new LazyAlarmStore();
@@ -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);
@@ -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();
@@ -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
@@ -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;
            }
+118 −3
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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;
@@ -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;
@@ -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
@@ -240,6 +247,8 @@ public final class AlarmManagerServiceTest {
    @Mock
    private ActivityManagerInternal mActivityManagerInternal;
    @Mock
    private ActivityManager mActivityManager;
    @Mock
    private PackageManagerInternal mPackageManagerInternal;
    @Mock
    private AppStateTrackerImpl mAppStateTracker;
@@ -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));
@@ -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)});
@@ -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);
@@ -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));
    }