From bcdf21f5c060b29be58010d242eb08bb672a57fc Mon Sep 17 00:00:00 2001 From: Jiyong Park Date: Thu, 7 Nov 2024 00:17:57 +0900 Subject: Apps can opt in to be stoppable in the task manager Before this change, a system apps having foreground service wasn't stoppable in the task manager. The rationale for it was that users being able to stop system apps can be risky. However, that at the same time meant that users have no way to stop such apps even if the apps can be stopped without any risk. This change adds an array stoppable_fgs_system_apps, which device makers can put the package name of the system apps can be safely stopped. Bug: 376564917 Test: watch SystemUITests in TH Test: check if there is a stop button in the UI with flag and config Flag: com.android.systemui.stoppable_fgs_system_app Change-Id: I09a946fdcba695f2ae0e0da333cce1e43c7c7421 --- core/res/res/values/stoppable_fgs_system_apps.xml | 26 ++++++++++++++++ core/res/res/values/symbols.xml | 2 ++ packages/SystemUI/aconfig/systemui.aconfig | 10 ++++++ .../systemui/qs/FgsManagerControllerTest.java | 36 ++++++++++++++++++++-- .../android/systemui/qs/FgsManagerController.kt | 25 ++++++++++++++- 5 files changed, 95 insertions(+), 4 deletions(-) create mode 100644 core/res/res/values/stoppable_fgs_system_apps.xml diff --git a/core/res/res/values/stoppable_fgs_system_apps.xml b/core/res/res/values/stoppable_fgs_system_apps.xml new file mode 100644 index 000000000000..165ff61c7b3e --- /dev/null +++ b/core/res/res/values/stoppable_fgs_system_apps.xml @@ -0,0 +1,26 @@ + + + + + + + + + + diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml index aa08d5e2313e..db81a3be440f 100644 --- a/core/res/res/values/symbols.xml +++ b/core/res/res/values/symbols.xml @@ -1306,6 +1306,8 @@ + + diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig index 3bf3e24a2ba6..87ea2a7ab2ae 100644 --- a/packages/SystemUI/aconfig/systemui.aconfig +++ b/packages/SystemUI/aconfig/systemui.aconfig @@ -1743,3 +1743,13 @@ flag { description: "An implementation of shortcut customizations through shortcut helper." bug: "365064144" } + +flag { + name: "stoppable_fgs_system_app" + namespace: "systemui" + description: "System app with foreground service can opt in to be stoppable." + bug: "376564917" + metadata { + purpose: PURPOSE_BUGFIX + } +} diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java index 16ae4662c2d4..0356422bda04 100644 --- a/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java +++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/qs/FgsManagerControllerTest.java @@ -42,6 +42,7 @@ import android.content.pm.UserInfo; import android.os.Binder; import android.os.RemoteException; import android.os.UserHandle; +import android.platform.test.annotations.EnableFlags; import android.provider.DeviceConfig; import android.testing.TestableLooper; @@ -49,6 +50,7 @@ import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.filters.SmallTest; import com.android.internal.config.sysui.SystemUiDeviceConfigFlags; +import com.android.systemui.Flags; import com.android.systemui.SysuiTestCase; import com.android.systemui.animation.DialogTransitionAnimator; import com.android.systemui.broadcast.BroadcastDispatcher; @@ -314,6 +316,29 @@ public class FgsManagerControllerTest extends SysuiTestCase { Assert.assertEquals(0, mFmc.visibleButtonsCount()); } + @Test + @EnableFlags(Flags.FLAG_STOPPABLE_FGS_SYSTEM_APP) + public void testButtonVisibilityOfStoppableApps() throws Exception { + setUserProfiles(0); + setBackgroundRestrictionExemptionReason("pkg", 12345, REASON_ALLOWLISTED_PACKAGE); + setBackgroundRestrictionExemptionReason("vendor_pkg", 67890, REASON_ALLOWLISTED_PACKAGE); + + // Same as above, but apps are opt-in to be stoppable + setStoppableApps(new String[] {"pkg"}, /* vendor */ false); + setStoppableApps(new String[] {"vendor_pkg"}, /* vendor */ true); + + final Binder binder = new Binder(); + setShowStopButtonForUserAllowlistedApps(true); + // Both are foreground. + mIForegroundServiceObserver.onForegroundStateChanged(binder, "pkg", 0, true); + mIForegroundServiceObserver.onForegroundStateChanged(binder, "vendor_pkg", 0, true); + Assert.assertEquals(2, mFmc.visibleButtonsCount()); + + // The vendor package is no longer foreground. Only `pkg` remains. + mIForegroundServiceObserver.onForegroundStateChanged(binder, "vendor_pkg", 0, false); + Assert.assertEquals(1, mFmc.visibleButtonsCount()); + } + @Test public void testShowUserVisibleJobsOnCreation() { // Test when the default is on. @@ -321,7 +346,7 @@ public class FgsManagerControllerTest extends SysuiTestCase { SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_USER_VISIBLE_JOBS, "true", false); FgsManagerController fmc = new FgsManagerControllerImpl( - mContext, + mContext.getResources(), mMainExecutor, mBackgroundExecutor, mSystemClock, @@ -348,7 +373,7 @@ public class FgsManagerControllerTest extends SysuiTestCase { SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_USER_VISIBLE_JOBS, "false", false); fmc = new FgsManagerControllerImpl( - mContext, + mContext.getResources(), mMainExecutor, mBackgroundExecutor, mSystemClock, @@ -446,6 +471,11 @@ public class FgsManagerControllerTest extends SysuiTestCase { .getBackgroundRestrictionExemptionReason(uid); } + private void setStoppableApps(String[] packageNames, boolean vendor) throws Exception { + overrideResource(vendor ? com.android.internal.R.array.vendor_stoppable_fgs_system_apps + : com.android.internal.R.array.stoppable_fgs_system_apps, packageNames); + } + FgsManagerController createFgsManagerController() throws RemoteException { ArgumentCaptor iForegroundServiceObserverArgumentCaptor = ArgumentCaptor.forClass(IForegroundServiceObserver.class); @@ -455,7 +485,7 @@ public class FgsManagerControllerTest extends SysuiTestCase { ArgumentCaptor.forClass(BroadcastReceiver.class); FgsManagerController result = new FgsManagerControllerImpl( - mContext, + mContext.getResources(), mMainExecutor, mBackgroundExecutor, mSystemClock, diff --git a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt index a1071907cd3d..2a5ffc6cc391 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt +++ b/packages/SystemUI/src/com/android/systemui/qs/FgsManagerController.kt @@ -27,6 +27,7 @@ import android.content.Intent import android.content.IntentFilter import android.content.pm.PackageManager import android.content.pm.UserInfo +import android.content.res.Resources import android.graphics.drawable.Drawable import android.os.IBinder import android.os.PowerExemptionManager @@ -54,6 +55,7 @@ import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_ import com.android.internal.config.sysui.SystemUiDeviceConfigFlags.TASK_MANAGER_SHOW_USER_VISIBLE_JOBS import com.android.internal.jank.InteractionJankMonitor import com.android.systemui.Dumpable +import com.android.systemui.Flags; import com.android.systemui.res.R import com.android.systemui.animation.DialogCuj import com.android.systemui.animation.DialogTransitionAnimator @@ -137,7 +139,7 @@ interface FgsManagerController { @SysUISingleton class FgsManagerControllerImpl @Inject constructor( - private val context: Context, + @Main private val resources: Resources, @Main private val mainExecutor: Executor, @Background private val backgroundExecutor: Executor, private val systemClock: SystemClock, @@ -223,6 +225,14 @@ class FgsManagerControllerImpl @Inject constructor( private val userVisibleJobObserver = UserVisibleJobObserver() + private val stoppableApps by lazy { resources + .getStringArray(com.android.internal.R.array.stoppable_fgs_system_apps) + } + + private val vendorStoppableApps by lazy { resources + .getStringArray(com.android.internal.R.array.vendor_stoppable_fgs_system_apps) + } + override fun init() { synchronized(lock) { if (initialized) { @@ -725,9 +735,22 @@ class FgsManagerControllerImpl @Inject constructor( } else -> UIControl.NORMAL } + // If the app wants to be a good citizen by being stoppable, even if the category it + // belongs to is exempted for background restriction, let it be stoppable by user. + if (Flags.stoppableFgsSystemApp()) { + if (isStoppableApp(packageName)) { + uiControl = UIControl.NORMAL + } + } + uiControlInitialized = true } + fun isStoppableApp(packageName: String): Boolean { + return stoppableApps.contains(packageName) || + vendorStoppableApps.contains(packageName) + } + override fun equals(other: Any?): Boolean { if (other !is UserPackage) { return false -- cgit v1.2.3-59-g8ed1b