Removing dependency on PackageInfo in IconCache

Bug: 363324203
Flag: EXEMPT bugfix
Test: atest IconCacheTest
      atest IconCacheUpdateHandler

Change-Id: I85005ef1069960a17a0f3e7265749a8ef3004172
This commit is contained in:
Sunny Goyal
2024-10-08 04:09:54 +00:00
parent 5ddf3ba31f
commit b619d2c297
9 changed files with 203 additions and 86 deletions
@@ -24,6 +24,7 @@ import static com.android.launcher3.icons.IconCacheUpdateHandlerTestKt.waitForUp
import static com.android.launcher3.model.data.AppInfo.makeLaunchIntent;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY;
import static com.android.launcher3.util.LauncherModelHelper.TEST_ACTIVITY2;
import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
import static com.android.launcher3.util.TestUtil.runOnExecutorSync;
@@ -210,6 +211,70 @@ public class IconCacheTest {
() -> assertNull(mIconCache.getInMemoryEntryLocked(cacheKey)));
}
@Test
public void item_kept_in_db_if_nothing_changes() {
RoboApiWrapper.INSTANCE.initialize();
ComponentName cn = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
UserHandle user = myUserHandle();
LauncherActivityInfo lai = mContext.getSystemService(LauncherApps.class)
.resolveActivity(makeLaunchIntent(cn), user);
assertNotNull(lai);
// Since this is a new update, there should not be any update
Truth.assertThat(executeIconUpdate(lai, LauncherActivityCachingLogic.INSTANCE)).isEmpty();
assertTrue(mIconCache.isItemInDb(new ComponentKey(cn, user)));
// Another update should not cause any changes
Truth.assertThat(executeIconUpdate(lai, LauncherActivityCachingLogic.INSTANCE)).isEmpty();
assertTrue(mIconCache.isItemInDb(new ComponentKey(cn, user)));
}
@Test
public void item_updated_in_db_if_appInfo_changes() {
RoboApiWrapper.INSTANCE.initialize();
ComponentName cn = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
UserHandle user = myUserHandle();
LauncherActivityInfo lai = mContext.getSystemService(LauncherApps.class)
.resolveActivity(makeLaunchIntent(cn), user);
assertNotNull(lai);
// Since this is a new update, there should not be any update
Truth.assertThat(executeIconUpdate(lai, LauncherActivityCachingLogic.INSTANCE)).isEmpty();
assertTrue(mIconCache.isItemInDb(new ComponentKey(cn, user)));
// Another update should trigger an update
lai.getApplicationInfo().sourceDir = "some-random-source-dir";
Truth.assertThat(executeIconUpdate(lai, LauncherActivityCachingLogic.INSTANCE))
.containsExactly(new PackageUserKey(TEST_PACKAGE, user));
assertTrue(mIconCache.isItemInDb(new ComponentKey(cn, user)));
}
@Test
public void item_removed_in_db_if_item_removed() {
RoboApiWrapper.INSTANCE.initialize();
ComponentName cn = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY);
UserHandle user = myUserHandle();
LauncherActivityInfo lai = mContext.getSystemService(LauncherApps.class)
.resolveActivity(makeLaunchIntent(cn), user);
assertNotNull(lai);
// Since this is a new update, there should not be any update
Truth.assertThat(executeIconUpdate(lai, LauncherActivityCachingLogic.INSTANCE)).isEmpty();
assertTrue(mIconCache.isItemInDb(new ComponentKey(cn, user)));
// Another update should trigger an update
ComponentName cn2 = new ComponentName(TEST_PACKAGE, TEST_ACTIVITY2);
LauncherActivityInfo lai2 = mContext.getSystemService(LauncherApps.class)
.resolveActivity(makeLaunchIntent(cn2), user);
Truth.assertThat(executeIconUpdate(lai2, LauncherActivityCachingLogic.INSTANCE)).isEmpty();
assertFalse(mIconCache.isItemInDb(new ComponentKey(cn, user)));
assertTrue(mIconCache.isItemInDb(new ComponentKey(cn2, user)));
}
/**
* Executes the icon update for the provided entry and returns the updated packages
*/
@@ -17,77 +17,109 @@
package com.android.launcher3.icons
import android.content.ComponentName
import android.content.pm.PackageInfo
import android.database.Cursor
import android.os.UserHandle
import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import android.database.MatrixCursor
import android.os.Process.myUserHandle
import androidx.test.core.app.ApplicationProvider.getApplicationContext
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.launcher3.icons.cache.BaseIconCache
import com.android.launcher3.icons.cache.CachingLogic
import com.android.launcher3.icons.cache.BaseIconCache.IconDB
import com.android.launcher3.icons.cache.CachedObject
import com.android.launcher3.icons.cache.CachedObjectCachingLogic
import com.android.launcher3.icons.cache.IconCacheUpdateHandler
import com.android.launcher3.util.RoboApiWrapper
import com.google.common.truth.Truth.assertThat
import java.util.concurrent.FutureTask
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
import org.mockito.MockitoAnnotations
import org.mockito.kotlin.doReturn
import org.mockito.kotlin.whenever
@SmallTest
@RunWith(AndroidJUnit4::class)
class IconCacheUpdateHandlerTest {
@Mock private lateinit var cursor: Cursor
@Mock private lateinit var user: UserHandle
@Mock private lateinit var cachingLogic: CachingLogic<String>
@Mock private lateinit var iconProvider: IconProvider
@Mock private lateinit var baseIconCache: BaseIconCache
private var componentMap: HashMap<ComponentName, String> = hashMapOf()
private var ignorePackages: Set<String> = setOf()
private var packageInfoMap: HashMap<String, PackageInfo> = hashMapOf()
private val dummyRowData =
IconCacheRowData(
"com.android.fake/.FakeActivity",
System.currentTimeMillis(),
1,
1.0.toLong(),
"stateOfConfusion",
)
private var cursor: MatrixCursor? = null
private var cachingLogic = CachedObjectCachingLogic<BaseIconCache>(getApplicationContext())
@Before
fun setup() {
MockitoAnnotations.initMocks(this)
// Load in a specific row to the database
doReturn(0).`when`(cursor).getColumnIndex(BaseIconCache.IconDB.COLUMN_COMPONENT)
doReturn(1).`when`(cursor).getColumnIndex(BaseIconCache.IconDB.COLUMN_LAST_UPDATED)
doReturn(2).`when`(cursor).getColumnIndex(BaseIconCache.IconDB.COLUMN_VERSION)
doReturn(3).`when`(cursor).getColumnIndex(BaseIconCache.IconDB.COLUMN_ROWID)
doReturn(4).`when`(cursor).getColumnIndex(BaseIconCache.IconDB.COLUMN_SYSTEM_STATE)
doReturn(dummyRowData.component).`when`(cursor).getString(0)
doReturn(dummyRowData.lastUpdated).`when`(cursor).getLong(1)
doReturn(dummyRowData.version).`when`(cursor).getInt(2)
doReturn(dummyRowData.row).`when`(cursor).getLong(3)
doReturn(dummyRowData.systemState).`when`(cursor).getString(4)
doReturn(iconProvider).whenever(baseIconCache).iconProvider
}
@After
fun tearDown() {
cursor?.close()
}
@Test
fun `IconCacheUpdateHandler returns null if the component name is malformed`() {
val updateHandlerUnderTest = IconCacheUpdateHandler(packageInfoMap, baseIconCache)
val updateHandlerUnderTest = IconCacheUpdateHandler(baseIconCache)
val cn = ComponentName.unflattenFromString("com.android.fake/.FakeActivity")!!
val result =
updateHandlerUnderTest.updateOrDeleteIcon(
cursor,
componentMap,
ignorePackages,
user,
createCursor(1, cn.flattenToString() + "#", "freshId-old"),
hashMapOf(cn to TestCachedObject(cn, "freshId")),
setOf(),
myUserHandle(),
cachingLogic,
)
assert(result == null)
assertThat(result).isNull()
}
@Test
fun `IconCacheUpdateHandler returns null if the freshId match`() {
val updateHandlerUnderTest = IconCacheUpdateHandler(baseIconCache)
val cn = ComponentName.unflattenFromString("com.android.fake/.FakeActivity")!!
val result =
updateHandlerUnderTest.updateOrDeleteIcon(
createCursor(1, cn.flattenToString(), "freshId"),
hashMapOf(cn to TestCachedObject(cn, "freshId")),
setOf(),
myUserHandle(),
cachingLogic,
)
assertThat(result).isNull()
}
@Test
fun `IconCacheUpdateHandler returns non-null if the freshId do not match`() {
val updateHandlerUnderTest = IconCacheUpdateHandler(baseIconCache)
val cn = ComponentName.unflattenFromString("com.android.fake/.FakeActivity")!!
val testObj = TestCachedObject(cn, "freshId")
val result =
updateHandlerUnderTest.updateOrDeleteIcon(
createCursor(1, cn.flattenToString(), "freshId-old"),
hashMapOf(cn to testObj),
setOf(),
myUserHandle(),
cachingLogic,
)
assertThat(result).isEqualTo(testObj)
}
private fun createCursor(row: Long, component: String, appState: String) =
MatrixCursor(
arrayOf(IconDB.COLUMN_ROWID, IconDB.COLUMN_COMPONENT, IconDB.COLUMN_FRESHNESS_ID)
)
.apply { addRow(arrayOf(row, component, appState)) }
.apply {
cursor = this
moveToNext()
}
}
/** Utility method to wait for the icon update handler to finish */
@@ -105,10 +137,16 @@ fun IconCache.waitForUpdateHandlerToFinish() {
}
}
data class IconCacheRowData(
val component: String,
val lastUpdated: Long,
val version: Int,
val row: Long,
val systemState: String,
)
class TestCachedObject(val cn: ComponentName, val freshnessId: String) :
CachedObject<BaseIconCache> {
override fun getComponent() = cn
override fun getUser() = myUserHandle()
override fun getLabel(pm: PackageManager?): CharSequence? = null
override fun getApplicationInfo(): ApplicationInfo? = null
override fun getFreshnessIdentifier(iconProvider: IconProvider): String? = freshnessId
}
@@ -38,8 +38,8 @@ class GeneratedPreviewTest {
@get:Rule val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
private val providerName =
ComponentName(
"com.android.launcher3.tests",
"com.android.launcher3.testcomponent.AppWidgetNoConfig"
getInstrumentation().getContext().getPackageName(),
"com.android.launcher3.testcomponent.AppWidgetNoConfig",
)
private val generatedPreviewLayout =
getInstrumentation().context.run {
@@ -61,7 +61,7 @@ class GeneratedPreviewTest {
ActivityContextWrapper(
ContextThemeWrapper(
context,
com.android.launcher3.R.style.WidgetContainerTheme
com.android.launcher3.R.style.WidgetContainerTheme,
)
)
)
@@ -78,7 +78,7 @@ class GeneratedPreviewTest {
object : WidgetManagerHelper(context) {
override fun loadGeneratedPreview(
info: AppWidgetProviderInfo,
widgetCategory: Int
widgetCategory: Int,
) =
generatedPreview.takeIf {
info === appWidgetProviderInfo &&