summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--packages/SettingsLib/src/com/android/settingslib/applications/AppIconCacheManager.java26
-rw-r--r--packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppIconCacheManagerTest.java70
2 files changed, 95 insertions, 1 deletions
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/AppIconCacheManager.java b/packages/SettingsLib/src/com/android/settingslib/applications/AppIconCacheManager.java
index 9dfc8eaac024..c0117b952beb 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/AppIconCacheManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/AppIconCacheManager.java
@@ -22,13 +22,16 @@ import android.os.UserHandle;
import android.util.Log;
import android.util.LruCache;
+import androidx.annotation.VisibleForTesting;
+
/**
* Cache app icon for management.
*/
public class AppIconCacheManager {
private static final String TAG = "AppIconCacheManager";
private static final float CACHE_RATIO = 0.1f;
- private static final int MAX_CACHE_SIZE_IN_KB = getMaxCacheInKb();
+ @VisibleForTesting
+ static final int MAX_CACHE_SIZE_IN_KB = getMaxCacheInKb();
private static final String DELIMITER = ":";
private static AppIconCacheManager sAppIconCacheManager;
private final LruCache<String, Drawable> mDrawableCache;
@@ -109,4 +112,25 @@ public class AppIconCacheManager {
private static int getMaxCacheInKb() {
return Math.round(CACHE_RATIO * Runtime.getRuntime().maxMemory() / 1024);
}
+
+ /**
+ * Clears as much memory as possible.
+ *
+ * @see android.content.ComponentCallbacks2#onTrimMemory(int)
+ */
+ public void trimMemory(int level) {
+ if (level >= android.content.ComponentCallbacks2.TRIM_MEMORY_BACKGROUND) {
+ // Time to clear everything
+ if (sAppIconCacheManager != null) {
+ sAppIconCacheManager.mDrawableCache.trimToSize(0);
+ }
+ } else if (level >= android.content.ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN
+ || level == android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL) {
+ // Tough time but still affordable, clear half of the cache
+ if (sAppIconCacheManager != null) {
+ final int maxSize = sAppIconCacheManager.mDrawableCache.maxSize();
+ sAppIconCacheManager.mDrawableCache.trimToSize(maxSize / 2);
+ }
+ }
+ }
}
diff --git a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppIconCacheManagerTest.java b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppIconCacheManagerTest.java
index 64f8bef1ecf3..1b0e1f1236c8 100644
--- a/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppIconCacheManagerTest.java
+++ b/packages/SettingsLib/tests/robotests/src/com/android/settingslib/applications/AppIconCacheManagerTest.java
@@ -34,11 +34,21 @@ import org.robolectric.RobolectricTestRunner;
public class AppIconCacheManagerTest {
private static final String APP_PACKAGE_NAME = "com.test.app";
+ private static final String APP_PACKAGE_NAME1 = "com.test.app1";
+ private static final String APP_PACKAGE_NAME2 = "com.test.app2";
+ private static final String APP_PACKAGE_NAME3 = "com.test.app3";
private static final int APP_UID = 9999;
@Mock
private Drawable mIcon;
+ @Mock
+ private Drawable mIcon1;
+ @Mock
+ private Drawable mIcon2;
+ @Mock
+ private Drawable mIcon3;
+
private AppIconCacheManager mAppIconCacheManager;
@Before
@@ -48,6 +58,29 @@ public class AppIconCacheManagerTest {
doReturn(10).when(mIcon).getIntrinsicHeight();
doReturn(10).when(mIcon).getIntrinsicWidth();
doReturn(mIcon).when(mIcon).mutate();
+
+ // Algorithm for trim memory test:
+ // The real maxsize is defined by AppIconCacheManager.MAX_CACHE_SIZE_IN_KB, and the size
+ // of each element is calculated as following:
+ // n * n * 4 / 1024
+ // In the testcase, we want to mock the maxsize of LruCache is 3, so the formula calculating
+ // the size of each element will be like:
+ // n * n * 4 / 1024 = maxsize / 3
+ // Thus, n = square_root(maxsize / 3 * 1024 / 4), which can be used as an icon size.
+ final int iconSize =
+ (int) Math.sqrt(AppIconCacheManager.MAX_CACHE_SIZE_IN_KB / 3f * 1024f / 4f);
+
+ doReturn(iconSize).when(mIcon1).getIntrinsicHeight();
+ doReturn(iconSize).when(mIcon1).getIntrinsicWidth();
+ doReturn(mIcon1).when(mIcon1).mutate();
+
+ doReturn(iconSize).when(mIcon2).getIntrinsicHeight();
+ doReturn(iconSize).when(mIcon2).getIntrinsicWidth();
+ doReturn(mIcon2).when(mIcon2).mutate();
+
+ doReturn(iconSize).when(mIcon3).getIntrinsicHeight();
+ doReturn(iconSize).when(mIcon3).getIntrinsicWidth();
+ doReturn(mIcon3).when(mIcon3).mutate();
}
@After
@@ -106,4 +139,41 @@ public class AppIconCacheManagerTest {
assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME, APP_UID)).isNull();
}
+
+ @Test
+ public void trimMemory_levelSatisfied_shouldNotCacheIcon() {
+
+ mAppIconCacheManager.put(APP_PACKAGE_NAME1, APP_UID, mIcon1);
+ mAppIconCacheManager.put(APP_PACKAGE_NAME2, APP_UID, mIcon2);
+ mAppIconCacheManager.put(APP_PACKAGE_NAME3, APP_UID, mIcon3);
+
+ // Expected to trim size to 0
+ final int level = android.content.ComponentCallbacks2.TRIM_MEMORY_BACKGROUND;
+ mAppIconCacheManager.trimMemory(level);
+
+ // None of the elements should be cached
+ assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME1, APP_UID)).isNull();
+ assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME2, APP_UID)).isNull();
+ assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME3, APP_UID)).isNull();
+ }
+
+ @Test
+ public void trimMemory_levelSatisfied_shouldCacheAtLeastHalf() {
+
+ mAppIconCacheManager.put(APP_PACKAGE_NAME1, APP_UID, mIcon1);
+ mAppIconCacheManager.put(APP_PACKAGE_NAME2, APP_UID, mIcon2);
+ mAppIconCacheManager.put(APP_PACKAGE_NAME3, APP_UID, mIcon3);
+
+ // Get the last element
+ mAppIconCacheManager.get(APP_PACKAGE_NAME1, APP_UID);
+
+ // Expected to trim size to half of it, which is int( 3 / 2 ) = 1
+ final int level = android.content.ComponentCallbacks2.TRIM_MEMORY_RUNNING_CRITICAL;
+ mAppIconCacheManager.trimMemory(level);
+
+ // There should be only one cached element, which is the last recently used one
+ assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME1, APP_UID)).isNotNull();
+ assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME2, APP_UID)).isNull();
+ assertThat(mAppIconCacheManager.get(APP_PACKAGE_NAME3, APP_UID)).isNull();
+ }
}