Allow tile services to start foreground service when tile is clicked
Add the tile service app to temp allow list for 15 seconds to allow
it to start a foreground service during that short period.
Flag: NA
Bug: 329242921
Test: Manual test. CtsTileServiceTestCases, TileLifecycleManagerTest, CtsSystemUiHostTestCases
Change-Id: I26d4fb94162c88e3320d8b308c3cdec48b7e73b2
diff --git a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
index 24d815f..e734174 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerExemptionManager.java
@@ -427,6 +427,12 @@
*/
public static final int REASON_PACKAGE_UNARCHIVE = 328;
+ /**
+ * Tile onClick event
+ * @hide
+ */
+ public static final int REASON_TILE_ONCLICK = 329;
+
/** @hide The app requests out-out. */
public static final int REASON_OPT_OUT_REQUESTED = 1000;
@@ -504,13 +510,15 @@
REASON_ROLE_EMERGENCY,
REASON_SYSTEM_MODULE,
REASON_CARRIER_PRIVILEGED_APP,
- REASON_OPT_OUT_REQUESTED,
REASON_DPO_PROTECTED_APP,
REASON_DISALLOW_APPS_CONTROL,
REASON_ACTIVE_DEVICE_ADMIN,
REASON_MEDIA_NOTIFICATION_TRANSFER,
REASON_PACKAGE_INSTALLER,
+ REASON_SYSTEM_EXEMPT_APP_OP,
REASON_PACKAGE_UNARCHIVE,
+ REASON_TILE_ONCLICK,
+ REASON_OPT_OUT_REQUESTED,
})
@Retention(RetentionPolicy.SOURCE)
public @interface ReasonCode {}
diff --git a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
index 20da171..18ffb7a 100644
--- a/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
+++ b/apex/jobscheduler/framework/java/android/os/PowerWhitelistManager.java
@@ -310,6 +310,11 @@
* @hide
*/
public static final int REASON_SHELL = PowerExemptionManager.REASON_SHELL;
+ /**
+ * Tile onClick event
+ * @hide
+ */
+ public static final int REASON_TILE_ONCLICK = PowerExemptionManager.REASON_TILE_ONCLICK;
/**
* The list of BG-FGS-Launch and temp-allowlist reason code.
diff --git a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
index 98ca09d..ef3f10f 100644
--- a/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
+++ b/packages/SystemUI/src/com/android/systemui/dagger/FrameworkServicesModule.java
@@ -76,6 +76,7 @@
import android.net.NetworkScoreManager;
import android.net.wifi.WifiManager;
import android.os.BatteryStats;
+import android.os.IDeviceIdleController;
import android.os.PowerExemptionManager;
import android.os.PowerManager;
import android.os.ServiceManager;
@@ -735,4 +736,11 @@
static Optional<SatelliteManager> provideSatelliteManager(Context context) {
return Optional.ofNullable(context.getSystemService(SatelliteManager.class));
}
+
+ @Provides
+ @Singleton
+ static IDeviceIdleController provideDeviceIdleController() {
+ return IDeviceIdleController.Stub.asInterface(
+ ServiceManager.getService(Context.DEVICE_IDLE_CONTROLLER));
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
index 880289e..2a726c2 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -15,6 +15,8 @@
*/
package com.android.systemui.qs.external;
+import static android.os.PowerWhitelistManager.REASON_TILE_ONCLICK;
+import static android.provider.DeviceConfig.NAMESPACE_SYSTEMUI;
import static android.service.quicksettings.TileService.START_ACTIVITY_NEEDS_PENDING_INTENT;
import android.app.ActivityManager;
@@ -31,8 +33,10 @@
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
+import android.os.IDeviceIdleController;
import android.os.RemoteException;
import android.os.UserHandle;
+import android.provider.DeviceConfig;
import android.service.quicksettings.IQSService;
import android.service.quicksettings.IQSTileService;
import android.service.quicksettings.TileService;
@@ -89,7 +93,9 @@
private static final int MAX_BIND_RETRIES = 5;
private static final long DEFAULT_BIND_RETRY_DELAY = 5 * DateUtils.SECOND_IN_MILLIS;
private static final long LOW_MEMORY_BIND_RETRY_DELAY = 20 * DateUtils.SECOND_IN_MILLIS;
-
+ private static final long TILE_SERVICE_ONCLICK_ALLOW_LIST_DEFAULT_DURATION_MS = 15_000;
+ private static final String PROPERTY_TILE_SERVICE_ONCLICK_ALLOW_LIST_DURATION =
+ "property_tile_service_onclick_allow_list_duration";
// Shared prefs that hold tile lifecycle info.
private static final String TILES = "tiles_prefs";
@@ -102,6 +108,7 @@
private final PackageManagerAdapter mPackageManagerAdapter;
private final BroadcastDispatcher mBroadcastDispatcher;
private final ActivityManager mActivityManager;
+ private final IDeviceIdleController mDeviceIdleController;
private Set<Integer> mQueuedMessages = new ArraySet<>();
@NonNull
@@ -120,12 +127,15 @@
private TileChangeListener mChangeListener;
// Return value from bindServiceAsUser, determines whether safe to call unbind.
private AtomicBoolean mIsBound = new AtomicBoolean(false);
+ private long mTempAllowFgsLaunchDuration = TILE_SERVICE_ONCLICK_ALLOW_LIST_DEFAULT_DURATION_MS;
+ private final DeviceConfig.OnPropertiesChangedListener mDeviceConfigChangedListener;
+ private AtomicBoolean mDeviceConfigChangedListenerRegistered = new AtomicBoolean(false);
@AssistedInject
TileLifecycleManager(@Main Handler handler, Context context, IQSService service,
PackageManagerAdapter packageManagerAdapter, BroadcastDispatcher broadcastDispatcher,
@Assisted Intent intent, @Assisted UserHandle user, ActivityManager activityManager,
- @Background DelayableExecutor executor) {
+ IDeviceIdleController deviceIdleController, @Background DelayableExecutor executor) {
mContext = context;
mHandler = handler;
mIntent = intent;
@@ -136,6 +146,16 @@
mPackageManagerAdapter = packageManagerAdapter;
mBroadcastDispatcher = broadcastDispatcher;
mActivityManager = activityManager;
+ mDeviceIdleController = deviceIdleController;
+ mDeviceConfigChangedListener = properties -> {
+ if (!DeviceConfig.NAMESPACE_SYSTEMUI.equals(properties.getNamespace())) {
+ return;
+ }
+ mTempAllowFgsLaunchDuration = properties.getLong(
+ PROPERTY_TILE_SERVICE_ONCLICK_ALLOW_LIST_DURATION,
+ TILE_SERVICE_ONCLICK_ALLOW_LIST_DEFAULT_DURATION_MS);
+ };
+
if (mDebug) Log.d(TAG, "Creating " + mIntent + " " + mUser);
}
@@ -211,6 +231,13 @@
}
mBound.set(bind);
if (bind) {
+ if (mDeviceConfigChangedListenerRegistered.compareAndSet(false, true)) {
+ DeviceConfig.addOnPropertiesChangedListener(NAMESPACE_SYSTEMUI, mExecutor,
+ mDeviceConfigChangedListener);
+ mTempAllowFgsLaunchDuration = DeviceConfig.getLong(NAMESPACE_SYSTEMUI,
+ PROPERTY_TILE_SERVICE_ONCLICK_ALLOW_LIST_DURATION,
+ TILE_SERVICE_ONCLICK_ALLOW_LIST_DEFAULT_DURATION_MS);
+ }
if (mBindTryCount == MAX_BIND_RETRIES) {
// Too many failures, give up on this tile until an update.
startPackageListening();
@@ -363,6 +390,9 @@
stopPackageListening();
}
mChangeListener = null;
+ if (mDeviceConfigChangedListener != null) {
+ DeviceConfig.removeOnPropertiesChangedListener(mDeviceConfigChangedListener);
+ }
}
/**
@@ -566,7 +596,17 @@
@Override
public void onClick(IBinder iBinder) {
if (mDebug) Log.d(TAG, "onClick " + iBinder + " " + getComponent() + " " + mUser);
- if (isNullOrFailedAction(mOptionalWrapper, (wrapper) -> wrapper.onClick(iBinder))) {
+ if (isNullOrFailedAction(mOptionalWrapper, (wrapper) -> {
+ final String packageName = mIntent.getComponent().getPackageName();
+ try {
+ mDeviceIdleController.addPowerSaveTempWhitelistApp(packageName,
+ mTempAllowFgsLaunchDuration, mUser.getIdentifier(), REASON_TILE_ONCLICK,
+ "tile onclick");
+ } catch (RemoteException e) {
+ Log.d(TAG, "Caught exception trying to add client package to temp allow list", e);
+ }
+ return wrapper.onClick(iBinder);
+ })) {
mClickBinder = iBinder;
queueMessage(MSG_ON_CLICK);
handleDeath();
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
index 0a36ae6..f57f040 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileLifecycleManagerTest.java
@@ -15,6 +15,7 @@
*/
package com.android.systemui.qs.external;
+import static android.os.PowerExemptionManager.REASON_TILE_ONCLICK;
import static android.service.quicksettings.TileService.START_ACTIVITY_NEEDS_PENDING_INTENT;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
@@ -52,6 +53,7 @@
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
+import android.os.IDeviceIdleController;
import android.os.UserHandle;
import android.service.quicksettings.IQSService;
import android.service.quicksettings.IQSTileService;
@@ -83,6 +85,7 @@
mock(BroadcastDispatcher.class);
private final IQSTileService.Stub mMockTileService = mock(IQSTileService.Stub.class);
private final ActivityManager mActivityManager = mock(ActivityManager.class);
+ private final IDeviceIdleController mDeviceIdleController = mock(IDeviceIdleController.class);
private ComponentName mTileServiceComponentName;
private Intent mTileServiceIntent;
@@ -126,6 +129,7 @@
mTileServiceIntent,
mUser,
mActivityManager,
+ mDeviceIdleController,
mExecutor);
}
@@ -386,6 +390,20 @@
}
@Test
+ public void testClickCallsDeviceIdleManager() throws Exception {
+ mStateManager.onTileAdded();
+ mStateManager.onStartListening();
+ mStateManager.onClick(null);
+ mStateManager.executeSetBindService(true);
+ mExecutor.runAllReady();
+
+ verify(mMockTileService).onClick(null);
+ verify(mDeviceIdleController).addPowerSaveTempWhitelistApp(
+ mTileServiceComponentName.getPackageName(), 15000,
+ mUser.getIdentifier(), REASON_TILE_ONCLICK, "tile onclick");
+ }
+
+ @Test
public void testFalseBindCallsUnbind() {
Context falseContext = mock(Context.class);
when(falseContext.bindServiceAsUser(any(), any(), anyInt(), any())).thenReturn(false);
@@ -396,6 +414,7 @@
mTileServiceIntent,
mUser,
mActivityManager,
+ mDeviceIdleController,
mExecutor);
manager.executeSetBindService(true);
@@ -418,6 +437,7 @@
mTileServiceIntent,
mUser,
mActivityManager,
+ mDeviceIdleController,
mExecutor);
manager.executeSetBindService(true);
@@ -440,6 +460,7 @@
mTileServiceIntent,
mUser,
mActivityManager,
+ mDeviceIdleController,
mExecutor);
manager.executeSetBindService(true);
@@ -464,6 +485,7 @@
mTileServiceIntent,
mUser,
mActivityManager,
+ mDeviceIdleController,
mExecutor);
manager.executeSetBindService(true);