Loading packages/SystemUI/tests/src/com/android/systemui/statusbar/RunningFgsControllerTest.java 0 → 100644 +340 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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.systemui.statusbar; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.IActivityManager; import android.app.IForegroundServiceObserver; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.testing.AndroidTestingRunner; import android.util.Pair; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleOwner; import androidx.test.filters.MediumTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.policy.RunningFgsController; import com.android.systemui.statusbar.policy.RunningFgsController.UserPackageTime; import com.android.systemui.statusbar.policy.RunningFgsControllerImpl; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Random; import java.util.function.Consumer; @MediumTest @RunWith(AndroidTestingRunner.class) public class RunningFgsControllerTest extends SysuiTestCase { private RunningFgsController mController; private FakeSystemClock mSystemClock = new FakeSystemClock(); private FakeExecutor mExecutor = new FakeExecutor(mSystemClock); private TestCallback mCallback = new TestCallback(); @Mock private IActivityManager mActivityManager; @Mock private Lifecycle mLifecycle; @Mock private LifecycleOwner mLifecycleOwner; @Before public void setUp() { MockitoAnnotations.initMocks(this); when(mLifecycleOwner.getLifecycle()).thenReturn(mLifecycle); mController = new RunningFgsControllerImpl(mExecutor, mSystemClock, mActivityManager); } @Test public void testInitRegistersListenerInImpl() throws RemoteException { ((RunningFgsControllerImpl) mController).init(); verify(mActivityManager, times(1)).registerForegroundServiceObserver(any()); } @Test public void testAddCallbackCallsInitInImpl() { verifyInitIsCalled(controller -> controller.addCallback(mCallback)); } @Test public void testRemoveCallbackCallsInitInImpl() { verifyInitIsCalled(controller -> controller.removeCallback(mCallback)); } @Test public void testObserve1CallsInitInImpl() { verifyInitIsCalled(controller -> controller.observe(mLifecycle, mCallback)); } @Test public void testObserve2CallsInitInImpl() { verifyInitIsCalled(controller -> controller.observe(mLifecycleOwner, mCallback)); } @Test public void testGetPackagesWithFgsCallsInitInImpl() { verifyInitIsCalled(controller -> controller.getPackagesWithFgs()); } @Test public void testStopFgsCallsInitInImpl() { verifyInitIsCalled(controller -> controller.stopFgs(0, "")); } /** * Tests that callbacks can be added */ @Test public void testAddCallback() throws RemoteException { String testPackageName = "testPackageName"; int testUserId = 0; IForegroundServiceObserver observer = prepareObserver(); mController.addCallback(mCallback); observer.onForegroundStateChanged(new Binder(), testPackageName, testUserId, true); mExecutor.advanceClockToLast(); mExecutor.runAllReady(); assertEquals("Callback should have been invoked exactly once.", 1, mCallback.mInvocations.size()); List<UserPackageTime> userPackageTimes = mCallback.mInvocations.get(0); assertEquals("There should have only been one package in callback. packages=" + userPackageTimes, 1, userPackageTimes.size()); UserPackageTime upt = userPackageTimes.get(0); assertEquals(testPackageName, upt.getPackageName()); assertEquals(testUserId, upt.getUserId()); } /** * Tests that callbacks can be removed. This test is only meaningful if * {@link #testAddCallback()} can pass. */ @Test public void testRemoveCallback() throws RemoteException { String testPackageName = "testPackageName"; int testUserId = 0; IForegroundServiceObserver observer = prepareObserver(); mController.addCallback(mCallback); mController.removeCallback(mCallback); observer.onForegroundStateChanged(new Binder(), testPackageName, testUserId, true); mExecutor.advanceClockToLast(); mExecutor.runAllReady(); assertEquals("Callback should not have been invoked.", 0, mCallback.mInvocations.size()); } /** * Tests packages are added when the controller receives a callback from activity manager for * a foreground service start. */ @Test public void testGetPackagesWithFgsAddingPackages() throws RemoteException { int numPackages = 20; int numUsers = 3; IForegroundServiceObserver observer = prepareObserver(); assertEquals("List should be empty", 0, mController.getPackagesWithFgs().size()); List<Pair<Integer, String>> addedPackages = new ArrayList<>(); for (int pkgNumber = 0; pkgNumber < numPackages; pkgNumber++) { for (int userId = 0; userId < numUsers; userId++) { String packageName = "package.name." + pkgNumber; addedPackages.add(new Pair(userId, packageName)); observer.onForegroundStateChanged(new Binder(), packageName, userId, true); containsAllAddedPackages(addedPackages, mController.getPackagesWithFgs()); } } } /** * Tests packages are removed when the controller receives a callback from activity manager for * a foreground service ending. */ @Test public void testGetPackagesWithFgsRemovingPackages() throws RemoteException { int numPackages = 20; int numUsers = 3; int arrayLength = numPackages * numUsers; String[] packages = new String[arrayLength]; int[] users = new int[arrayLength]; IBinder[] tokens = new IBinder[arrayLength]; for (int pkgNumber = 0; pkgNumber < numPackages; pkgNumber++) { for (int userId = 0; userId < numUsers; userId++) { int i = pkgNumber * numUsers + userId; packages[i] = "package.name." + pkgNumber; users[i] = userId; tokens[i] = new Binder(); } } IForegroundServiceObserver observer = prepareObserver(); for (int i = 0; i < packages.length; i++) { observer.onForegroundStateChanged(tokens[i], packages[i], users[i], true); } assertEquals(packages.length, mController.getPackagesWithFgs().size()); List<Integer> removeOrder = new ArrayList<>(); for (int i = 0; i < packages.length; i++) { removeOrder.add(i); } Collections.shuffle(removeOrder, new Random(12345)); for (int idx : removeOrder) { removePackageAndAssertRemovedFromList(observer, tokens[idx], packages[idx], users[idx]); } assertEquals(0, mController.getPackagesWithFgs().size()); } /** * Tests a call on stopFgs forwards to activity manager. */ @Test public void testStopFgs() throws RemoteException { String pkgName = "package.name"; mController.stopFgs(0, pkgName); verify(mActivityManager).makeServicesNonForeground(pkgName, 0); } /** * Tests a package which starts multiple services is only listed once and is only removed once * all services are stopped. */ @Test public void testSinglePackageWithMultipleServices() throws RemoteException { String packageName = "package.name"; int userId = 0; IBinder serviceToken1 = new Binder(); IBinder serviceToken2 = new Binder(); IForegroundServiceObserver observer = prepareObserver(); assertEquals(0, mController.getPackagesWithFgs().size()); observer.onForegroundStateChanged(serviceToken1, packageName, userId, true); assertSinglePackage(packageName, userId); observer.onForegroundStateChanged(serviceToken2, packageName, userId, true); assertSinglePackage(packageName, userId); observer.onForegroundStateChanged(serviceToken2, packageName, userId, false); assertSinglePackage(packageName, userId); observer.onForegroundStateChanged(serviceToken1, packageName, userId, false); assertEquals(0, mController.getPackagesWithFgs().size()); } private IForegroundServiceObserver prepareObserver() throws RemoteException { mController.getPackagesWithFgs(); ArgumentCaptor<IForegroundServiceObserver> argumentCaptor = ArgumentCaptor.forClass(IForegroundServiceObserver.class); verify(mActivityManager).registerForegroundServiceObserver(argumentCaptor.capture()); return argumentCaptor.getValue(); } private void verifyInitIsCalled(Consumer<RunningFgsControllerImpl> c) { RunningFgsControllerImpl spiedController = Mockito.spy( ((RunningFgsControllerImpl) mController)); c.accept(spiedController); verify(spiedController, atLeastOnce()).init(); } private void containsAllAddedPackages(List<Pair<Integer, String>> addedPackages, List<UserPackageTime> runningFgsPackages) { for (Pair<Integer, String> userPkg : addedPackages) { assertTrue(userPkg + " was not found in returned list", runningFgsPackages.stream().anyMatch( upt -> userPkg.first == upt.getUserId() && Objects.equals(upt.getPackageName(), userPkg.second))); } for (UserPackageTime upt : runningFgsPackages) { int userId = upt.getUserId(); String packageName = upt.getPackageName(); assertTrue("Unknown <user=" + userId + ", package=" + packageName + ">" + " in returned list", addedPackages.stream().anyMatch(userPkg -> userPkg.first == userId && Objects.equals(packageName, userPkg.second))); } } private void removePackageAndAssertRemovedFromList(IForegroundServiceObserver observer, IBinder token, String pkg, int userId) throws RemoteException { observer.onForegroundStateChanged(token, pkg, userId, false); List<UserPackageTime> packagesWithFgs = mController.getPackagesWithFgs(); assertFalse("Package \"" + pkg + "\" was not removed", packagesWithFgs.stream().anyMatch(upt -> Objects.equals(upt.getPackageName(), pkg) && upt.getUserId() == userId)); } private void assertSinglePackage(String packageName, int userId) { assertEquals(1, mController.getPackagesWithFgs().size()); assertEquals(packageName, mController.getPackagesWithFgs().get(0).getPackageName()); assertEquals(userId, mController.getPackagesWithFgs().get(0).getUserId()); } private static class TestCallback implements RunningFgsController.Callback { private List<List<UserPackageTime>> mInvocations = new ArrayList<>(); @Override public void onFgsPackagesChanged(List<UserPackageTime> packages) { mInvocations.add(packages); } } } Loading
packages/SystemUI/tests/src/com/android/systemui/statusbar/RunningFgsControllerTest.java 0 → 100644 +340 −0 Original line number Diff line number Diff line /* * Copyright (C) 2021 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.systemui.statusbar; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.atLeastOnce; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.app.IActivityManager; import android.app.IForegroundServiceObserver; import android.os.Binder; import android.os.IBinder; import android.os.RemoteException; import android.testing.AndroidTestingRunner; import android.util.Pair; import androidx.lifecycle.Lifecycle; import androidx.lifecycle.LifecycleOwner; import androidx.test.filters.MediumTest; import com.android.systemui.SysuiTestCase; import com.android.systemui.statusbar.policy.RunningFgsController; import com.android.systemui.statusbar.policy.RunningFgsController.UserPackageTime; import com.android.systemui.statusbar.policy.RunningFgsControllerImpl; import com.android.systemui.util.concurrency.FakeExecutor; import com.android.systemui.util.time.FakeSystemClock; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.Mockito; import org.mockito.MockitoAnnotations; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Objects; import java.util.Random; import java.util.function.Consumer; @MediumTest @RunWith(AndroidTestingRunner.class) public class RunningFgsControllerTest extends SysuiTestCase { private RunningFgsController mController; private FakeSystemClock mSystemClock = new FakeSystemClock(); private FakeExecutor mExecutor = new FakeExecutor(mSystemClock); private TestCallback mCallback = new TestCallback(); @Mock private IActivityManager mActivityManager; @Mock private Lifecycle mLifecycle; @Mock private LifecycleOwner mLifecycleOwner; @Before public void setUp() { MockitoAnnotations.initMocks(this); when(mLifecycleOwner.getLifecycle()).thenReturn(mLifecycle); mController = new RunningFgsControllerImpl(mExecutor, mSystemClock, mActivityManager); } @Test public void testInitRegistersListenerInImpl() throws RemoteException { ((RunningFgsControllerImpl) mController).init(); verify(mActivityManager, times(1)).registerForegroundServiceObserver(any()); } @Test public void testAddCallbackCallsInitInImpl() { verifyInitIsCalled(controller -> controller.addCallback(mCallback)); } @Test public void testRemoveCallbackCallsInitInImpl() { verifyInitIsCalled(controller -> controller.removeCallback(mCallback)); } @Test public void testObserve1CallsInitInImpl() { verifyInitIsCalled(controller -> controller.observe(mLifecycle, mCallback)); } @Test public void testObserve2CallsInitInImpl() { verifyInitIsCalled(controller -> controller.observe(mLifecycleOwner, mCallback)); } @Test public void testGetPackagesWithFgsCallsInitInImpl() { verifyInitIsCalled(controller -> controller.getPackagesWithFgs()); } @Test public void testStopFgsCallsInitInImpl() { verifyInitIsCalled(controller -> controller.stopFgs(0, "")); } /** * Tests that callbacks can be added */ @Test public void testAddCallback() throws RemoteException { String testPackageName = "testPackageName"; int testUserId = 0; IForegroundServiceObserver observer = prepareObserver(); mController.addCallback(mCallback); observer.onForegroundStateChanged(new Binder(), testPackageName, testUserId, true); mExecutor.advanceClockToLast(); mExecutor.runAllReady(); assertEquals("Callback should have been invoked exactly once.", 1, mCallback.mInvocations.size()); List<UserPackageTime> userPackageTimes = mCallback.mInvocations.get(0); assertEquals("There should have only been one package in callback. packages=" + userPackageTimes, 1, userPackageTimes.size()); UserPackageTime upt = userPackageTimes.get(0); assertEquals(testPackageName, upt.getPackageName()); assertEquals(testUserId, upt.getUserId()); } /** * Tests that callbacks can be removed. This test is only meaningful if * {@link #testAddCallback()} can pass. */ @Test public void testRemoveCallback() throws RemoteException { String testPackageName = "testPackageName"; int testUserId = 0; IForegroundServiceObserver observer = prepareObserver(); mController.addCallback(mCallback); mController.removeCallback(mCallback); observer.onForegroundStateChanged(new Binder(), testPackageName, testUserId, true); mExecutor.advanceClockToLast(); mExecutor.runAllReady(); assertEquals("Callback should not have been invoked.", 0, mCallback.mInvocations.size()); } /** * Tests packages are added when the controller receives a callback from activity manager for * a foreground service start. */ @Test public void testGetPackagesWithFgsAddingPackages() throws RemoteException { int numPackages = 20; int numUsers = 3; IForegroundServiceObserver observer = prepareObserver(); assertEquals("List should be empty", 0, mController.getPackagesWithFgs().size()); List<Pair<Integer, String>> addedPackages = new ArrayList<>(); for (int pkgNumber = 0; pkgNumber < numPackages; pkgNumber++) { for (int userId = 0; userId < numUsers; userId++) { String packageName = "package.name." + pkgNumber; addedPackages.add(new Pair(userId, packageName)); observer.onForegroundStateChanged(new Binder(), packageName, userId, true); containsAllAddedPackages(addedPackages, mController.getPackagesWithFgs()); } } } /** * Tests packages are removed when the controller receives a callback from activity manager for * a foreground service ending. */ @Test public void testGetPackagesWithFgsRemovingPackages() throws RemoteException { int numPackages = 20; int numUsers = 3; int arrayLength = numPackages * numUsers; String[] packages = new String[arrayLength]; int[] users = new int[arrayLength]; IBinder[] tokens = new IBinder[arrayLength]; for (int pkgNumber = 0; pkgNumber < numPackages; pkgNumber++) { for (int userId = 0; userId < numUsers; userId++) { int i = pkgNumber * numUsers + userId; packages[i] = "package.name." + pkgNumber; users[i] = userId; tokens[i] = new Binder(); } } IForegroundServiceObserver observer = prepareObserver(); for (int i = 0; i < packages.length; i++) { observer.onForegroundStateChanged(tokens[i], packages[i], users[i], true); } assertEquals(packages.length, mController.getPackagesWithFgs().size()); List<Integer> removeOrder = new ArrayList<>(); for (int i = 0; i < packages.length; i++) { removeOrder.add(i); } Collections.shuffle(removeOrder, new Random(12345)); for (int idx : removeOrder) { removePackageAndAssertRemovedFromList(observer, tokens[idx], packages[idx], users[idx]); } assertEquals(0, mController.getPackagesWithFgs().size()); } /** * Tests a call on stopFgs forwards to activity manager. */ @Test public void testStopFgs() throws RemoteException { String pkgName = "package.name"; mController.stopFgs(0, pkgName); verify(mActivityManager).makeServicesNonForeground(pkgName, 0); } /** * Tests a package which starts multiple services is only listed once and is only removed once * all services are stopped. */ @Test public void testSinglePackageWithMultipleServices() throws RemoteException { String packageName = "package.name"; int userId = 0; IBinder serviceToken1 = new Binder(); IBinder serviceToken2 = new Binder(); IForegroundServiceObserver observer = prepareObserver(); assertEquals(0, mController.getPackagesWithFgs().size()); observer.onForegroundStateChanged(serviceToken1, packageName, userId, true); assertSinglePackage(packageName, userId); observer.onForegroundStateChanged(serviceToken2, packageName, userId, true); assertSinglePackage(packageName, userId); observer.onForegroundStateChanged(serviceToken2, packageName, userId, false); assertSinglePackage(packageName, userId); observer.onForegroundStateChanged(serviceToken1, packageName, userId, false); assertEquals(0, mController.getPackagesWithFgs().size()); } private IForegroundServiceObserver prepareObserver() throws RemoteException { mController.getPackagesWithFgs(); ArgumentCaptor<IForegroundServiceObserver> argumentCaptor = ArgumentCaptor.forClass(IForegroundServiceObserver.class); verify(mActivityManager).registerForegroundServiceObserver(argumentCaptor.capture()); return argumentCaptor.getValue(); } private void verifyInitIsCalled(Consumer<RunningFgsControllerImpl> c) { RunningFgsControllerImpl spiedController = Mockito.spy( ((RunningFgsControllerImpl) mController)); c.accept(spiedController); verify(spiedController, atLeastOnce()).init(); } private void containsAllAddedPackages(List<Pair<Integer, String>> addedPackages, List<UserPackageTime> runningFgsPackages) { for (Pair<Integer, String> userPkg : addedPackages) { assertTrue(userPkg + " was not found in returned list", runningFgsPackages.stream().anyMatch( upt -> userPkg.first == upt.getUserId() && Objects.equals(upt.getPackageName(), userPkg.second))); } for (UserPackageTime upt : runningFgsPackages) { int userId = upt.getUserId(); String packageName = upt.getPackageName(); assertTrue("Unknown <user=" + userId + ", package=" + packageName + ">" + " in returned list", addedPackages.stream().anyMatch(userPkg -> userPkg.first == userId && Objects.equals(packageName, userPkg.second))); } } private void removePackageAndAssertRemovedFromList(IForegroundServiceObserver observer, IBinder token, String pkg, int userId) throws RemoteException { observer.onForegroundStateChanged(token, pkg, userId, false); List<UserPackageTime> packagesWithFgs = mController.getPackagesWithFgs(); assertFalse("Package \"" + pkg + "\" was not removed", packagesWithFgs.stream().anyMatch(upt -> Objects.equals(upt.getPackageName(), pkg) && upt.getUserId() == userId)); } private void assertSinglePackage(String packageName, int userId) { assertEquals(1, mController.getPackagesWithFgs().size()); assertEquals(packageName, mController.getPackagesWithFgs().get(0).getPackageName()); assertEquals(userId, mController.getPackagesWithFgs().get(0).getUserId()); } private static class TestCallback implements RunningFgsController.Callback { private List<List<UserPackageTime>> mInvocations = new ArrayList<>(); @Override public void onFgsPackagesChanged(List<UserPackageTime> packages) { mInvocations.add(packages); } } }