From f8ffbd327cb14918710ecf1b73bcba75468edda5 Mon Sep 17 00:00:00 2001 From: Fabian Kozynski Date: Fri, 27 Aug 2021 15:49:47 -0400 Subject: [PATCH] Postpone available check for CustomTile In the case of custom tiles, we don't know if they are available until after initialization. Because it runs async to tile creation, the check in QSTileHost may fail and destroy the tile prematurely. Instead, postpone it until after we've tried to retrieve the service icon. Also, add TileService CTS tests to com.android.systemui.qs TEST_MAPPING. Test: atest SystemUITests Test: atest CtsAppTestCases Fixes: 197970498 Change-Id: Id8d4090cfbfaa144b71d78ffd74da8fc1b9e6c6e --- .../src/com/android/systemui/qs/TEST_MAPPING | 21 +++++++++++++++++++ .../systemui/qs/external/CustomTile.java | 20 +++++++++++++++++- .../systemui/qs/tileimpl/QSTileImpl.java | 2 +- .../android/systemui/qs/QSTileHostTest.java | 4 ++-- .../systemui/qs/external/CustomTileTest.kt | 16 ++++++++++++++ 5 files changed, 59 insertions(+), 4 deletions(-) create mode 100644 packages/SystemUI/src/com/android/systemui/qs/TEST_MAPPING diff --git a/packages/SystemUI/src/com/android/systemui/qs/TEST_MAPPING b/packages/SystemUI/src/com/android/systemui/qs/TEST_MAPPING new file mode 100644 index 0000000000000..55da2038754d9 --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/qs/TEST_MAPPING @@ -0,0 +1,21 @@ +{ + "presubmit": [ + { + "name": "CtsAppTestCases", + "options": [ + { + "include-filter": "android.app.cts.TileServiceTest" + }, + { + "include-filter": "android.app.cts.BooleanTileServiceTest" + }, + { + "exclude-annotation": "org.junit.Ignore" + }, + { + "exclude-annotation": "androidx.test.filters.FlakyTest" + } + ] + } + ] +} \ No newline at end of file diff --git a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java index 396eca5c1bee9..c5fa76e794af6 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java +++ b/packages/SystemUI/src/com/android/systemui/qs/external/CustomTile.java @@ -63,6 +63,7 @@ import com.android.systemui.qs.logging.QSLogger; import com.android.systemui.qs.tileimpl.QSTileImpl; import java.util.Objects; +import java.util.concurrent.atomic.AtomicBoolean; import javax.inject.Inject; @@ -98,6 +99,8 @@ public class CustomTile extends QSTileImpl implements TileChangeListener private final TileServiceKey mKey; + private final AtomicBoolean mInitialDefaultIconFetched = new AtomicBoolean(false); + private CustomTile( QSHost host, Looper backgroundLooper, @@ -128,6 +131,12 @@ public class CustomTile extends QSTileImpl implements TileChangeListener @Override protected void handleInitialize() { updateDefaultTileAndIcon(); + if (mInitialDefaultIconFetched.compareAndSet(false, true)) { + if (mDefaultIcon == null) { + mQSLogger.logTileDestroyed(getTileSpec(), + "Custom tile default icon not available"); + } + } if (mServiceManager.isToggleableTile()) { // Replace states with BooleanState resetStates(); @@ -213,9 +222,18 @@ public class CustomTile extends QSTileImpl implements TileChangeListener mHandler.post(this::updateDefaultTileAndIcon); } + /** + * Custom tile is considered available if there is a default icon (obtained from PM). + * + * It will return {@code true} before initialization, so tiles are not destroyed prematurely. + */ @Override public boolean isAvailable() { - return mDefaultIcon != null; + if (mInitialDefaultIconFetched.get()) { + return mDefaultIcon != null; + } else { + return true; + } } public int getUser() { diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java index 4616be8f79376..70e3a2b2bf268 100644 --- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java +++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSTileImpl.java @@ -105,7 +105,7 @@ public abstract class QSTileImpl implements QSTile, Lifecy protected final ActivityStarter mActivityStarter; private final UiEventLogger mUiEventLogger; private final FalsingManager mFalsingManager; - private final QSLogger mQSLogger; + protected final QSLogger mQSLogger; private volatile int mReadyState; private final ArrayList mCallbacks = new ArrayList<>(); diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java index fbb0a9555df5e..c746bcae6e2e8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/QSTileHostTest.java @@ -20,8 +20,8 @@ package com.android.systemui.qs; import static junit.framework.Assert.assertEquals; import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; + import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.isNull; import static org.mockito.Mockito.mock; @@ -424,7 +424,7 @@ public class QSTileHostTest extends SysuiTestCase { mock(MetricsLogger.class), mock(StatusBarStateController.class), mock(ActivityStarter.class), - mQSLogger + QSTileHostTest.this.mQSLogger ); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt index 9b5c1619ef316..9755d91d72d8e 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/qs/external/CustomTileTest.kt @@ -270,4 +270,20 @@ class CustomTileTest : SysuiTestCase() { verify(customTileStatePersister) .persistState(TileServiceKey(componentName, customTile.user), t) } + + @Test + fun testAvailableBeforeInitialization() { + `when`(packageManager.getApplicationInfo(anyString(), anyInt())) + .thenThrow(PackageManager.NameNotFoundException()) + val tile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext) + assertTrue(tile.isAvailable) + } + + @Test + fun testNotAvailableAfterInitializationWithoutIcon() { + val tile = CustomTile.create(customTileBuilder, TILE_SPEC, mContext) + tile.initialize() + testableLooper.processAllMessages() + assertFalse(tile.isAvailable) + } } \ No newline at end of file