diff options
| author | 2023-10-09 18:17:34 +0100 | |
|---|---|---|
| committer | 2023-10-09 18:17:34 +0100 | |
| commit | 8d2f697646002e5d9075ae46606da25857deeee6 (patch) | |
| tree | b4f79fbafd448d441de6d8e059a71e17159998ea | |
| parent | ce28f5384169ba8cae1ba674caca2e2382e3fb57 (diff) | |
Add Low memory state TileService rebinding delay
The problem is that TileService might be unbound by the system when it
kills the app to release memory in Low Memory state. This change
increases service rebind delay and increases it even further for
Low Memory state.
Test: manually check TileService binding when the providing app is
killed
Test: atest TileLifecycleManagerTest TileServicesTest
Fixes: 302657931
Change-Id: I2f3601b1936e41a676a8ac947d4e816554f6a089
5 files changed, 89 insertions, 35 deletions
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 2469a98140e3..78f2da53cd43 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 @@ package com.android.systemui.qs.external; 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.os.UserHandle; 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 @@ public class TileLifecycleManager extends BroadcastReceiver implements // 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 @@ public class TileLifecycleManager extends BroadcastReceiver implements 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 @@ public class TileLifecycleManager extends BroadcastReceiver implements 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 @@ public class TileLifecycleManager extends BroadcastReceiver implements @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 @@ public class TileLifecycleManager extends BroadcastReceiver implements mExecutor = executor; mPackageManagerAdapter = packageManagerAdapter; mBroadcastDispatcher = broadcastDispatcher; + mActivityManager = activityManager; if (DEBUG) Log.d(TAG, "Creating " + mIntent + " " + mUser); } @@ -152,10 +158,6 @@ public class TileLifecycleManager extends BroadcastReceiver implements } } - public void setBindRetryDelay(int delayMs) { - mBindRetryDelay = delayMs; - } - public boolean isActiveTile() { try { ServiceInfo info = mPackageManagerAdapter.getServiceInfo(mIntent.getComponent(), @@ -250,19 +252,15 @@ public class TileLifecycleManager extends BroadcastReceiver implements 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,8 +350,32 @@ public class TileLifecycleManager extends BroadcastReceiver implements 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() { 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 941a9d6dc952..3ee4a1be40b2 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 @@ public class TileServiceManager { 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 acee8e9ad2eb..c3744df5faeb 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 @@ public class TileServices extends IQSService.Stub { 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 @@ public class TileServices extends IQSService.Stub { CommandQueue commandQueue, StatusBarIconController statusBarIconController, PanelInteractor panelInteractor, + TileLifecycleManager.Factory tileLifecycleManagerFactory, CustomTileAddedRepository customTileAddedRepository, @Background DelayableExecutor backgroundExecutor) { mHost = host; @@ -109,6 +111,7 @@ public class TileServices extends IQSService.Stub { mStatusBarIconController = statusBarIconController; mCommandQueue.addCallback(mRequestListeningCallback); mPanelInteractor = panelInteractor; + mTileLifecycleManagerFactory = tileLifecycleManagerFactory; mCustomTileAddedRepository = customTileAddedRepository; mBackgroundExecutor = backgroundExecutor; } @@ -137,8 +140,8 @@ public class TileServices extends IQSService.Stub { 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 @@ public class TileServices extends IQSService.Stub { 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 @@ public class TileServices extends IQSService.Stub { 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 67587e3a8914..6cc52d70611a 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.ArgumentMatchers.eq; 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 @@ import org.mockito.MockitoSession; @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 @@ public class TileLifecycleManagerTest extends SysuiTestCase { 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 @@ public class TileLifecycleManagerTest extends SysuiTestCase { mStateManager.onStartListening(); mStateManager.executeSetBindService(true); mExecutor.runAllReady(); - mStateManager.setBindRetryDelay(0); - mExecutor.runAllReady(); 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); + + // 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 @@ public class TileLifecycleManagerTest extends SysuiTestCase { mMockBroadcastDispatcher, mTileServiceIntent, mUser, + mActivityManager, mExecutor); manager.executeSetBindService(true); @@ -340,6 +368,7 @@ public class TileLifecycleManagerTest extends SysuiTestCase { mMockBroadcastDispatcher, mTileServiceIntent, mUser, + mActivityManager, mExecutor); manager.executeSetBindService(true); @@ -361,6 +390,7 @@ public class TileLifecycleManagerTest extends SysuiTestCase { 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 4bc16a52f8dc..d0118218134c 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 @@ public class TileServicesTest extends SysuiTestCase { CustomTileAddedRepository customTileAddedRepository, DelayableExecutor executor) { super(host, handlerProvider, broadcastDispatcher, userTracker, keyguardStateController, commandQueue, statusBarIconController, panelInteractor, - customTileAddedRepository, executor); + mTileLifecycleManagerFactory, customTileAddedRepository, executor); } @Override |