/* * Copyright (C) 2017 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; import static android.app.usage.UsageStatsManager.REASON_MAIN_DEFAULT; import static android.app.usage.UsageStatsManager.REASON_MAIN_USAGE; import static com.android.server.AppStateTrackerImpl.TARGET_OP; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.ActivityManager; import android.app.ActivityManagerInternal; import android.app.AppOpsManager; import android.app.AppOpsManager.OpEntry; import android.app.AppOpsManager.PackageOps; import android.app.IActivityManager; import android.app.IUidObserver; import android.app.usage.UsageStatsManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.net.Uri; import android.os.BatteryManager; import android.os.Handler; import android.os.Looper; import android.os.PowerManager.ServiceType; import android.os.PowerManagerInternal; import android.os.PowerSaveState; import android.os.Process; import android.os.RemoteException; import android.os.UserHandle; import android.platform.test.annotations.Presubmit; import android.provider.Settings.Global; import android.test.mock.MockContentResolver; import android.util.ArraySet; import android.util.Pair; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; import com.android.internal.app.IAppOpsCallback; import com.android.internal.app.IAppOpsService; import com.android.server.AppStateTrackerImpl.Listener; import com.android.server.usage.AppStandbyInternal; import com.android.server.usage.AppStandbyInternal.AppIdleStateChangeListener; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.mockito.stubbing.Answer; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Random; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import java.util.function.Consumer; /** * Tests for {@link AppStateTrackerImpl} * * Run with: atest com.android.server.AppStateTrackerTest */ @Presubmit @SmallTest @RunWith(AndroidJUnit4.class) public class AppStateTrackerTest { private class AppStateTrackerTestable extends AppStateTrackerImpl { AppStateTrackerTestable() { super(mMockContext, Looper.getMainLooper()); } @Override AppOpsManager injectAppOpsManager() { return mMockAppOpsManager; } @Override IAppOpsService injectIAppOpsService() { return mMockIAppOpsService; } @Override IActivityManager injectIActivityManager() { return mMockIActivityManager; } @Override ActivityManagerInternal injectActivityManagerInternal() { return mMockIActivityManagerInternal; } @Override PowerManagerInternal injectPowerManagerInternal() { return mMockPowerManagerInternal; } @Override AppStandbyInternal injectAppStandbyInternal() { when(mMockAppStandbyInternal.isAppIdleEnabled()).thenReturn(true); return mMockAppStandbyInternal; } @Override int injectGetGlobalSettingInt(String key, int def) { Integer val = mGlobalSettings.get(key); return (val == null) ? def : val; } @Override boolean isSmallBatteryDevice() { return mIsSmallBatteryDevice; } } private static final int UID_1 = Process.FIRST_APPLICATION_UID + 1; private static final int UID_2 = Process.FIRST_APPLICATION_UID + 2; private static final int UID_3 = Process.FIRST_APPLICATION_UID + 3; private static final int UID_10_1 = UserHandle.getUid(10, UID_1); private static final int UID_10_2 = UserHandle.getUid(10, UID_2); private static final int UID_10_3 = UserHandle.getUid(10, UID_3); private static final String PACKAGE_1 = "package1"; private static final String PACKAGE_2 = "package2"; private static final String PACKAGE_3 = "package3"; private static final String PACKAGE_SYSTEM = "android"; private Handler mMainHandler; @Mock private Context mMockContext; @Mock private IActivityManager mMockIActivityManager; @Mock private ActivityManagerInternal mMockIActivityManagerInternal; @Mock private AppOpsManager mMockAppOpsManager; @Mock private IAppOpsService mMockIAppOpsService; @Mock private PowerManagerInternal mMockPowerManagerInternal; @Mock private AppStandbyInternal mMockAppStandbyInternal; private MockContentResolver mMockContentResolver; private IUidObserver mIUidObserver; private IAppOpsCallback.Stub mAppOpsCallback; private Consumer<PowerSaveState> mPowerSaveObserver; private BroadcastReceiver mReceiver; private AppIdleStateChangeListener mAppIdleStateChangeListener; private boolean mPowerSaveMode; private boolean mIsSmallBatteryDevice; private final ArraySet<Pair<Integer, String>> mRestrictedPackages = new ArraySet(); private final HashMap<String, Integer> mGlobalSettings = new HashMap<>(); private Answer<List<PackageOps>> mGetPackagesForOps = inv -> new ArrayList<PackageOps>(); @Before public void setUp() { mMainHandler = new Handler(Looper.getMainLooper()); } /** * Enqueues a message and waits for it to complete. This ensures that any messages posted until * now have been executed. * * Note that these messages may have enqueued more messages, which may or may not have executed * when this method returns. */ private void waitUntilMainHandlerDrain() throws Exception { final CountDownLatch l = new CountDownLatch(1); mMainHandler.post(() -> { l.countDown(); }); assertTrue(l.await(5, TimeUnit.SECONDS)); } private PowerSaveState getPowerSaveState() { return new PowerSaveState.Builder().setBatterySaverEnabled(mPowerSaveMode).build(); } private AppStateTrackerTestable newInstance() throws Exception { MockitoAnnotations.initMocks(this); when(mMockIAppOpsService.checkOperation(eq(TARGET_OP), anyInt(), anyString())) .thenAnswer(inv -> { return mRestrictedPackages.indexOf( Pair.create(inv.getArgument(1), inv.getArgument(2))) >= 0 ? AppOpsManager.MODE_IGNORED : AppOpsManager.MODE_ALLOWED; }); final AppStateTrackerTestable instance = new AppStateTrackerTestable(); return instance; } private void callStart(AppStateTrackerTestable instance) throws RemoteException { // Set up functions that start() calls. when(mMockPowerManagerInternal.getLowPowerState(eq(ServiceType.FORCE_ALL_APPS_STANDBY))) .thenAnswer(inv -> getPowerSaveState()); when(mMockAppOpsManager.getPackagesForOps( any(int[].class) )).thenAnswer(mGetPackagesForOps); mMockContentResolver = new MockContentResolver(); when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver); // Call start. instance.onSystemServicesReady(); // Capture the listeners. ArgumentCaptor<IUidObserver> uidObserverArgumentCaptor = ArgumentCaptor.forClass(IUidObserver.class); ArgumentCaptor<IAppOpsCallback.Stub> appOpsCallbackCaptor = ArgumentCaptor.forClass(IAppOpsCallback.Stub.class); ArgumentCaptor<Consumer<PowerSaveState>> powerSaveObserverCaptor = ArgumentCaptor.forClass(Consumer.class); ArgumentCaptor<BroadcastReceiver> receiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); ArgumentCaptor<AppIdleStateChangeListener> appIdleStateChangeListenerCaptor = ArgumentCaptor.forClass(AppIdleStateChangeListener.class); verify(mMockIActivityManager).registerUidObserver( uidObserverArgumentCaptor.capture(), eq(ActivityManager.UID_OBSERVER_GONE | ActivityManager.UID_OBSERVER_IDLE | ActivityManager.UID_OBSERVER_ACTIVE | ActivityManager.UID_OBSERVER_CACHED), eq(ActivityManager.PROCESS_STATE_UNKNOWN), isNull()); verify(mMockIAppOpsService).startWatchingMode( eq(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND), isNull(), appOpsCallbackCaptor.capture()); verify(mMockPowerManagerInternal).registerLowPowerModeObserver( eq(ServiceType.FORCE_ALL_APPS_STANDBY), powerSaveObserverCaptor.capture()); verify(mMockContext, times(2)).registerReceiver( receiverCaptor.capture(), any(IntentFilter.class)); verify(mMockAppStandbyInternal).addListener( appIdleStateChangeListenerCaptor.capture()); mIUidObserver = uidObserverArgumentCaptor.getValue(); mAppOpsCallback = appOpsCallbackCaptor.getValue(); mPowerSaveObserver = powerSaveObserverCaptor.getValue(); mReceiver = receiverCaptor.getValue(); mAppIdleStateChangeListener = appIdleStateChangeListenerCaptor.getValue(); assertNotNull(mIUidObserver); assertNotNull(mAppOpsCallback); assertNotNull(mPowerSaveObserver); assertNotNull(mReceiver); assertNotNull(instance.mFlagsObserver); } private void setAppOps(int uid, String packageName, boolean restrict) throws RemoteException { final Pair p = Pair.create(uid, packageName); if (restrict) { mRestrictedPackages.add(p); } else { mRestrictedPackages.remove(p); } if (mAppOpsCallback != null) { mAppOpsCallback.opChanged(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, uid, packageName); } } private void areJobsRestricted(AppStateTrackerTestable instance, int[] uids, String[] packages, boolean[] restricted, boolean exemption) { assertTrue(uids.length == packages.length && uids.length == restricted.length); for (int i = 0; i < uids.length; i++) { assertEquals(restricted[i], instance.areJobsRestricted(uids[i], packages[i], exemption)); } } private void areAlarmsRestrictedByFAS(AppStateTrackerTestable instance, int[] uids, String[] packages, boolean[] restricted) { assertTrue(uids.length == packages.length && uids.length == restricted.length); for (int i = 0; i < uids.length; i++) { assertEquals(restricted[i], instance.areAlarmsRestricted(uids[i], packages[i])); } } private void areAlarmsRestrictedByBatterySaver(AppStateTrackerTestable instance, int[] uids, String[] packages, boolean[] restricted) { assertTrue(uids.length == packages.length && uids.length == restricted.length); for (int i = 0; i < uids.length; i++) { assertEquals(restricted[i], instance.areAlarmsRestrictedByBatterySaver(uids[i], packages[i])); } } @Test public void testAll() throws Exception { final AppStateTrackerTestable instance = newInstance(); callStart(instance); assertFalse(instance.isForceAllAppsStandbyEnabled()); when(mMockIActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled()) .thenReturn(false); areJobsRestricted(instance, new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {false, false, false, false}, false); areJobsRestricted(instance, new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {false, false, false, false}, true); areAlarmsRestrictedByBatterySaver(instance, new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {false, false, false, false}); // Toggle the auto restricted bucket feature flag on bg restriction, shouldn't make a // difference. when(mMockIActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled()) .thenReturn(true); areJobsRestricted(instance, new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {false, false, false, false}, false); areJobsRestricted(instance, new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {false, false, false, false}, true); areAlarmsRestrictedByBatterySaver(instance, new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {false, false, false, false}); mPowerSaveMode = true; mPowerSaveObserver.accept(getPowerSaveState()); assertTrue(instance.isForceAllAppsStandbyEnabled()); when(mMockIActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled()) .thenReturn(false); areJobsRestricted(instance, new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {true, true, true, false}, false); areJobsRestricted(instance, new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {false, false, false, false}, true); areAlarmsRestrictedByBatterySaver(instance, new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {true, true, true, false}); // Toggle the auto restricted bucket feature flag on bg restriction, shouldn't make a // difference. when(mMockIActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled()) .thenReturn(true); areJobsRestricted(instance, new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {true, true, true, false}, false); areJobsRestricted(instance, new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {false, false, false, false}, true); areAlarmsRestrictedByBatterySaver(instance, new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {true, true, true, false}); when(mMockIActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled()) .thenReturn(false); // Toggle the foreground state. assertFalse(instance.isUidActive(UID_1)); assertFalse(instance.isUidActive(UID_2)); assertTrue(instance.isUidActive(Process.SYSTEM_UID)); mIUidObserver.onUidActive(UID_1); waitUntilMainHandlerDrain(); waitUntilMainHandlerDrain(); areJobsRestricted(instance, new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {false, true, true, false}, false); areAlarmsRestrictedByBatterySaver(instance, new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {false, true, true, false}); assertTrue(instance.isUidActive(UID_1)); assertFalse(instance.isUidActive(UID_2)); mIUidObserver.onUidGone(UID_1, /*disable=*/ false); waitUntilMainHandlerDrain(); waitUntilMainHandlerDrain(); areJobsRestricted(instance, new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {true, true, true, false}, false); areAlarmsRestrictedByBatterySaver(instance, new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {true, true, true, false}); assertFalse(instance.isUidActive(UID_1)); assertFalse(instance.isUidActive(UID_2)); mIUidObserver.onUidActive(UID_1); waitUntilMainHandlerDrain(); waitUntilMainHandlerDrain(); areJobsRestricted(instance, new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {false, true, true, false}, false); areAlarmsRestrictedByBatterySaver(instance, new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {false, true, true, false}); mIUidObserver.onUidIdle(UID_1, /*disable=*/ false); waitUntilMainHandlerDrain(); waitUntilMainHandlerDrain(); areJobsRestricted(instance, new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {true, true, true, false}, false); areAlarmsRestrictedByBatterySaver(instance, new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {true, true, true, false}); assertFalse(instance.isUidActive(UID_1)); assertFalse(instance.isUidActive(UID_2)); // Toggle the app ops. mPowerSaveMode = false; mPowerSaveObserver.accept(getPowerSaveState()); assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_1, PACKAGE_1)); assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_1, PACKAGE_1)); assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_2, PACKAGE_2)); assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_2, PACKAGE_2)); areJobsRestricted(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {false, false, false, false, false}, false); areAlarmsRestrictedByBatterySaver(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {false, false, false, false, false}); areAlarmsRestrictedByFAS(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {false, false, false, false, false}); setAppOps(UID_1, PACKAGE_1, true); setAppOps(UID_10_2, PACKAGE_2, true); assertFalse(instance.isRunAnyInBackgroundAppOpsAllowed(UID_1, PACKAGE_1)); assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_1, PACKAGE_1)); assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_2, PACKAGE_2)); assertFalse(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_2, PACKAGE_2)); areJobsRestricted(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {true, false, false, true, false}, false); areJobsRestricted(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {true, false, false, true, false}, true); areAlarmsRestrictedByBatterySaver(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {false, false, false, false, false}); areAlarmsRestrictedByFAS(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {true, false, false, true, false}); // Toggle the auto restricted bucket feature flag on bg restriction. when(mMockIActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled()) .thenReturn(true); areJobsRestricted(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {false, false, false, false, false}, false); areJobsRestricted(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {false, false, false, false, false}, true); areAlarmsRestrictedByBatterySaver(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {false, false, false, false, false}); areAlarmsRestrictedByFAS(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {false, false, false, false, false}); // Toggle power saver, should still be the same. mPowerSaveMode = true; mPowerSaveObserver.accept(getPowerSaveState()); when(mMockIActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled()) .thenReturn(false); areJobsRestricted(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {true, true, true, true, false}, false); areJobsRestricted(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {true, false, false, true, false}, true); areAlarmsRestrictedByBatterySaver(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {true, true, true, true, false}); areAlarmsRestrictedByFAS(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {true, false, false, true, false}); // Toggle the auto restricted bucket feature flag on bg restriction. when(mMockIActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled()) .thenReturn(true); areJobsRestricted(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {true, true, true, true, false}, false); areJobsRestricted(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {false, false, false, false, false}, true); areAlarmsRestrictedByBatterySaver(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {true, true, true, true, false}); areAlarmsRestrictedByFAS(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {false, false, false, false, false}); mPowerSaveMode = false; mPowerSaveObserver.accept(getPowerSaveState()); when(mMockIActivityManagerInternal.isBgAutoRestrictedBucketFeatureFlagEnabled()) .thenReturn(false); areJobsRestricted(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {true, false, false, true, false}, false); areJobsRestricted(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {true, false, false, true, false}, true); areAlarmsRestrictedByBatterySaver(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {false, false, false, false, false}); areAlarmsRestrictedByFAS(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {true, false, false, true, false}); // Clear the app ops and update the exemption list. setAppOps(UID_1, PACKAGE_1, false); setAppOps(UID_10_2, PACKAGE_2, false); mPowerSaveMode = true; mPowerSaveObserver.accept(getPowerSaveState()); areJobsRestricted(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {true, true, true, true, false}, false); areJobsRestricted(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {false, false, false, false, false}, true); areAlarmsRestrictedByBatterySaver(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {true, true, true, true, false}); areAlarmsRestrictedByFAS(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {false, false, false, false, false}); instance.setPowerSaveExemptionListAppIds(new int[] {UID_1}, new int[] {}, new int[] {UID_2}); areJobsRestricted(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, UID_3, UID_10_3, Process.SYSTEM_UID}, new String[]{PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_3, PACKAGE_3, PACKAGE_SYSTEM}, new boolean[] {false, false, false, false, true, true, false}, false); areAlarmsRestrictedByBatterySaver(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, UID_3, UID_10_3, Process.SYSTEM_UID}, new String[]{PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_3, PACKAGE_3, PACKAGE_SYSTEM}, new boolean[] {false, false, true, true, true, true, false}); // Again, make sure toggling the global state doesn't change it. mPowerSaveMode = false; mPowerSaveObserver.accept(getPowerSaveState()); mPowerSaveMode = true; mPowerSaveObserver.accept(getPowerSaveState()); areJobsRestricted(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, UID_3, UID_10_3, Process.SYSTEM_UID}, new String[]{PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_3, PACKAGE_3, PACKAGE_SYSTEM}, new boolean[] {false, false, false, false, true, true, false}, false); areAlarmsRestrictedByBatterySaver(instance, new int[] {UID_1, UID_10_1, UID_2, UID_10_2, UID_3, UID_10_3, Process.SYSTEM_UID}, new String[]{PACKAGE_1, PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_3, PACKAGE_3, PACKAGE_SYSTEM}, new boolean[] {false, false, true, true, true, true, false}); assertTrue(instance.isUidPowerSaveExempt(UID_1)); assertTrue(instance.isUidPowerSaveExempt(UID_10_1)); assertFalse(instance.isUidPowerSaveExempt(UID_2)); assertFalse(instance.isUidPowerSaveExempt(UID_10_2)); assertFalse(instance.isUidTempPowerSaveExempt(UID_1)); assertFalse(instance.isUidTempPowerSaveExempt(UID_10_1)); assertTrue(instance.isUidTempPowerSaveExempt(UID_2)); assertTrue(instance.isUidTempPowerSaveExempt(UID_10_2)); } @Test public void testPowerSaveUserExemptionList() throws Exception { final AppStateTrackerTestable instance = newInstance(); instance.setPowerSaveExemptionListAppIds(new int[] {}, new int[] {UID_1, UID_2}, new int[] {}); assertTrue(instance.isUidPowerSaveUserExempt(UID_1)); assertTrue(instance.isUidPowerSaveUserExempt(UID_2)); assertFalse(instance.isUidPowerSaveUserExempt(UID_3)); } @Test public void testUidStateForeground() throws Exception { final AppStateTrackerTestable instance = newInstance(); callStart(instance); mIUidObserver.onUidActive(UID_1); waitUntilMainHandlerDrain(); waitUntilMainHandlerDrain(); assertTrue(instance.isUidActive(UID_1)); assertFalse(instance.isUidActive(UID_2)); assertTrue(instance.isUidActive(Process.SYSTEM_UID)); assertTrue(instance.isUidActiveSynced(UID_1)); assertFalse(instance.isUidActiveSynced(UID_2)); assertTrue(instance.isUidActiveSynced(Process.SYSTEM_UID)); mIUidObserver.onUidStateChanged(UID_2, ActivityManager.PROCESS_STATE_BOUND_FOREGROUND_SERVICE, 0, ActivityManager.PROCESS_CAPABILITY_NONE); waitUntilMainHandlerDrain(); waitUntilMainHandlerDrain(); assertTrue(instance.isUidActive(UID_1)); assertFalse(instance.isUidActive(UID_2)); assertTrue(instance.isUidActive(Process.SYSTEM_UID)); assertTrue(instance.isUidActiveSynced(UID_1)); assertFalse(instance.isUidActiveSynced(UID_2)); assertTrue(instance.isUidActiveSynced(Process.SYSTEM_UID)); mIUidObserver.onUidStateChanged(UID_1, ActivityManager.PROCESS_STATE_FOREGROUND_SERVICE, 0, ActivityManager.PROCESS_CAPABILITY_NONE); waitUntilMainHandlerDrain(); waitUntilMainHandlerDrain(); assertTrue(instance.isUidActive(UID_1)); assertFalse(instance.isUidActive(UID_2)); assertTrue(instance.isUidActive(Process.SYSTEM_UID)); mIUidObserver.onUidGone(UID_1, true); waitUntilMainHandlerDrain(); waitUntilMainHandlerDrain(); assertFalse(instance.isUidActive(UID_1)); assertFalse(instance.isUidActive(UID_2)); assertTrue(instance.isUidActive(Process.SYSTEM_UID)); mIUidObserver.onUidIdle(UID_2, true); waitUntilMainHandlerDrain(); waitUntilMainHandlerDrain(); assertFalse(instance.isUidActive(UID_1)); assertFalse(instance.isUidActive(UID_2)); assertTrue(instance.isUidActive(Process.SYSTEM_UID)); mIUidObserver.onUidStateChanged(UID_1, ActivityManager.PROCESS_STATE_IMPORTANT_FOREGROUND, 0, ActivityManager.PROCESS_CAPABILITY_NONE); waitUntilMainHandlerDrain(); waitUntilMainHandlerDrain(); assertFalse(instance.isUidActive(UID_1)); assertFalse(instance.isUidActive(UID_2)); assertTrue(instance.isUidActive(Process.SYSTEM_UID)); mIUidObserver.onUidStateChanged(UID_1, ActivityManager.PROCESS_STATE_TRANSIENT_BACKGROUND, 0, ActivityManager.PROCESS_CAPABILITY_NONE); waitUntilMainHandlerDrain(); waitUntilMainHandlerDrain(); assertFalse(instance.isUidActive(UID_1)); assertFalse(instance.isUidActive(UID_2)); assertTrue(instance.isUidActive(Process.SYSTEM_UID)); assertFalse(instance.isUidActiveSynced(UID_1)); assertFalse(instance.isUidActiveSynced(UID_2)); assertTrue(instance.isUidActiveSynced(Process.SYSTEM_UID)); // The result from AMI.isUidActive() only affects isUidActiveSynced(). when(mMockIActivityManagerInternal.isUidActive(anyInt())).thenReturn(true); assertFalse(instance.isUidActive(UID_1)); assertFalse(instance.isUidActive(UID_2)); assertTrue(instance.isUidActive(Process.SYSTEM_UID)); assertTrue(instance.isUidActiveSynced(UID_1)); assertTrue(instance.isUidActiveSynced(UID_2)); assertTrue(instance.isUidActiveSynced(Process.SYSTEM_UID)); } @Test public void testExemptedBucket() throws Exception { final AppStateTrackerTestable instance = newInstance(); callStart(instance); assertFalse(instance.isForceAllAppsStandbyEnabled()); areJobsRestricted(instance, new int[] {UID_1, UID_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {false, false, false}, false); areAlarmsRestrictedByBatterySaver(instance, new int[] {UID_1, UID_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {false, false, false}); mPowerSaveMode = true; mPowerSaveObserver.accept(getPowerSaveState()); assertTrue(instance.isForceAllAppsStandbyEnabled()); areJobsRestricted(instance, new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {true, true, true, false}, false); areJobsRestricted(instance, new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {false, false, false, false}, true); areAlarmsRestrictedByBatterySaver(instance, new int[] {UID_1, UID_2, UID_10_2, Process.SYSTEM_UID}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2, PACKAGE_SYSTEM}, new boolean[] {true, true, true, false}); // Exempt package 2 on user-10. mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_2, /*user=*/ 10, false, UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT); areJobsRestricted(instance, new int[] {UID_1, UID_2, UID_10_2}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2}, new boolean[] {true, true, false}, false); areJobsRestricted(instance, new int[] {UID_1, UID_2, UID_10_2}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2}, new boolean[] {false, false, false}, true); areAlarmsRestrictedByBatterySaver(instance, new int[] {UID_1, UID_2, UID_10_2}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2}, new boolean[] {true, true, false}); // Exempt package 1 on user-0. mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_1, /*user=*/ 0, false, UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT); areJobsRestricted(instance, new int[] {UID_1, UID_2, UID_10_2}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2}, new boolean[] {false, true, false}, false); areJobsRestricted(instance, new int[] {UID_1, UID_2, UID_10_2}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2}, new boolean[] {false, false, false}, true); areAlarmsRestrictedByBatterySaver(instance, new int[] {UID_1, UID_2, UID_10_2}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2}, new boolean[] {false, true, false}); // Unexempt package 2 on user-10. mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_2, /*user=*/ 10, false, UsageStatsManager.STANDBY_BUCKET_ACTIVE, REASON_MAIN_USAGE); areJobsRestricted(instance, new int[] {UID_1, UID_2, UID_10_2}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2}, new boolean[] {false, true, true}, false); areJobsRestricted(instance, new int[] {UID_1, UID_2, UID_10_2}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2}, new boolean[] {false, false, false}, true); areAlarmsRestrictedByBatterySaver(instance, new int[] {UID_1, UID_2, UID_10_2}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2}, new boolean[] {false, true, true}); // Check force-app-standby. // EXEMPT doesn't exempt from force-app-standby. mPowerSaveMode = false; mPowerSaveObserver.accept(getPowerSaveState()); mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_1, /*user=*/ 0, false, UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT); mAppIdleStateChangeListener.onAppIdleStateChanged(PACKAGE_2, /*user=*/ 0, false, UsageStatsManager.STANDBY_BUCKET_EXEMPTED, REASON_MAIN_DEFAULT); // All 3 packages (u0:p1, u0:p2, u10:p2) are now in the exempted bucket. setAppOps(UID_1, PACKAGE_1, true); areJobsRestricted(instance, new int[] {UID_1, UID_2, UID_10_2}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2}, new boolean[] {true, false, false}, false); areJobsRestricted(instance, new int[] {UID_1, UID_2, UID_10_2}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2}, new boolean[] {true, false, false}, true); areAlarmsRestrictedByBatterySaver(instance, new int[] {UID_1, UID_2, UID_10_2}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2}, new boolean[] {false, false, false}); areAlarmsRestrictedByFAS(instance, new int[] {UID_1, UID_2, UID_10_2}, new String[] {PACKAGE_1, PACKAGE_2, PACKAGE_2}, new boolean[] {true, false, false}); } @Test public void loadPersistedAppOps() throws Exception { final AppStateTrackerTestable instance = newInstance(); final List<PackageOps> ops = new ArrayList<>(); //-------------------------------------------------- List<OpEntry> entries = new ArrayList<>(); entries.add(new OpEntry( AppOpsManager.OP_ACCESS_NOTIFICATIONS, AppOpsManager.MODE_IGNORED, Collections.emptyMap())); entries.add(new OpEntry( AppStateTrackerImpl.TARGET_OP, AppOpsManager.MODE_IGNORED, Collections.emptyMap())); ops.add(new PackageOps(PACKAGE_1, UID_1, entries)); //-------------------------------------------------- entries = new ArrayList<>(); entries.add(new OpEntry( AppStateTrackerImpl.TARGET_OP, AppOpsManager.MODE_IGNORED, Collections.emptyMap())); ops.add(new PackageOps(PACKAGE_2, UID_2, entries)); //-------------------------------------------------- entries = new ArrayList<>(); entries.add(new OpEntry( AppStateTrackerImpl.TARGET_OP, AppOpsManager.MODE_ALLOWED, Collections.emptyMap())); ops.add(new PackageOps(PACKAGE_1, UID_10_1, entries)); //-------------------------------------------------- entries = new ArrayList<>(); entries.add(new OpEntry( AppStateTrackerImpl.TARGET_OP, AppOpsManager.MODE_IGNORED, Collections.emptyMap())); entries.add(new OpEntry( AppOpsManager.OP_ACCESS_NOTIFICATIONS, AppOpsManager.MODE_IGNORED, Collections.emptyMap())); ops.add(new PackageOps(PACKAGE_3, UID_10_3, entries)); mGetPackagesForOps = inv -> { final int[] arg = (int[]) inv.getArgument(0); assertEquals(1, arg.length); assertEquals(AppOpsManager.OP_RUN_ANY_IN_BACKGROUND, arg[0]); return ops; }; callStart(instance); assertFalse(instance.isRunAnyInBackgroundAppOpsAllowed(UID_1, PACKAGE_1)); assertFalse(instance.isRunAnyInBackgroundAppOpsAllowed(UID_2, PACKAGE_2)); assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_3, PACKAGE_3)); assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_1, PACKAGE_1)); assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_2, PACKAGE_2)); assertFalse(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_3, PACKAGE_3)); } private void assertNoCallbacks(Listener l) throws Exception { waitUntilMainHandlerDrain(); verify(l, times(0)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); } @Test public void testPowerSaveListener() throws Exception { final AppStateTrackerTestable instance = newInstance(); callStart(instance); Listener l = mock(Listener.class); instance.addListener(l); // Power save on. mPowerSaveMode = true; mPowerSaveObserver.accept(getPowerSaveState()); waitUntilMainHandlerDrain(); verify(l, times(1)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(1)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); // Power save off. mPowerSaveMode = false; mPowerSaveObserver.accept(getPowerSaveState()); waitUntilMainHandlerDrain(); verify(l, times(1)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(1)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); // Updating to the same state should not fire listener mPowerSaveMode = false; mPowerSaveObserver.accept(getPowerSaveState()); assertNoCallbacks(l); } @Test public void testAllListeners() throws Exception { final AppStateTrackerTestable instance = newInstance(); callStart(instance); Listener l = mock(Listener.class); instance.addListener(l); // ------------------------------------------------------------------------- // Test with apppops. setAppOps(UID_10_2, PACKAGE_2, true); waitUntilMainHandlerDrain(); verify(l, times(0)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(1)).updateJobsForUidPackage(eq(UID_10_2), eq(PACKAGE_2), anyBoolean()); verify(l, times(1)).updateBackgroundRestrictedForUidPackage(eq(UID_10_2), eq(PACKAGE_2), eq(true)); verify(l, times(0)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); setAppOps(UID_10_2, PACKAGE_2, false); waitUntilMainHandlerDrain(); verify(l, times(0)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(1)).updateJobsForUidPackage(eq(UID_10_2), eq(PACKAGE_2), anyBoolean()); verify(l, times(1)).updateBackgroundRestrictedForUidPackage(eq(UID_10_2), eq(PACKAGE_2), eq(false)); verify(l, times(0)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(1)).unblockAlarmsForUidPackage(eq(UID_10_2), eq(PACKAGE_2)); reset(l); setAppOps(UID_10_2, PACKAGE_2, false); verify(l, times(0)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); // Test overlap with battery saver mPowerSaveMode = true; mPowerSaveObserver.accept(getPowerSaveState()); setAppOps(UID_10_2, PACKAGE_2, true); waitUntilMainHandlerDrain(); verify(l, times(1)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(1)).updateJobsForUidPackage(eq(UID_10_2), eq(PACKAGE_2), anyBoolean()); verify(l, times(1)).updateBackgroundRestrictedForUidPackage(eq(UID_10_2), eq(PACKAGE_2), eq(true)); verify(l, times(1)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); // Battery saver off. mPowerSaveMode = false; mPowerSaveObserver.accept(getPowerSaveState()); waitUntilMainHandlerDrain(); verify(l, times(1)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(1)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); // ------------------------------------------------------------------------- // Tests with system/user/temp exemption list. instance.setPowerSaveExemptionListAppIds(new int[] {UID_1, UID_2}, new int[] {}, new int[] {}); waitUntilMainHandlerDrain(); verify(l, times(1)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(1)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); verify(l, times(1)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); instance.setPowerSaveExemptionListAppIds(new int[] {UID_2}, new int[] {}, new int[] {}); waitUntilMainHandlerDrain(); verify(l, times(1)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(1)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); // Update temp exemption list. instance.setPowerSaveExemptionListAppIds(new int[] {UID_2}, new int[] {}, new int[] {UID_1, UID_3}); waitUntilMainHandlerDrain(); verify(l, times(1)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); instance.setPowerSaveExemptionListAppIds(new int[] {UID_2}, new int[] {}, new int[] {UID_3}); waitUntilMainHandlerDrain(); verify(l, times(1)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); // Do the same thing with battery saver on. mPowerSaveMode = true; mPowerSaveObserver.accept(getPowerSaveState()); waitUntilMainHandlerDrain(); verify(l, times(1)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(1)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); instance.setPowerSaveExemptionListAppIds(new int[] {UID_1, UID_2}, new int[] {}, new int[] {}); waitUntilMainHandlerDrain(); // Called once for updating all exemption list and once for updating temp exemption list verify(l, times(2)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(1)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); verify(l, times(1)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); instance.setPowerSaveExemptionListAppIds(new int[] {UID_2}, new int[] {}, new int[] {}); waitUntilMainHandlerDrain(); verify(l, times(1)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(1)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); // Update temp exemption list. instance.setPowerSaveExemptionListAppIds(new int[] {UID_2}, new int[] {}, new int[] {UID_1, UID_3}); waitUntilMainHandlerDrain(); verify(l, times(1)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); instance.setPowerSaveExemptionListAppIds(new int[] {UID_2}, new int[] {}, new int[] {UID_3}); waitUntilMainHandlerDrain(); verify(l, times(1)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(anyInt(), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(anyInt()); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); reset(l); // ------------------------------------------------------------------------- // Tests with proc state changes. // With battery saver. // Battery saver is already on. mIUidObserver.onUidActive(UID_10_1); waitUntilMainHandlerDrain(); waitUntilMainHandlerDrain(); verify(l, times(0)).updateAllJobs(); verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); verify(l, times(0)).removeAlarmsForUid(anyInt()); verify(l, times(0)).handleUidCachedChanged(anyInt(), anyBoolean()); reset(l); mIUidObserver.onUidGone(UID_10_1, true); waitUntilMainHandlerDrain(); waitUntilMainHandlerDrain(); verify(l, times(0)).updateAllJobs(); verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); verify(l, times(1)).removeAlarmsForUid(UID_10_1); verify(l, times(0)).handleUidCachedChanged(anyInt(), anyBoolean()); reset(l); mIUidObserver.onUidActive(UID_10_1); waitUntilMainHandlerDrain(); waitUntilMainHandlerDrain(); verify(l, times(0)).updateAllJobs(); verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); verify(l, times(0)).removeAlarmsForUid(anyInt()); verify(l, times(0)).handleUidCachedChanged(anyInt(), anyBoolean()); reset(l); mIUidObserver.onUidIdle(UID_10_1, true); waitUntilMainHandlerDrain(); waitUntilMainHandlerDrain(); verify(l, times(0)).updateAllJobs(); verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); verify(l, times(1)).removeAlarmsForUid(UID_10_1); verify(l, times(0)).handleUidCachedChanged(anyInt(), anyBoolean()); reset(l); mIUidObserver.onUidCachedChanged(UID_10_1, true); waitUntilMainHandlerDrain(); waitUntilMainHandlerDrain(); verify(l, times(0)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); verify(l, times(0)).removeAlarmsForUid(anyInt()); verify(l, times(1)).handleUidCachedChanged(UID_10_1, true); reset(l); mIUidObserver.onUidCachedChanged(UID_10_1, false); waitUntilMainHandlerDrain(); waitUntilMainHandlerDrain(); verify(l, times(0)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); verify(l, times(0)).removeAlarmsForUid(anyInt()); verify(l, times(1)).handleUidCachedChanged(UID_10_1, false); reset(l); // Without battery saver. mPowerSaveMode = false; mPowerSaveObserver.accept(getPowerSaveState()); waitUntilMainHandlerDrain(); verify(l, times(1)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(1)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); verify(l, times(0)).removeAlarmsForUid(anyInt()); verify(l, times(0)).handleUidCachedChanged(anyInt(), anyBoolean()); reset(l); mIUidObserver.onUidActive(UID_10_1); waitUntilMainHandlerDrain(); waitUntilMainHandlerDrain(); verify(l, times(0)).updateAllJobs(); verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); verify(l, times(0)).removeAlarmsForUid(anyInt()); verify(l, times(0)).handleUidCachedChanged(anyInt(), anyBoolean()); reset(l); mIUidObserver.onUidGone(UID_10_1, true); waitUntilMainHandlerDrain(); waitUntilMainHandlerDrain(); verify(l, times(0)).updateAllJobs(); verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); verify(l, times(1)).removeAlarmsForUid(UID_10_1); verify(l, times(0)).handleUidCachedChanged(anyInt(), anyBoolean()); reset(l); mIUidObserver.onUidActive(UID_10_1); waitUntilMainHandlerDrain(); waitUntilMainHandlerDrain(); verify(l, times(0)).updateAllJobs(); verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(1)).unblockAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); verify(l, times(0)).removeAlarmsForUid(anyInt()); verify(l, times(0)).handleUidCachedChanged(anyInt(), anyBoolean()); reset(l); mIUidObserver.onUidIdle(UID_10_1, true); waitUntilMainHandlerDrain(); waitUntilMainHandlerDrain(); verify(l, times(0)).updateAllJobs(); verify(l, times(1)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(1)).updateAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); verify(l, times(1)).removeAlarmsForUid(UID_10_1); verify(l, times(0)).handleUidCachedChanged(anyInt(), anyBoolean()); reset(l); mIUidObserver.onUidCachedChanged(UID_10_1, true); waitUntilMainHandlerDrain(); waitUntilMainHandlerDrain(); verify(l, times(0)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); verify(l, times(0)).removeAlarmsForUid(anyInt()); verify(l, times(1)).handleUidCachedChanged(UID_10_1, true); reset(l); mIUidObserver.onUidCachedChanged(UID_10_1, false); waitUntilMainHandlerDrain(); waitUntilMainHandlerDrain(); verify(l, times(0)).updateAllJobs(); verify(l, times(0)).updateJobsForUid(eq(UID_10_1), anyBoolean()); verify(l, times(0)).updateJobsForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateBackgroundRestrictedForUidPackage(anyInt(), anyString(), anyBoolean()); verify(l, times(0)).updateAllAlarms(); verify(l, times(0)).updateAlarmsForUid(eq(UID_10_1)); verify(l, times(0)).unblockAllUnrestrictedAlarms(); verify(l, times(0)).unblockAlarmsForUid(anyInt()); verify(l, times(0)).unblockAlarmsForUidPackage(anyInt(), anyString()); verify(l, times(0)).removeAlarmsForUid(anyInt()); verify(l, times(1)).handleUidCachedChanged(UID_10_1, false); reset(l); } @Test public void testUserRemoved() throws Exception { final AppStateTrackerTestable instance = newInstance(); callStart(instance); mIUidObserver.onUidActive(UID_1); mIUidObserver.onUidActive(UID_10_1); waitUntilMainHandlerDrain(); waitUntilMainHandlerDrain(); setAppOps(UID_2, PACKAGE_2, true); setAppOps(UID_10_2, PACKAGE_2, true); assertTrue(instance.isUidActive(UID_1)); assertTrue(instance.isUidActive(UID_10_1)); assertFalse(instance.isRunAnyInBackgroundAppOpsAllowed(UID_2, PACKAGE_2)); assertFalse(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_2, PACKAGE_2)); final Intent intent = new Intent(Intent.ACTION_USER_REMOVED); intent.putExtra(Intent.EXTRA_USER_HANDLE, 10); mReceiver.onReceive(mMockContext, intent); waitUntilMainHandlerDrain(); assertTrue(instance.isUidActive(UID_1)); assertFalse(instance.isUidActive(UID_10_1)); assertFalse(instance.isRunAnyInBackgroundAppOpsAllowed(UID_2, PACKAGE_2)); assertTrue(instance.isRunAnyInBackgroundAppOpsAllowed(UID_10_2, PACKAGE_2)); } @Test public void testSmallBatteryAndPluggedIn() throws Exception { // This is a small battery device mIsSmallBatteryDevice = true; final AppStateTrackerTestable instance = newInstance(); callStart(instance); assertFalse(instance.isForceAllAppsStandbyEnabled()); // Setting/experiment for all app standby for small battery is enabled mGlobalSettings.put(Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED, 1); instance.mFlagsObserver.onChange(true, Global.getUriFor(Global.FORCED_APP_STANDBY_FOR_SMALL_BATTERY_ENABLED)); assertTrue(instance.isForceAllAppsStandbyEnabled()); // When battery is plugged in, force app standby is disabled Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED); intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_USB); mReceiver.onReceive(mMockContext, intent); assertFalse(instance.isForceAllAppsStandbyEnabled()); // When battery stops plugged in, force app standby is enabled mReceiver.onReceive(mMockContext, new Intent(Intent.ACTION_BATTERY_CHANGED)); assertTrue(instance.isForceAllAppsStandbyEnabled()); } @Test public void testNotSmallBatteryAndPluggedIn() throws Exception { // Not a small battery device, so plugged in status should not affect forced app standby mIsSmallBatteryDevice = false; final AppStateTrackerTestable instance = newInstance(); callStart(instance); assertFalse(instance.isForceAllAppsStandbyEnabled()); mPowerSaveMode = true; mPowerSaveObserver.accept(getPowerSaveState()); assertTrue(instance.isForceAllAppsStandbyEnabled()); // When battery is plugged in, force app standby is unaffected Intent intent = new Intent(Intent.ACTION_BATTERY_CHANGED); intent.putExtra(BatteryManager.EXTRA_PLUGGED, BatteryManager.BATTERY_PLUGGED_USB); mReceiver.onReceive(mMockContext, intent); assertTrue(instance.isForceAllAppsStandbyEnabled()); // When battery stops plugged in, force app standby is unaffected mReceiver.onReceive(mMockContext, new Intent(Intent.ACTION_BATTERY_CHANGED)); assertTrue(instance.isForceAllAppsStandbyEnabled()); } @Test public void testStateClearedOnPackageRemoved() throws Exception { final AppStateTrackerTestable instance = newInstance(); callStart(instance); instance.mActiveUids.put(UID_1, true); instance.mRunAnyRestrictedPackages.add(Pair.create(UID_1, PACKAGE_1)); instance.mExemptedBucketPackages.add(UserHandle.getUserId(UID_2), PACKAGE_2); // Replace PACKAGE_1, nothing should change Intent packageRemoved = new Intent(Intent.ACTION_PACKAGE_REMOVED) .putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(UID_1)) .putExtra(Intent.EXTRA_UID, UID_1) .putExtra(Intent.EXTRA_REPLACING, true) .setData(Uri.fromParts(IntentFilter.SCHEME_PACKAGE, PACKAGE_1, null)); mReceiver.onReceive(mMockContext, packageRemoved); assertEquals(1, instance.mActiveUids.size()); assertEquals(1, instance.mRunAnyRestrictedPackages.size()); assertEquals(1, instance.mExemptedBucketPackages.size()); // Replace PACKAGE_2, nothing should change packageRemoved = new Intent(Intent.ACTION_PACKAGE_REMOVED) .putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(UID_2)) .putExtra(Intent.EXTRA_UID, UID_2) .putExtra(Intent.EXTRA_REPLACING, true) .setData(Uri.fromParts(IntentFilter.SCHEME_PACKAGE, PACKAGE_2, null)); mReceiver.onReceive(mMockContext, packageRemoved); assertEquals(1, instance.mActiveUids.size()); assertEquals(1, instance.mRunAnyRestrictedPackages.size()); assertEquals(1, instance.mExemptedBucketPackages.size()); // Remove PACKAGE_1 packageRemoved = new Intent(Intent.ACTION_PACKAGE_REMOVED) .putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(UID_1)) .putExtra(Intent.EXTRA_UID, UID_1) .setData(Uri.fromParts(IntentFilter.SCHEME_PACKAGE, PACKAGE_1, null)); mReceiver.onReceive(mMockContext, packageRemoved); assertEquals(0, instance.mActiveUids.size()); assertEquals(0, instance.mRunAnyRestrictedPackages.size()); assertEquals(1, instance.mExemptedBucketPackages.size()); // Remove PACKAGE_2 packageRemoved = new Intent(Intent.ACTION_PACKAGE_REMOVED) .putExtra(Intent.EXTRA_USER_HANDLE, UserHandle.getUserId(UID_2)) .putExtra(Intent.EXTRA_UID, UID_2) .setData(Uri.fromParts(IntentFilter.SCHEME_PACKAGE, PACKAGE_2, null)); mReceiver.onReceive(mMockContext, packageRemoved); assertEquals(0, instance.mActiveUids.size()); assertEquals(0, instance.mRunAnyRestrictedPackages.size()); assertEquals(0, instance.mExemptedBucketPackages.size()); } static int[] array(int... appIds) { Arrays.sort(appIds); return appIds; } private final Random mRandom = new Random(); int[] makeRandomArray() { final ArrayList<Integer> list = new ArrayList<>(); for (int i = 0; i < 5; i++) { if (mRandom.nextDouble() < 0.5) { list.add(i); } } return Arrays.stream(list.toArray(new Integer[list.size()])) .mapToInt(Integer::intValue).toArray(); } static boolean isAnyAppIdUnexemptSlow(int[] prevArray, int[] newArray) { Arrays.sort(newArray); // Just in case... for (int p : prevArray) { if (Arrays.binarySearch(newArray, p) < 0) { return true; } } return false; } private void checkAnyAppIdUnexempt(int[] prevArray, int[] newArray, boolean expected) { assertEquals("Input: " + Arrays.toString(prevArray) + " " + Arrays.toString(newArray), expected, AppStateTrackerImpl.isAnyAppIdUnexempt(prevArray, newArray)); // Also test isAnyAppIdUnexempt. assertEquals("Input: " + Arrays.toString(prevArray) + " " + Arrays.toString(newArray), expected, isAnyAppIdUnexemptSlow(prevArray, newArray)); } @Test public void isAnyAppIdUnexempt() { checkAnyAppIdUnexempt(array(), array(), false); checkAnyAppIdUnexempt(array(1), array(), true); checkAnyAppIdUnexempt(array(1), array(1), false); checkAnyAppIdUnexempt(array(1), array(0, 1), false); checkAnyAppIdUnexempt(array(1), array(0, 1, 2), false); checkAnyAppIdUnexempt(array(1), array(0, 1, 2), false); checkAnyAppIdUnexempt(array(1, 2, 10), array(), true); checkAnyAppIdUnexempt(array(1, 2, 10), array(1, 2), true); checkAnyAppIdUnexempt(array(1, 2, 10), array(1, 2, 10), false); checkAnyAppIdUnexempt(array(1, 2, 10), array(2, 10), true); checkAnyAppIdUnexempt(array(1, 2, 10), array(0, 1, 2, 4, 3, 10), false); checkAnyAppIdUnexempt(array(1, 2, 10), array(0, 0, 1, 2, 10), false); // Random test int trueCount = 0; final int count = 10000; for (int i = 0; i < count; i++) { final int[] array1 = makeRandomArray(); final int[] array2 = makeRandomArray(); final boolean expected = isAnyAppIdUnexemptSlow(array1, array2); final boolean actual = AppStateTrackerImpl.isAnyAppIdUnexempt(array1, array2); assertEquals("Input: " + Arrays.toString(array1) + " " + Arrays.toString(array2), expected, actual); if (expected) { trueCount++; } } // Make sure makeRandomArray() didn't generate all same arrays by accident. assertTrue(trueCount > 0); assertTrue(trueCount < count); } }