Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5b361c07 authored by Anton Potapov's avatar Anton Potapov Committed by Android (Google) Code Review
Browse files

Merge "Add Low memory state TileService rebinding delay" into main

parents bb44d8c0 8d2f6976
Loading
Loading
Loading
Loading
+39 −17
Original line number Diff line number Diff line
@@ -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() {
+4 −5
Original line number Diff line number Diff line
@@ -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
+11 −8
Original line number Diff line number Diff line
@@ -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) {
+34 −4
Original line number Diff line number Diff line
@@ -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);
+1 −1
Original line number Diff line number Diff line
@@ -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