Merge "Only show packages for the current user in FgsManager" into tm-dev am: a82c616519 am: 720098873b

Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/17298359

Change-Id: I5cf67d5b34ad96e345933cf9fd061e6e81cd8b4b
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
TreeHugger Robot
2022-05-13 08:52:27 +00:00
committed by Automerger Merge Worker
2 changed files with 349 additions and 24 deletions

View File

@@ -23,10 +23,12 @@ import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageManager
import android.content.pm.UserInfo
import android.graphics.drawable.Drawable
import android.os.IBinder
import android.os.PowerExemptionManager
import android.os.RemoteException
import android.os.UserHandle
import android.provider.DeviceConfig.NAMESPACE_SYSTEMUI
import android.text.format.DateUtils
import android.util.ArrayMap
@@ -51,6 +53,7 @@ import com.android.systemui.dagger.qualifiers.Background
import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.dump.DumpManager
import com.android.systemui.shared.system.SysUiStatsLog
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.phone.SystemUIDialog
import com.android.systemui.util.DeviceConfigProxy
import com.android.systemui.util.indentIfPossible
@@ -69,6 +72,7 @@ class FgsManagerController @Inject constructor(
private val systemClock: SystemClock,
private val activityManager: IActivityManager,
private val packageManager: PackageManager,
private val userTracker: UserTracker,
private val deviceConfigProxy: DeviceConfigProxy,
private val dialogLaunchAnimator: DialogLaunchAnimator,
private val broadcastDispatcher: BroadcastDispatcher,
@@ -82,13 +86,20 @@ class FgsManagerController @Inject constructor(
var changesSinceDialog = false
private set
private var isAvailable = false
var isAvailable = false
private set
private val lock = Any()
@GuardedBy("lock")
var initialized = false
@GuardedBy("lock")
private var lastNumberOfVisiblePackages = 0
@GuardedBy("lock")
private var currentProfileIds = mutableSetOf<Int>()
@GuardedBy("lock")
private val runningServiceTokens = mutableMapOf<UserPackage, StartTimeAndTokens>()
@@ -101,6 +112,19 @@ class FgsManagerController @Inject constructor(
@GuardedBy("lock")
private var runningApps: ArrayMap<UserPackage, RunningApp> = ArrayMap()
private val userTrackerCallback = object : UserTracker.Callback {
override fun onUserChanged(newUser: Int, userContext: Context) {}
override fun onProfilesChanged(profiles: List<UserInfo>) {
synchronized(lock) {
currentProfileIds.clear()
currentProfileIds.addAll(profiles.map { it.id })
lastNumberOfVisiblePackages = 0
updateNumberOfVisibleRunningPackagesLocked()
}
}
}
interface OnNumberOfPackagesChangedListener {
fun onNumberOfPackagesChanged(numPackages: Int)
}
@@ -120,6 +144,10 @@ class FgsManagerController @Inject constructor(
e.rethrowFromSystemServer()
}
userTracker.addCallback(userTrackerCallback, backgroundExecutor)
currentProfileIds.addAll(userTracker.userProfiles.map { it.id })
deviceConfigProxy.addOnPropertiesChangedListener(NAMESPACE_SYSTEMUI,
backgroundExecutor) {
isAvailable = it.getBoolean(TASK_MANAGER_ENABLED, isAvailable)
@@ -153,10 +181,9 @@ class FgsManagerController @Inject constructor(
isForeground: Boolean
) {
synchronized(lock) {
val numPackagesBefore = getNumRunningPackagesLocked()
val userPackageKey = UserPackage(userId, packageName)
if (isForeground) {
runningServiceTokens.getOrPut(userPackageKey, { StartTimeAndTokens(systemClock) })
runningServiceTokens.getOrPut(userPackageKey) { StartTimeAndTokens(systemClock) }
.addToken(token)
} else {
if (runningServiceTokens[userPackageKey]?.also {
@@ -165,14 +192,7 @@ class FgsManagerController @Inject constructor(
}
}
val numPackagesAfter = getNumRunningPackagesLocked()
if (numPackagesAfter != numPackagesBefore) {
changesSinceDialog = true
onNumberOfPackagesChangedListeners.forEach {
backgroundExecutor.execute { it.onNumberOfPackagesChanged(numPackagesAfter) }
}
}
updateNumberOfVisibleRunningPackagesLocked()
updateAppItemsLocked()
}
@@ -209,18 +229,30 @@ class FgsManagerController @Inject constructor(
}
}
fun isAvailable(): Boolean {
return isAvailable
}
fun getNumRunningPackages(): Int {
synchronized(lock) {
return getNumRunningPackagesLocked()
return getNumVisiblePackagesLocked()
}
}
private fun getNumRunningPackagesLocked() =
runningServiceTokens.keys.count { it.uiControl != UIControl.HIDE_ENTRY }
private fun getNumVisiblePackagesLocked(): Int {
return runningServiceTokens.keys.count {
it.uiControl != UIControl.HIDE_ENTRY && currentProfileIds.contains(it.userId)
}
}
private fun updateNumberOfVisibleRunningPackagesLocked() {
val num = getNumVisiblePackagesLocked()
if (num != lastNumberOfVisiblePackages) {
lastNumberOfVisiblePackages = num
changesSinceDialog = true
onNumberOfPackagesChangedListeners.forEach {
backgroundExecutor.execute {
it.onNumberOfPackagesChanged(num)
}
}
}
}
fun shouldUpdateFooterVisibility() = dialog == null
@@ -289,7 +321,9 @@ class FgsManagerController @Inject constructor(
val ai = packageManager.getApplicationInfoAsUser(it.packageName, 0, it.userId)
runningApps[it] = RunningApp(it.userId, it.packageName,
runningServiceTokens[it]!!.startTime, it.uiControl,
ai.loadLabel(packageManager), ai.loadIcon(packageManager))
packageManager.getApplicationLabel(ai),
packageManager.getUserBadgedIcon(
packageManager.getApplicationIcon(ai), UserHandle.of(it.userId)))
logEvent(stopped = false, it.packageName, it.userId, runningApps[it]!!.timeStarted)
}
@@ -404,6 +438,7 @@ class FgsManagerController @Inject constructor(
val packageName: String
) {
val uid by lazy { packageManager.getPackageUidAsUser(packageName, userId) }
var backgroundRestrictionExemptionReason = PowerExemptionManager.REASON_DENIED
private var uiControlInitialized = false
var uiControl: UIControl = UIControl.NORMAL
@@ -416,7 +451,9 @@ class FgsManagerController @Inject constructor(
private set
fun updateUiControl() {
uiControl = when (activityManager.getBackgroundRestrictionExemptionReason(uid)) {
backgroundRestrictionExemptionReason =
activityManager.getBackgroundRestrictionExemptionReason(uid)
uiControl = when (backgroundRestrictionExemptionReason) {
PowerExemptionManager.REASON_SYSTEM_UID,
PowerExemptionManager.REASON_DEVICE_DEMO_MODE -> UIControl.HIDE_ENTRY
@@ -448,7 +485,7 @@ class FgsManagerController @Inject constructor(
pw.indentIfPossible {
pw.println("userId=$userId")
pw.println("packageName=$packageName")
pw.println("uiControl=$uiControl")
pw.println("uiControl=$uiControl (reason=$backgroundRestrictionExemptionReason)")
}
pw.println("]")
}
@@ -525,7 +562,7 @@ class FgsManagerController @Inject constructor(
pw.println("userId=$userId")
pw.println("packageName=$packageName")
pw.println("timeStarted=$timeStarted (time since start =" +
" ${systemClock.elapsedRealtime() - timeStarted}ms)\"")
" ${systemClock.elapsedRealtime() - timeStarted}ms)")
pw.println("uiControl=$uiControl")
pw.println("appLabel=$appLabel")
pw.println("icon=$icon")
@@ -542,6 +579,7 @@ class FgsManagerController @Inject constructor(
override fun dump(printwriter: PrintWriter, args: Array<out String>) {
val pw = IndentingPrintWriter(printwriter)
synchronized(lock) {
pw.println("current user profiles = $currentProfileIds")
pw.println("changesSinceDialog=$changesSinceDialog")
pw.println("Running service tokens: [")
pw.indentIfPossible {
@@ -560,8 +598,10 @@ class FgsManagerController @Inject constructor(
pw.indentIfPossible {
runningApps.forEach { (userPackage, runningApp) ->
pw.println("{")
userPackage.dump(pw)
runningApp.dump(pw, systemClock)
pw.indentIfPossible {
userPackage.dump(pw)
runningApp.dump(pw, systemClock)
}
pw.println("}")
}
}

View File

@@ -0,0 +1,285 @@
/*
* Copyright (C) 2022 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.qs;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.argThat;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.ArgumentMatchers.isNull;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.app.IActivityManager;
import android.app.IForegroundServiceObserver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.os.Binder;
import android.os.RemoteException;
import android.provider.DeviceConfig;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import androidx.test.filters.SmallTest;
import com.android.internal.config.sysui.SystemUiDeviceConfigFlags;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.DialogLaunchAnimator;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dump.DumpManager;
import com.android.systemui.settings.UserTracker;
import com.android.systemui.util.DeviceConfigProxyFake;
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.ArgumentMatchers;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
@RunWith(AndroidTestingRunner.class)
@TestableLooper.RunWithLooper
@SmallTest
public class FgsManagerControllerTest extends SysuiTestCase {
FakeSystemClock mSystemClock;
FakeExecutor mMainExecutor;
FakeExecutor mBackgroundExecutor;
DeviceConfigProxyFake mDeviceConfigProxyFake;
@Mock
IActivityManager mIActivityManager;
@Mock
PackageManager mPackageManager;
@Mock
UserTracker mUserTracker;
@Mock
DialogLaunchAnimator mDialogLaunchAnimator;
@Mock
BroadcastDispatcher mBroadcastDispatcher;
@Mock
DumpManager mDumpManager;
private FgsManagerController mFmc;
private IForegroundServiceObserver mIForegroundServiceObserver;
private UserTracker.Callback mUserTrackerCallback;
private BroadcastReceiver mShowFgsManagerReceiver;
private List<UserInfo> mUserProfiles;
@Before
public void setUp() throws RemoteException {
MockitoAnnotations.initMocks(this);
mDeviceConfigProxyFake = new DeviceConfigProxyFake();
mDeviceConfigProxyFake.setProperty(DeviceConfig.NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.TASK_MANAGER_ENABLED, "true", false);
mSystemClock = new FakeSystemClock();
mMainExecutor = new FakeExecutor(mSystemClock);
mBackgroundExecutor = new FakeExecutor(mSystemClock);
mUserProfiles = new ArrayList<>();
Mockito.doReturn(mUserProfiles).when(mUserTracker).getUserProfiles();
mFmc = createFgsManagerController();
}
@Test
public void testNumPackages() throws RemoteException {
setUserProfiles(0);
Binder b1 = new Binder();
Binder b2 = new Binder();
Assert.assertEquals(0, mFmc.getNumRunningPackages());
mIForegroundServiceObserver.onForegroundStateChanged(b1, "pkg1", 0, true);
Assert.assertEquals(1, mFmc.getNumRunningPackages());
mIForegroundServiceObserver.onForegroundStateChanged(b2, "pkg2", 0, true);
Assert.assertEquals(2, mFmc.getNumRunningPackages());
mIForegroundServiceObserver.onForegroundStateChanged(b1, "pkg1", 0, false);
Assert.assertEquals(1, mFmc.getNumRunningPackages());
mIForegroundServiceObserver.onForegroundStateChanged(b2, "pkg2", 0, false);
Assert.assertEquals(0, mFmc.getNumRunningPackages());
}
@Test
public void testNumPackagesDoesNotChangeWhenSecondFgsIsStarted() throws RemoteException {
setUserProfiles(0);
// Different tokens == different services
Binder b1 = new Binder();
Binder b2 = new Binder();
Assert.assertEquals(0, mFmc.getNumRunningPackages());
mIForegroundServiceObserver.onForegroundStateChanged(b1, "pkg1", 0, true);
Assert.assertEquals(1, mFmc.getNumRunningPackages());
mIForegroundServiceObserver.onForegroundStateChanged(b2, "pkg1", 0, true);
Assert.assertEquals(1, mFmc.getNumRunningPackages());
mIForegroundServiceObserver.onForegroundStateChanged(b1, "pkg1", 0, false);
Assert.assertEquals(1, mFmc.getNumRunningPackages());
mIForegroundServiceObserver.onForegroundStateChanged(b2, "pkg1", 0, false);
Assert.assertEquals(0, mFmc.getNumRunningPackages());
}
@Test
public void testNumPackagesListener() throws RemoteException {
setUserProfiles(0);
FgsManagerController.OnNumberOfPackagesChangedListener onNumberOfPackagesChangedListener =
Mockito.mock(FgsManagerController.OnNumberOfPackagesChangedListener.class);
mFmc.addOnNumberOfPackagesChangedListener(onNumberOfPackagesChangedListener);
Binder b1 = new Binder();
Binder b2 = new Binder();
verify(onNumberOfPackagesChangedListener, never()).onNumberOfPackagesChanged(anyInt());
mIForegroundServiceObserver.onForegroundStateChanged(b1, "pkg1", 0, true);
mBackgroundExecutor.advanceClockToLast();
mBackgroundExecutor.runAllReady();
verify(onNumberOfPackagesChangedListener).onNumberOfPackagesChanged(1);
mIForegroundServiceObserver.onForegroundStateChanged(b2, "pkg2", 0, true);
mBackgroundExecutor.advanceClockToLast();
mBackgroundExecutor.runAllReady();
verify(onNumberOfPackagesChangedListener).onNumberOfPackagesChanged(2);
mIForegroundServiceObserver.onForegroundStateChanged(b1, "pkg1", 0, false);
mBackgroundExecutor.advanceClockToLast();
mBackgroundExecutor.runAllReady();
verify(onNumberOfPackagesChangedListener, times(2)).onNumberOfPackagesChanged(1);
mIForegroundServiceObserver.onForegroundStateChanged(b2, "pkg2", 0, false);
mBackgroundExecutor.advanceClockToLast();
mBackgroundExecutor.runAllReady();
verify(onNumberOfPackagesChangedListener).onNumberOfPackagesChanged(0);
}
@Test
public void testChangesSinceLastDialog() throws RemoteException {
setUserProfiles(0);
Assert.assertFalse(mFmc.getChangesSinceDialog());
mIForegroundServiceObserver.onForegroundStateChanged(new Binder(), "pkg", 0, true);
Assert.assertTrue(mFmc.getChangesSinceDialog());
}
@Test
public void testProfilePackagesCounted() throws RemoteException {
setUserProfiles(0, 10);
mIForegroundServiceObserver.onForegroundStateChanged(new Binder(), "pkg1", 0, true);
mIForegroundServiceObserver.onForegroundStateChanged(new Binder(), "pkg2", 10, true);
Assert.assertEquals(2, mFmc.getNumRunningPackages());
}
@Test
public void testSecondaryUserPackagesAreNotCounted() throws RemoteException {
setUserProfiles(0);
mIForegroundServiceObserver.onForegroundStateChanged(new Binder(), "pkg1", 0, true);
mIForegroundServiceObserver.onForegroundStateChanged(new Binder(), "pkg2", 10, true);
Assert.assertEquals(1, mFmc.getNumRunningPackages());
}
@Test
public void testSecondaryUserPackagesAreCountedWhenUserSwitch() throws RemoteException {
setUserProfiles(0);
mIForegroundServiceObserver.onForegroundStateChanged(new Binder(), "pkg1", 0, true);
mIForegroundServiceObserver.onForegroundStateChanged(new Binder(), "pkg2", 10, true);
mIForegroundServiceObserver.onForegroundStateChanged(new Binder(), "pkg3", 10, true);
Assert.assertEquals(1, mFmc.getNumRunningPackages());
setUserProfiles(10);
Assert.assertEquals(2, mFmc.getNumRunningPackages());
}
FgsManagerController createFgsManagerController() throws RemoteException {
ArgumentCaptor<IForegroundServiceObserver> iForegroundServiceObserverArgumentCaptor =
ArgumentCaptor.forClass(IForegroundServiceObserver.class);
ArgumentCaptor<UserTracker.Callback> userTrackerCallbackArgumentCaptor =
ArgumentCaptor.forClass(UserTracker.Callback.class);
ArgumentCaptor<BroadcastReceiver> showFgsManagerReceiverArgumentCaptor =
ArgumentCaptor.forClass(BroadcastReceiver.class);
FgsManagerController result = new FgsManagerController(
mContext,
mMainExecutor,
mBackgroundExecutor,
mSystemClock,
mIActivityManager,
mPackageManager,
mUserTracker,
mDeviceConfigProxyFake,
mDialogLaunchAnimator,
mBroadcastDispatcher,
mDumpManager
);
result.init();
verify(mIActivityManager).registerForegroundServiceObserver(
iForegroundServiceObserverArgumentCaptor.capture()
);
verify(mUserTracker).addCallback(
userTrackerCallbackArgumentCaptor.capture(),
ArgumentMatchers.eq(mBackgroundExecutor)
);
verify(mBroadcastDispatcher).registerReceiver(
showFgsManagerReceiverArgumentCaptor.capture(),
argThat(fltr -> fltr.matchAction(Intent.ACTION_SHOW_FOREGROUND_SERVICE_MANAGER)),
eq(mMainExecutor),
isNull(),
eq(Context.RECEIVER_NOT_EXPORTED),
isNull()
);
mIForegroundServiceObserver = iForegroundServiceObserverArgumentCaptor.getValue();
mUserTrackerCallback = userTrackerCallbackArgumentCaptor.getValue();
mShowFgsManagerReceiver = showFgsManagerReceiverArgumentCaptor.getValue();
return result;
}
private void setUserProfiles(int current, int... profileUserIds) {
mUserProfiles.clear();
mUserProfiles.add(new UserInfo(current, "current:" + current, 0));
for (int id : profileUserIds) {
mUserProfiles.add(new UserInfo(id, "profile:" + id, 0));
}
if (mUserTrackerCallback != null) {
mUserTrackerCallback.onUserChanged(current, mock(Context.class));
mUserTrackerCallback.onProfilesChanged(mUserProfiles);
}
}
}