Shorter rebind time for active tile service
Rebind after 1 second (down from 5 seconds) when the tile is active,
except when there's already been a rebind attempt less than 5 seconds
ago. In that case, the rebind delay is still 5 seconds.
Flag: com.android.systemui.qs_quick_rebind_active_tiles
Bug: 315249245
Bug: 362526228
Test: atest TileLifeCycleManagerTest
Change-Id: I674f1ec9f7e53aa6198efebfc5e35f4cb0069bfb
diff --git a/packages/SystemUI/aconfig/systemui.aconfig b/packages/SystemUI/aconfig/systemui.aconfig
index 8a1d81b..538a5a7 100644
--- a/packages/SystemUI/aconfig/systemui.aconfig
+++ b/packages/SystemUI/aconfig/systemui.aconfig
@@ -295,6 +295,16 @@
}
flag {
+ name: "qs_quick_rebind_active_tiles"
+ namespace: "systemui"
+ description: "Rebind active custom tiles quickly."
+ bug: "362526228"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
+
+flag {
name: "coroutine_tracing"
namespace: "systemui"
description: "Adds thread-local data to System UI's global coroutine scopes to "
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 cbcf68c..2f843ac 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -50,10 +50,12 @@
import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread;
+import com.android.systemui.Flags;
import com.android.systemui.broadcast.BroadcastDispatcher;
import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.time.SystemClock;
import dagger.assisted.Assisted;
import dagger.assisted.AssistedFactory;
@@ -95,6 +97,7 @@
// Bind retry control.
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 ACTIVE_TILE_BIND_RETRY_DELAY = 1 * 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 =
@@ -107,6 +110,7 @@
private final Intent mIntent;
private final UserHandle mUser;
private final DelayableExecutor mExecutor;
+ private final SystemClock mSystemClock;
private final IBinder mToken = new Binder();
private final PackageManagerAdapter mPackageManagerAdapter;
private final BroadcastDispatcher mBroadcastDispatcher;
@@ -120,7 +124,6 @@
private IBinder mClickBinder;
private int mBindTryCount;
- private long mBindRetryDelay = DEFAULT_BIND_RETRY_DELAY;
private AtomicBoolean isDeathRebindScheduled = new AtomicBoolean(false);
private AtomicBoolean mBound = new AtomicBoolean(false);
private AtomicBoolean mPackageReceiverRegistered = new AtomicBoolean(false);
@@ -138,7 +141,8 @@
TileLifecycleManager(@Main Handler handler, Context context, IQSService service,
PackageManagerAdapter packageManagerAdapter, BroadcastDispatcher broadcastDispatcher,
@Assisted Intent intent, @Assisted UserHandle user, ActivityManager activityManager,
- IDeviceIdleController deviceIdleController, @Background DelayableExecutor executor) {
+ IDeviceIdleController deviceIdleController, @Background DelayableExecutor executor,
+ SystemClock systemClock) {
mContext = context;
mHandler = handler;
mIntent = intent;
@@ -146,6 +150,7 @@
mIntent.putExtra(TileService.EXTRA_TOKEN, mToken);
mUser = user;
mExecutor = executor;
+ mSystemClock = systemClock;
mPackageManagerAdapter = packageManagerAdapter;
mBroadcastDispatcher = broadcastDispatcher;
mActivityManager = activityManager;
@@ -436,25 +441,31 @@
// If mBound is true (meaning that we should be bound), then reschedule binding for
// later.
if (mBound.get() && checkComponentState()) {
- if (isDeathRebindScheduled.compareAndSet(false, true)) {
+ if (isDeathRebindScheduled.compareAndSet(false, true)) { // if already not scheduled
+
+
mExecutor.executeDelayed(() -> {
// Only rebind if we are supposed to, but remove the scheduling anyway.
if (mBound.get()) {
setBindService(true);
}
- isDeathRebindScheduled.set(false);
+ isDeathRebindScheduled.set(false); // allow scheduling again
}, getRebindDelay());
}
}
});
}
+ private long mLastRebind = 0;
/**
* @return the delay to automatically rebind after a service died. It provides a longer delay if
* the device is a low memory state because the service is likely to get killed again by the
* system. In this case we want to rebind later and not to cause a loop of a frequent rebinds.
+ * It also provides a longer delay if called quickly (a few seconds) after a first call.
*/
private long getRebindDelay() {
+ final long now = mSystemClock.currentTimeMillis();
+
final ActivityManager.MemoryInfo info = new ActivityManager.MemoryInfo();
mActivityManager.getMemoryInfo(info);
@@ -462,7 +473,20 @@
if (info.lowMemory) {
delay = LOW_MEMORY_BIND_RETRY_DELAY;
} else {
- delay = mBindRetryDelay;
+ if (Flags.qsQuickRebindActiveTiles()) {
+ final long elapsedTimeSinceLastRebind = now - mLastRebind;
+ final boolean justAttemptedRebind =
+ elapsedTimeSinceLastRebind < DEFAULT_BIND_RETRY_DELAY;
+ if (isActiveTile() && !justAttemptedRebind) {
+ delay = ACTIVE_TILE_BIND_RETRY_DELAY;
+ } else {
+ delay = DEFAULT_BIND_RETRY_DELAY;
+ }
+ } else {
+ delay = DEFAULT_BIND_RETRY_DELAY;
+ }
+
+ mLastRebind = now;
}
if (mDebug) Log.i(TAG, "Rebinding with a delay=" + delay + " - " + getComponent());
return delay;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
index d10471d..c5fa8cf 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
@@ -44,7 +44,7 @@
/**
* Manages the priority which lets {@link TileServices} make decisions about which tiles
* to bind. Also holds on to and manages the {@link TileLifecycleManager}, informing it
- * of when it is allowed to bind based on decisions frome the {@link TileServices}.
+ * of when it is allowed to bind based on decisions from the {@link TileServices}.
*/
public class TileServiceManager {
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 c1cf91d..bc0ec2d 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
@@ -22,6 +22,7 @@
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession;
import static com.android.systemui.Flags.FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX;
+import static com.android.systemui.Flags.FLAG_QS_QUICK_REBIND_ACTIVE_TILES;
import static com.google.common.truth.Truth.assertThat;
@@ -75,6 +76,8 @@
import com.android.systemui.util.concurrency.FakeExecutor;
import com.android.systemui.util.time.FakeSystemClock;
+import com.google.common.truth.Truth;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
@@ -95,7 +98,8 @@
@Parameters(name = "{0}")
public static List<FlagsParameterization> getParams() {
- return allCombinationsOf(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX);
+ return allCombinationsOf(FLAG_QS_CUSTOM_TILE_CLICK_GUARANTEED_BUG_FIX,
+ FLAG_QS_QUICK_REBIND_ACTIVE_TILES);
}
private final PackageManagerAdapter mMockPackageManagerAdapter =
@@ -154,7 +158,8 @@
mUser,
mActivityManager,
mDeviceIdleController,
- mExecutor);
+ mExecutor,
+ mClock);
}
@After
@@ -169,12 +174,12 @@
mStateManager.handleDestroy();
}
- private void setPackageEnabled(boolean enabled) throws Exception {
+ private void setPackageEnabledAndActive(boolean enabled, boolean active) throws Exception {
ServiceInfo defaultServiceInfo = null;
if (enabled) {
defaultServiceInfo = new ServiceInfo();
defaultServiceInfo.metaData = new Bundle();
- defaultServiceInfo.metaData.putBoolean(TileService.META_DATA_ACTIVE_TILE, true);
+ defaultServiceInfo.metaData.putBoolean(TileService.META_DATA_ACTIVE_TILE, active);
defaultServiceInfo.metaData.putBoolean(TileService.META_DATA_TOGGLEABLE_TILE, true);
}
when(mMockPackageManagerAdapter.getServiceInfo(any(), anyInt(), anyInt()))
@@ -186,6 +191,10 @@
.thenReturn(defaultPackageInfo);
}
+ private void setPackageEnabled(boolean enabled) throws Exception {
+ setPackageEnabledAndActive(enabled, true);
+ }
+
private void setPackageInstalledForUser(
boolean installed,
boolean active,
@@ -396,13 +405,40 @@
}
@Test
- public void testKillProcess() throws Exception {
+ public void testKillProcessWhenTileServiceIsNotActive() throws Exception {
+ setPackageEnabledAndActive(true, false);
mStateManager.onStartListening();
mStateManager.executeSetBindService(true);
mExecutor.runAllReady();
+ verifyBind(1);
+ verify(mMockTileService, times(1)).onStartListening();
+
mStateManager.onBindingDied(mTileServiceComponentName);
mExecutor.runAllReady();
- mClock.advanceTime(5000);
+ mClock.advanceTime(1000);
+ mExecutor.runAllReady();
+
+ // still 4 seconds left because non active tile service rebind time is 5 seconds
+ Truth.assertThat(mContext.isBound(mTileServiceComponentName)).isFalse();
+
+ mClock.advanceTime(4000); // 5 seconds delay for nonActive service rebinding
+ mExecutor.runAllReady();
+ verifyBind(2);
+ verify(mMockTileService, times(2)).onStartListening();
+ }
+
+ @EnableFlags(FLAG_QS_QUICK_REBIND_ACTIVE_TILES)
+ @Test
+ public void testKillProcessWhenTileServiceIsActive_withRebindFlagOn() throws Exception {
+ mStateManager.onStartListening();
+ mStateManager.executeSetBindService(true);
+ mExecutor.runAllReady();
+ verifyBind(1);
+ verify(mMockTileService, times(1)).onStartListening();
+
+ mStateManager.onBindingDied(mTileServiceComponentName);
+ mExecutor.runAllReady();
+ mClock.advanceTime(1000);
mExecutor.runAllReady();
// Two calls: one for the first bind, one for the restart.
@@ -410,6 +446,86 @@
verify(mMockTileService, times(2)).onStartListening();
}
+ @DisableFlags(FLAG_QS_QUICK_REBIND_ACTIVE_TILES)
+ @Test
+ public void testKillProcessWhenTileServiceIsActive_withRebindFlagOff() throws Exception {
+ mStateManager.onStartListening();
+ mStateManager.executeSetBindService(true);
+ mExecutor.runAllReady();
+ verifyBind(1);
+ verify(mMockTileService, times(1)).onStartListening();
+
+ mStateManager.onBindingDied(mTileServiceComponentName);
+ mExecutor.runAllReady();
+ mClock.advanceTime(1000);
+ mExecutor.runAllReady();
+ verifyBind(0); // the rebind happens after 4 more seconds
+
+ mClock.advanceTime(4000);
+ mExecutor.runAllReady();
+ verifyBind(1);
+ }
+
+ @EnableFlags(FLAG_QS_QUICK_REBIND_ACTIVE_TILES)
+ @Test
+ public void testKillProcessWhenTileServiceIsActiveTwice_withRebindFlagOn_delaysSecondRebind()
+ throws Exception {
+ mStateManager.onStartListening();
+ mStateManager.executeSetBindService(true);
+ mExecutor.runAllReady();
+ verifyBind(1);
+ verify(mMockTileService, times(1)).onStartListening();
+
+ mStateManager.onBindingDied(mTileServiceComponentName);
+ mExecutor.runAllReady();
+ mClock.advanceTime(1000);
+ mExecutor.runAllReady();
+
+ // Two calls: one for the first bind, one for the restart.
+ verifyBind(2);
+ verify(mMockTileService, times(2)).onStartListening();
+
+ mStateManager.onBindingDied(mTileServiceComponentName);
+ mExecutor.runAllReady();
+ mClock.advanceTime(1000);
+ mExecutor.runAllReady();
+ // because active tile will take 5 seconds to bind the second time, not 1
+ verifyBind(0);
+
+ mClock.advanceTime(4000);
+ mExecutor.runAllReady();
+ verifyBind(1);
+ }
+
+ @DisableFlags(FLAG_QS_QUICK_REBIND_ACTIVE_TILES)
+ @Test
+ public void testKillProcessWhenTileServiceIsActiveTwice_withRebindFlagOff_rebindsFromFirstKill()
+ throws Exception {
+ mStateManager.onStartListening();
+ mStateManager.executeSetBindService(true);
+ mExecutor.runAllReady();
+ verifyBind(1);
+ verify(mMockTileService, times(1)).onStartListening();
+
+ mStateManager.onBindingDied(mTileServiceComponentName); // rebind scheduled for 5 seconds
+ mExecutor.runAllReady();
+ mClock.advanceTime(1000);
+ mExecutor.runAllReady();
+
+ verifyBind(0); // it would bind in 4 more seconds
+
+ mStateManager.onBindingDied(mTileServiceComponentName); // this does not affect the rebind
+ mExecutor.runAllReady();
+ mClock.advanceTime(1000);
+ mExecutor.runAllReady();
+
+ verifyBind(0); // only 2 seconds passed from first kill
+
+ mClock.advanceTime(3000);
+ mExecutor.runAllReady();
+ verifyBind(1); // the rebind scheduled 5 seconds from the first kill should now happen
+ }
+
@Test
public void testKillProcessLowMemory() throws Exception {
doAnswer(invocation -> {
@@ -510,7 +626,8 @@
mUser,
mActivityManager,
mDeviceIdleController,
- mExecutor);
+ mExecutor,
+ mClock);
manager.executeSetBindService(true);
mExecutor.runAllReady();
@@ -533,7 +650,8 @@
mUser,
mActivityManager,
mDeviceIdleController,
- mExecutor);
+ mExecutor,
+ mClock);
manager.executeSetBindService(true);
mExecutor.runAllReady();
@@ -556,7 +674,8 @@
mUser,
mActivityManager,
mDeviceIdleController,
- mExecutor);
+ mExecutor,
+ mClock);
manager.executeSetBindService(true);
mExecutor.runAllReady();
@@ -581,7 +700,8 @@
mUser,
mActivityManager,
mDeviceIdleController,
- mExecutor);
+ mExecutor,
+ mClock);
manager.executeSetBindService(true);
mExecutor.runAllReady();
@@ -607,7 +727,8 @@
mUser,
mActivityManager,
mDeviceIdleController,
- mExecutor);
+ mExecutor,
+ mClock);
assertThat(manager.isActiveTile()).isTrue();
}
@@ -626,7 +747,8 @@
mUser,
mActivityManager,
mDeviceIdleController,
- mExecutor);
+ mExecutor,
+ mClock);
assertThat(manager.isActiveTile()).isTrue();
}
@@ -644,7 +766,8 @@
mUser,
mActivityManager,
mDeviceIdleController,
- mExecutor);
+ mExecutor,
+ mClock);
assertThat(manager.isToggleableTile()).isTrue();
}
@@ -663,7 +786,8 @@
mUser,
mActivityManager,
mDeviceIdleController,
- mExecutor);
+ mExecutor,
+ mClock);
assertThat(manager.isToggleableTile()).isTrue();
}
@@ -682,7 +806,8 @@
mUser,
mActivityManager,
mDeviceIdleController,
- mExecutor);
+ mExecutor,
+ mClock);
assertThat(manager.isToggleableTile()).isFalse();
assertThat(manager.isActiveTile()).isFalse();
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TileLifecycleManagerKosmos.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TileLifecycleManagerKosmos.kt
index a0fc76b..4978558 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TileLifecycleManagerKosmos.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/qs/external/TileLifecycleManagerKosmos.kt
@@ -24,6 +24,7 @@
import com.android.systemui.kosmos.Kosmos
import com.android.systemui.qs.tiles.impl.custom.packageManagerAdapterFacade
import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.time.fakeSystemClock
val Kosmos.tileLifecycleManagerFactory: TileLifecycleManager.Factory by
Kosmos.Fixture {
@@ -39,6 +40,7 @@
activityManager,
mock(),
fakeExecutor,
+ fakeSystemClock,
)
}
}