Merge "Add Low memory state TileService rebinding delay" into main
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 2469a98..78f2da5 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileLifecycleManager.java
@@ -17,6 +17,7 @@
import static android.service.quicksettings.TileService.START_ACTIVITY_NEEDS_PENDING_INTENT;
+import android.app.ActivityManager;
import android.app.compat.CompatChanges;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -35,6 +36,7 @@
import android.service.quicksettings.IQSService;
import android.service.quicksettings.IQSTileService;
import android.service.quicksettings.TileService;
+import android.text.format.DateUtils;
import android.util.ArraySet;
import android.util.Log;
@@ -81,7 +83,8 @@
// Bind retry control.
private static final int MAX_BIND_RETRIES = 5;
- private static final int DEFAULT_BIND_RETRY_DELAY = 1000;
+ 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;
// Shared prefs that hold tile lifecycle info.
private static final String TILES = "tiles_prefs";
@@ -94,6 +97,7 @@
private final IBinder mToken = new Binder();
private final PackageManagerAdapter mPackageManagerAdapter;
private final BroadcastDispatcher mBroadcastDispatcher;
+ private final ActivityManager mActivityManager;
private Set<Integer> mQueuedMessages = new ArraySet<>();
@Nullable
@@ -102,7 +106,8 @@
private IBinder mClickBinder;
private int mBindTryCount;
- private int mBindRetryDelay = DEFAULT_BIND_RETRY_DELAY;
+ 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);
private AtomicBoolean mUserReceiverRegistered = new AtomicBoolean(false);
@@ -115,7 +120,7 @@
@AssistedInject
TileLifecycleManager(@Main Handler handler, Context context, IQSService service,
PackageManagerAdapter packageManagerAdapter, BroadcastDispatcher broadcastDispatcher,
- @Assisted Intent intent, @Assisted UserHandle user,
+ @Assisted Intent intent, @Assisted UserHandle user, ActivityManager activityManager,
@Background DelayableExecutor executor) {
mContext = context;
mHandler = handler;
@@ -126,6 +131,7 @@
mExecutor = executor;
mPackageManagerAdapter = packageManagerAdapter;
mBroadcastDispatcher = broadcastDispatcher;
+ mActivityManager = activityManager;
if (DEBUG) Log.d(TAG, "Creating " + mIntent + " " + mUser);
}
@@ -152,10 +158,6 @@
}
}
- public void setBindRetryDelay(int delayMs) {
- mBindRetryDelay = delayMs;
- }
-
public boolean isActiveTile() {
try {
ServiceInfo info = mPackageManagerAdapter.getServiceInfo(mIntent.getComponent(),
@@ -250,19 +252,15 @@
private boolean bindServices() {
String packageName = mIntent.getComponent().getPackageName();
+ int flags = Context.BIND_AUTO_CREATE
+ | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
+ | Context.BIND_WAIVE_PRIORITY;
if (CompatChanges.isChangeEnabled(START_ACTIVITY_NEEDS_PENDING_INTENT, packageName,
mUser)) {
- return mContext.bindServiceAsUser(mIntent, this,
- Context.BIND_AUTO_CREATE
- | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
- | Context.BIND_WAIVE_PRIORITY,
- mUser);
+ return mContext.bindServiceAsUser(mIntent, this, flags, mUser);
}
return mContext.bindServiceAsUser(mIntent, this,
- Context.BIND_AUTO_CREATE
- | Context.BIND_FOREGROUND_SERVICE_WHILE_AWAKE
- | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS
- | Context.BIND_WAIVE_PRIORITY,
+ flags | Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS,
mUser);
}
@@ -352,10 +350,34 @@
if (!mBound.get()) return;
if (DEBUG) Log.d(TAG, "handleDeath");
if (checkComponentState()) {
- mExecutor.executeDelayed(() -> setBindService(true), mBindRetryDelay);
+ if (isDeathRebindScheduled.compareAndSet(false, true)) {
+ mExecutor.executeDelayed(() -> {
+ setBindService(true);
+ isDeathRebindScheduled.set(false);
+ }, getRebindDelay());
+ }
}
}
+ /**
+ * @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.
+ */
+ private long getRebindDelay() {
+ final ActivityManager.MemoryInfo info = new ActivityManager.MemoryInfo();
+ mActivityManager.getMemoryInfo(info);
+
+ final long delay;
+ if (info.lowMemory) {
+ delay = LOW_MEMORY_BIND_RETRY_DELAY;
+ } else {
+ delay = mBindRetryDelay;
+ }
+ Log.i(TAG, "Rebinding with a delay=" + delay);
+ return delay;
+ }
+
private boolean checkComponentState() {
if (!isPackageAvailable() || !isComponentAvailable()) {
startPackageListening();
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 941a9d6..3ee4a1b 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServiceManager.java
@@ -75,13 +75,12 @@
private boolean mStarted = false;
TileServiceManager(TileServices tileServices, Handler handler, ComponentName component,
- BroadcastDispatcher broadcastDispatcher, UserTracker userTracker,
- CustomTileAddedRepository customTileAddedRepository, DelayableExecutor executor) {
+ UserTracker userTracker, TileLifecycleManager.Factory tileLifecycleManagerFactory,
+ CustomTileAddedRepository customTileAddedRepository) {
this(tileServices, handler, userTracker, customTileAddedRepository,
- new TileLifecycleManager(handler, tileServices.getContext(), tileServices,
- new PackageManagerAdapter(tileServices.getContext()), broadcastDispatcher,
+ tileLifecycleManagerFactory.create(
new Intent(TileService.ACTION_QS_TILE).setComponent(component),
- userTracker.getUserHandle(), executor));
+ userTracker.getUserHandle()));
}
@VisibleForTesting
diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
index acee8e9..c3744df 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java
@@ -81,6 +81,7 @@
private final UserTracker mUserTracker;
private final StatusBarIconController mStatusBarIconController;
private final PanelInteractor mPanelInteractor;
+ private final TileLifecycleManager.Factory mTileLifecycleManagerFactory;
private final CustomTileAddedRepository mCustomTileAddedRepository;
private final DelayableExecutor mBackgroundExecutor;
@@ -96,6 +97,7 @@
CommandQueue commandQueue,
StatusBarIconController statusBarIconController,
PanelInteractor panelInteractor,
+ TileLifecycleManager.Factory tileLifecycleManagerFactory,
CustomTileAddedRepository customTileAddedRepository,
@Background DelayableExecutor backgroundExecutor) {
mHost = host;
@@ -109,6 +111,7 @@
mStatusBarIconController = statusBarIconController;
mCommandQueue.addCallback(mRequestListeningCallback);
mPanelInteractor = panelInteractor;
+ mTileLifecycleManagerFactory = tileLifecycleManagerFactory;
mCustomTileAddedRepository = customTileAddedRepository;
mBackgroundExecutor = backgroundExecutor;
}
@@ -137,8 +140,8 @@
protected TileServiceManager onCreateTileService(ComponentName component,
BroadcastDispatcher broadcastDispatcher) {
- return new TileServiceManager(this, mHandlerProvider.get(), component,
- broadcastDispatcher, mUserTracker, mCustomTileAddedRepository, mBackgroundExecutor);
+ return new TileServiceManager(this, mHandlerProvider.get(), component, mUserTracker,
+ mTileLifecycleManagerFactory, mCustomTileAddedRepository);
}
public void freeService(CustomTileInterface tile, TileServiceManager service) {
@@ -323,7 +326,7 @@
if (info.applicationInfo.isSystemApp()) {
final StatusBarIcon statusIcon = icon != null
? new StatusBarIcon(userHandle, packageName, icon, 0, 0,
- contentDescription)
+ contentDescription)
: null;
final String slot = getStatusBarIconSlotName(componentName);
mMainHandler.post(new Runnable() {
@@ -356,11 +359,11 @@
synchronized (mServices) {
mTokenMap.forEach((iBinder, customTile) ->
sb.append(iBinder.toString())
- .append(":")
- .append(customTile.getComponent().flattenToShortString())
- .append(":")
- .append(customTile.getUser())
- .append(","));
+ .append(":")
+ .append(customTile.getComponent().flattenToShortString())
+ .append(":")
+ .append(customTile.getUser())
+ .append(","));
}
sb.append("]");
return sb.toString();
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 67587e3..6cc52d7 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
@@ -29,12 +29,14 @@
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.anyString;
+import static org.mockito.Mockito.doAnswer;
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 static org.mockito.Mockito.when;
+import android.app.ActivityManager;
import android.app.compat.CompatChanges;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
@@ -73,16 +75,18 @@
@SmallTest
@RunWith(AndroidJUnit4.class)
public class TileLifecycleManagerTest extends SysuiTestCase {
- private static final int TEST_FAIL_TIMEOUT = 5000;
private final PackageManagerAdapter mMockPackageManagerAdapter =
mock(PackageManagerAdapter.class);
private final BroadcastDispatcher mMockBroadcastDispatcher =
mock(BroadcastDispatcher.class);
private final IQSTileService.Stub mMockTileService = mock(IQSTileService.Stub.class);
+ private final ActivityManager mActivityManager = mock(ActivityManager.class);
+
private ComponentName mTileServiceComponentName;
private Intent mTileServiceIntent;
private UserHandle mUser;
+ private FakeSystemClock mClock;
private FakeExecutor mExecutor;
private HandlerThread mThread;
private Handler mHandler;
@@ -112,13 +116,15 @@
mThread = new HandlerThread("TestThread");
mThread.start();
mHandler = Handler.createAsync(mThread.getLooper());
- mExecutor = new FakeExecutor(new FakeSystemClock());
+ mClock = new FakeSystemClock();
+ mExecutor = new FakeExecutor(mClock);
mStateManager = new TileLifecycleManager(mHandler, mWrappedContext,
mock(IQSService.class),
mMockPackageManagerAdapter,
mMockBroadcastDispatcher,
mTileServiceIntent,
mUser,
+ mActivityManager,
mExecutor);
}
@@ -294,11 +300,32 @@
mStateManager.onStartListening();
mStateManager.executeSetBindService(true);
mExecutor.runAllReady();
- mStateManager.setBindRetryDelay(0);
+ mStateManager.onServiceDisconnected(mTileServiceComponentName);
+ mClock.advanceTime(5000);
+
+ // Two calls: one for the first bind, one for the restart.
+ verifyBind(2);
+ verify(mMockTileService, times(2)).onStartListening();
+ }
+
+ @Test
+ public void testKillProcessLowMemory() throws Exception {
+ doAnswer(invocation -> {
+ ActivityManager.MemoryInfo memoryInfo = invocation.getArgument(0);
+ memoryInfo.lowMemory = true;
+ return null;
+ }).when(mActivityManager).getMemoryInfo(any());
+ mStateManager.onStartListening();
+ mStateManager.executeSetBindService(true);
mExecutor.runAllReady();
mStateManager.onServiceDisconnected(mTileServiceComponentName);
- mExecutor.runAllReady();
+ // Longer delay than a regular one
+ mClock.advanceTime(5000);
+ verifyBind(1);
+ verify(mMockTileService, times(1)).onStartListening();
+
+ mClock.advanceTime(20000);
// Two calls: one for the first bind, one for the restart.
verifyBind(2);
verify(mMockTileService, times(2)).onStartListening();
@@ -319,6 +346,7 @@
mMockBroadcastDispatcher,
mTileServiceIntent,
mUser,
+ mActivityManager,
mExecutor);
manager.executeSetBindService(true);
@@ -340,6 +368,7 @@
mMockBroadcastDispatcher,
mTileServiceIntent,
mUser,
+ mActivityManager,
mExecutor);
manager.executeSetBindService(true);
@@ -361,6 +390,7 @@
mMockBroadcastDispatcher,
mTileServiceIntent,
mUser,
+ mActivityManager,
mExecutor);
manager.executeSetBindService(true);
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
index 4bc16a5..d011821 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/TileServicesTest.java
@@ -304,7 +304,7 @@
CustomTileAddedRepository customTileAddedRepository, DelayableExecutor executor) {
super(host, handlerProvider, broadcastDispatcher, userTracker, keyguardStateController,
commandQueue, statusBarIconController, panelInteractor,
- customTileAddedRepository, executor);
+ mTileLifecycleManagerFactory, customTileAddedRepository, executor);
}
@Override