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

Commit 9262d19d authored by Behnam Heydarshahi's avatar Behnam Heydarshahi
Browse files

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
parent c8bcb9a1
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -294,6 +294,16 @@ flag {
   bug: "311147395"
}

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"
+29 −5
Original line number Diff line number Diff line
@@ -50,10 +50,12 @@ import androidx.annotation.NonNull;
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 @@ public class TileLifecycleManager extends BroadcastReceiver implements
    // 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 @@ public class TileLifecycleManager extends BroadcastReceiver implements
    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 @@ public class TileLifecycleManager extends BroadcastReceiver implements
    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 @@ public class TileLifecycleManager extends BroadcastReceiver implements
    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 @@ public class TileLifecycleManager extends BroadcastReceiver implements
        mIntent.putExtra(TileService.EXTRA_TOKEN, mToken);
        mUser = user;
        mExecutor = executor;
        mSystemClock = systemClock;
        mPackageManagerAdapter = packageManagerAdapter;
        mBroadcastDispatcher = broadcastDispatcher;
        mActivityManager = activityManager;
@@ -436,25 +441,31 @@ public class TileLifecycleManager extends BroadcastReceiver implements
            // 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 @@ public class TileLifecycleManager extends BroadcastReceiver implements
        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;
+1 −1
Original line number Diff line number Diff line
@@ -44,7 +44,7 @@ import java.util.concurrent.atomic.AtomicBoolean;
/**
 * 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 {

+140 −15
Original line number Diff line number Diff line
@@ -22,6 +22,7 @@ import static android.service.quicksettings.TileService.START_ACTIVITY_NEEDS_PEN
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.broadcast.BroadcastDispatcher;
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 @@ public class TileLifecycleManagerTest extends SysuiTestCase {

    @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 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
                mUser,
                mActivityManager,
                mDeviceIdleController,
                mExecutor);
                mExecutor,
                mClock);
    }

    @After
@@ -169,12 +174,12 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
        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 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
                .thenReturn(defaultPackageInfo);
    }

    private void setPackageEnabled(boolean enabled) throws Exception {
        setPackageEnabledAndActive(enabled, true);
    }

    private void setPackageInstalledForUser(
            boolean installed,
            boolean active,
@@ -396,18 +405,125 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
    }

    @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.
        verifyBind(2);
        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
@@ -510,7 +626,8 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
                mUser,
                mActivityManager,
                mDeviceIdleController,
                mExecutor);
                mExecutor,
                mClock);

        manager.executeSetBindService(true);
        mExecutor.runAllReady();
@@ -533,7 +650,8 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
                mUser,
                mActivityManager,
                mDeviceIdleController,
                mExecutor);
                mExecutor,
                mClock);

        manager.executeSetBindService(true);
        mExecutor.runAllReady();
@@ -556,7 +674,8 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
                mUser,
                mActivityManager,
                mDeviceIdleController,
                mExecutor);
                mExecutor,
                mClock);

        manager.executeSetBindService(true);
        mExecutor.runAllReady();
@@ -581,7 +700,8 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
                mUser,
                mActivityManager,
                mDeviceIdleController,
                mExecutor);
                mExecutor,
                mClock);

        manager.executeSetBindService(true);
        mExecutor.runAllReady();
@@ -607,7 +727,8 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
                mUser,
                mActivityManager,
                mDeviceIdleController,
                mExecutor);
                mExecutor,
                mClock);

        assertThat(manager.isActiveTile()).isTrue();
    }
@@ -626,7 +747,8 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
                mUser,
                mActivityManager,
                mDeviceIdleController,
                mExecutor);
                mExecutor,
                mClock);

        assertThat(manager.isActiveTile()).isTrue();
    }
@@ -644,7 +766,8 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
                mUser,
                mActivityManager,
                mDeviceIdleController,
                mExecutor);
                mExecutor,
                mClock);

        assertThat(manager.isToggleableTile()).isTrue();
    }
@@ -663,7 +786,8 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
                mUser,
                mActivityManager,
                mDeviceIdleController,
                mExecutor);
                mExecutor,
                mClock);

        assertThat(manager.isToggleableTile()).isTrue();
    }
@@ -682,7 +806,8 @@ public class TileLifecycleManagerTest extends SysuiTestCase {
                mUser,
                mActivityManager,
                mDeviceIdleController,
                mExecutor);
                mExecutor,
                mClock);

        assertThat(manager.isToggleableTile()).isFalse();
        assertThat(manager.isActiveTile()).isFalse();
+2 −0
Original line number Diff line number Diff line
@@ -24,6 +24,7 @@ import com.android.systemui.concurrency.fakeExecutor
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 @@ val Kosmos.tileLifecycleManagerFactory: TileLifecycleManager.Factory by
                activityManager,
                mock(),
                fakeExecutor,
                fakeSystemClock,
            )
        }
    }