From c4ef08411d04159fc0bc472c2037fddd1360c81c Mon Sep 17 00:00:00 2001 From: Fabián Kozynski Date: Tue, 28 Mar 2023 15:30:34 -0400 Subject: Store tiles by component name and user This way, when the user changes, the old tile is not just replaced in the map, instead we keep it until it can be freed. This solves an issue of active tiles not being able to be set into listening state after user changing. Test: atest TileServices Test: manual, using sample tile service app Fixes: 243902636 Change-Id: I57d08aec5c0b40f09f5bd763d63068aaea7ba450 --- .../android/systemui/qs/external/TileServices.java | 18 ++++++---- .../systemui/qs/external/TileServicesTest.java | 40 ++++++++++++++++++++++ 2 files changed, 51 insertions(+), 7 deletions(-) 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 5e4f53181706..42536fef17aa 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/TileServices.java @@ -30,6 +30,7 @@ import android.service.quicksettings.IQSService; import android.service.quicksettings.Tile; import android.util.ArrayMap; import android.util.Log; +import android.util.SparseArrayMap; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -64,7 +65,7 @@ public class TileServices extends IQSService.Stub { private static final String TAG = "TileServices"; private final ArrayMap mServices = new ArrayMap<>(); - private final ArrayMap mTiles = new ArrayMap<>(); + private final SparseArrayMap mTiles = new SparseArrayMap<>(); private final ArrayMap mTokenMap = new ArrayMap<>(); private final Context mContext; private final Handler mMainHandler; @@ -112,10 +113,11 @@ public class TileServices extends IQSService.Stub { public TileServiceManager getTileWrapper(CustomTile tile) { ComponentName component = tile.getComponent(); + int userId = tile.getUser(); TileServiceManager service = onCreateTileService(component, mBroadcastDispatcher); synchronized (mServices) { mServices.put(tile, service); - mTiles.put(component, tile); + mTiles.add(userId, component, tile); mTokenMap.put(service.getToken(), tile); } // Makes sure binding only happens after the maps have been populated @@ -135,7 +137,7 @@ public class TileServices extends IQSService.Stub { service.handleDestroy(); mServices.remove(tile); mTokenMap.remove(service.getToken()); - mTiles.remove(tile.getComponent()); + mTiles.delete(tile.getUser(), tile.getComponent()); final String slot = getStatusBarIconSlotName(tile.getComponent()); mMainHandler.post(() -> mStatusBarIconController.removeIconForTile(slot)); } @@ -188,9 +190,10 @@ public class TileServices extends IQSService.Stub { private void requestListening(ComponentName component) { synchronized (mServices) { - CustomTile customTile = getTileForComponent(component); + int userId = mUserTracker.getUserId(); + CustomTile customTile = getTileForUserAndComponent(userId, component); if (customTile == null) { - Log.d("TileServices", "Couldn't find tile for " + component); + Log.d(TAG, "Couldn't find tile for " + component + "(" + userId + ")"); return; } TileServiceManager service = mServices.get(customTile); @@ -362,9 +365,9 @@ public class TileServices extends IQSService.Stub { } @Nullable - private CustomTile getTileForComponent(ComponentName component) { + private CustomTile getTileForUserAndComponent(int userId, ComponentName component) { synchronized (mServices) { - return mTiles.get(component); + return mTiles.get(userId, component); } } @@ -395,4 +398,5 @@ public class TileServices extends IQSService.Stub { return -Integer.compare(left.getBindPriority(), right.getBindPriority()); } }; + } 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 7e052bfa15d2..fb9336734d99 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 @@ -54,6 +54,7 @@ import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -95,6 +96,8 @@ public class TileServicesTest extends SysuiTestCase { private QSHost mQSHost; @Mock private PanelInteractor mPanelInteractor; + @Captor + private ArgumentCaptor mCallbacksArgumentCaptor; @Before public void setUp() throws Exception { @@ -251,6 +254,41 @@ public class TileServicesTest extends SysuiTestCase { verify(mPanelInteractor).forceCollapsePanels(); } + @Test + public void tileFreedForCorrectUser() throws RemoteException { + verify(mCommandQueue).addCallback(mCallbacksArgumentCaptor.capture()); + + ComponentName componentName = new ComponentName("pkg", "cls"); + CustomTile tileUser0 = mock(CustomTile.class); + CustomTile tileUser1 = mock(CustomTile.class); + + when(tileUser0.getComponent()).thenReturn(componentName); + when(tileUser1.getComponent()).thenReturn(componentName); + when(tileUser0.getUser()).thenReturn(0); + when(tileUser1.getUser()).thenReturn(1); + + // Create a tile for user 0 + TileServiceManager manager0 = mTileService.getTileWrapper(tileUser0); + when(manager0.isActiveTile()).thenReturn(true); + // Then create a tile for user 1 + TileServiceManager manager1 = mTileService.getTileWrapper(tileUser1); + when(manager1.isActiveTile()).thenReturn(true); + + // When the tile for user 0 gets freed + mTileService.freeService(tileUser0, manager0); + // and the user is 1 + when(mUserTracker.getUserId()).thenReturn(1); + + // a call to requestListeningState + mCallbacksArgumentCaptor.getValue().requestTileServiceListeningState(componentName); + mTestableLooper.processAllMessages(); + + // will call in the correct tile + verify(manager1).setBindRequested(true); + // and set it to listening + verify(manager1.getTileService()).onStartListening(); + } + private class TestTileServices extends TileServices { TestTileServices(QSHost host, Provider handlerProvider, BroadcastDispatcher broadcastDispatcher, UserTracker userTracker, @@ -268,6 +306,8 @@ public class TileServicesTest extends SysuiTestCase { when(manager.isLifecycleStarted()).thenReturn(true); Binder b = new Binder(); when(manager.getToken()).thenReturn(b); + IQSTileService service = mock(IQSTileService.class); + when(manager.getTileService()).thenReturn(service); return manager; } } -- cgit v1.2.3-59-g8ed1b