diff options
| -rw-r--r-- | core/api/module-lib-current.txt | 1 | ||||
| -rw-r--r-- | core/api/test-current.txt | 1 | ||||
| -rw-r--r-- | core/java/android/os/storage/StorageManager.java | 83 | ||||
| -rw-r--r-- | core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java | 49 |
4 files changed, 117 insertions, 17 deletions
diff --git a/core/api/module-lib-current.txt b/core/api/module-lib-current.txt index 70bb13a36b6e..ba34ce433ac6 100644 --- a/core/api/module-lib-current.txt +++ b/core/api/module-lib-current.txt @@ -341,6 +341,7 @@ package android.os { package android.os.storage { public class StorageManager { + method public long computeStorageCacheBytes(@NonNull java.io.File); method public void notifyAppIoBlocked(@NonNull java.util.UUID, int, int, int); method public void notifyAppIoResumed(@NonNull java.util.UUID, int, int, int); field public static final int APP_IO_BLOCKED_REASON_TRANSCODING = 1; // 0x1 diff --git a/core/api/test-current.txt b/core/api/test-current.txt index c4b20117b5b8..96374fbd05fa 100644 --- a/core/api/test-current.txt +++ b/core/api/test-current.txt @@ -1976,6 +1976,7 @@ package android.os.storage { } public class StorageManager { + method public long computeStorageCacheBytes(@NonNull java.io.File); method @NonNull public static java.util.UUID convert(@NonNull String); method @NonNull public static String convert(@NonNull java.util.UUID); method public boolean isAppIoBlocked(@NonNull java.util.UUID, int, int, int); diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java index 77c794cd17a8..e52cd5aa6cff 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -1434,11 +1434,36 @@ public class StorageManager { throw new IllegalStateException("Missing primary storage"); } - private static final int DEFAULT_THRESHOLD_PERCENTAGE = 5; - private static final long DEFAULT_THRESHOLD_MAX_BYTES = DataUnit.MEBIBYTES.toBytes(500); + /** + * Devices having above STORAGE_THRESHOLD_PERCENT_HIGH of total space free are considered to be + * in high free space category. + * + * @hide + */ + public static final int STORAGE_THRESHOLD_PERCENT_HIGH = 20; + /** + * Devices having below STORAGE_THRESHOLD_PERCENT_LOW of total space free are considered to be + * in low free space category. + * + * @hide + */ + public static final int STORAGE_THRESHOLD_PERCENT_LOW = 5; + /** + * For devices in high free space category, CACHE_RESERVE_PERCENT_HIGH percent of total space is + * allocated for cache. + * + * @hide + */ + public static final int CACHE_RESERVE_PERCENT_HIGH = 10; + /** + * For devices in low free space category, CACHE_RESERVE_PERCENT_LOW percent of total space is + * allocated for cache. + * + * @hide + */ + public static final int CACHE_RESERVE_PERCENT_LOW = 2; - private static final int DEFAULT_CACHE_PERCENTAGE = 10; - private static final long DEFAULT_CACHE_MAX_BYTES = DataUnit.GIBIBYTES.toBytes(5); + private static final long DEFAULT_THRESHOLD_MAX_BYTES = DataUnit.MEBIBYTES.toBytes(500); private static final long DEFAULT_FULL_THRESHOLD_BYTES = DataUnit.MEBIBYTES.toBytes(1); @@ -1462,7 +1487,7 @@ public class StorageManager { @UnsupportedAppUsage public long getStorageLowBytes(File path) { final long lowPercent = Settings.Global.getInt(mResolver, - Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE); + Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, STORAGE_THRESHOLD_PERCENT_LOW); final long lowBytes = (path.getTotalSpace() * lowPercent) / 100; final long maxLowBytes = Settings.Global.getLong(mResolver, @@ -1472,28 +1497,54 @@ public class StorageManager { } /** + * Compute the minimum number of bytes of storage on the device that could + * be reserved for cached data depending on the device state which is then passed on + * to getStorageCacheBytes. + * + * @hide + */ + @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) + @TestApi + @SuppressLint("StreamFiles") + public long computeStorageCacheBytes(@NonNull File path) { + final long totalBytes = path.getTotalSpace(); + final long usableBytes = path.getUsableSpace(); + final long storageThresholdHighBytes = totalBytes * STORAGE_THRESHOLD_PERCENT_HIGH / 100; + final long storageThresholdLowBytes = getStorageLowBytes(path); + long result; + if (usableBytes > storageThresholdHighBytes) { + // If free space is >STORAGE_THRESHOLD_PERCENT_HIGH of total space, + // reserve CACHE_RESERVE_PERCENT_HIGH of total space + result = totalBytes * CACHE_RESERVE_PERCENT_HIGH / 100; + } else if (usableBytes < storageThresholdLowBytes) { + // If free space is <min(STORAGE_THRESHOLD_PERCENT_LOW of total space, 500MB), + // reserve CACHE_RESERVE_PERCENT_LOW of total space + result = totalBytes * CACHE_RESERVE_PERCENT_LOW / 100; + } else { + // Else, linearly interpolate the amount of space to reserve + result = ((CACHE_RESERVE_PERCENT_HIGH - CACHE_RESERVE_PERCENT_LOW) + * (usableBytes - storageThresholdHighBytes) + CACHE_RESERVE_PERCENT_HIGH + * (storageThresholdHighBytes - storageThresholdLowBytes)) * totalBytes + / (100 * (storageThresholdHighBytes - storageThresholdLowBytes)); + } + return result; + } + + /** * Return the minimum number of bytes of storage on the device that should * be reserved for cached data. * * @hide */ - public long getStorageCacheBytes(File path, @AllocateFlags int flags) { - final long cachePercent = Settings.Global.getInt(mResolver, - Settings.Global.SYS_STORAGE_CACHE_PERCENTAGE, DEFAULT_CACHE_PERCENTAGE); - final long cacheBytes = (path.getTotalSpace() * cachePercent) / 100; - - final long maxCacheBytes = Settings.Global.getLong(mResolver, - Settings.Global.SYS_STORAGE_CACHE_MAX_BYTES, DEFAULT_CACHE_MAX_BYTES); - - final long result = Math.min(cacheBytes, maxCacheBytes); + public long getStorageCacheBytes(@NonNull File path, @AllocateFlags int flags) { if ((flags & StorageManager.FLAG_ALLOCATE_AGGRESSIVE) != 0) { return 0; } else if ((flags & StorageManager.FLAG_ALLOCATE_DEFY_ALL_RESERVED) != 0) { return 0; } else if ((flags & StorageManager.FLAG_ALLOCATE_DEFY_HALF_RESERVED) != 0) { - return result / 2; + return computeStorageCacheBytes(path) / 2; } else { - return result; + return computeStorageCacheBytes(path); } } diff --git a/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java b/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java index 16dcff5e0c8c..7ccbb0150cb6 100644 --- a/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java +++ b/core/tests/coretests/src/android/os/storage/StorageManagerBaseTest.java @@ -16,6 +16,10 @@ package android.os.storage; +import static com.google.common.truth.Truth.assertThat; + +import static org.mockito.Mockito.when; + import android.content.Context; import android.content.res.Resources; import android.test.InstrumentationTestCase; @@ -23,6 +27,10 @@ import android.util.Log; import libcore.io.Streams; +import org.junit.Test; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; + import java.io.BufferedReader; import java.io.DataInputStream; import java.io.File; @@ -39,6 +47,7 @@ public class StorageManagerBaseTest extends InstrumentationTestCase { protected Context mContext = null; protected StorageManager mSm = null; + @Mock private File mFile; private static String LOG_TAG = "StorageManagerBaseTest"; protected static final long MAX_WAIT_TIME = 120*1000; protected static final long WAIT_TIME_INCR = 5*1000; @@ -121,12 +130,50 @@ public class StorageManagerBaseTest extends InstrumentationTestCase { */ @Override public void setUp() throws Exception { + MockitoAnnotations.initMocks(this); mContext = getInstrumentation().getContext(); mSm = (StorageManager)mContext.getSystemService(android.content.Context.STORAGE_SERVICE); } /** + * Tests the space reserved for cache when system has high free space i.e. more than + * StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH of total space. + */ + @Test + public void testGetStorageCacheBytesUnderHighStorage() throws Exception { + when(mFile.getUsableSpace()).thenReturn(10000L); + when(mFile.getTotalSpace()).thenReturn(15000L); + long result = mSm.getStorageCacheBytes(mFile, 0); + assertThat(result).isEqualTo(1500L); + } + + /** + * Tests the space reserved for cache when system has low free space i.e. less than + * StorageManager.STORAGE_THRESHOLD_PERCENT_LOW of total space. + */ + @Test + public void testGetStorageCacheBytesUnderLowStorage() throws Exception { + when(mFile.getUsableSpace()).thenReturn(10000L); + when(mFile.getTotalSpace()).thenReturn(250000L); + long result = mSm.getStorageCacheBytes(mFile, 0); + assertThat(result).isEqualTo(5000L); + } + + /** + * Tests the space reserved for cache when system has moderate free space i.e.more than + * StorageManager.STORAGE_THRESHOLD_PERCENT_LOW of total space but less than + * StorageManager.STORAGE_THRESHOLD_PERCENT_HIGH of total space. + */ + @Test + public void testGetStorageCacheBytesUnderModerateStorage() throws Exception { + when(mFile.getUsableSpace()).thenReturn(10000L); + when(mFile.getTotalSpace()).thenReturn(100000L); + long result = mSm.getStorageCacheBytes(mFile, 0); + assertThat(result).isEqualTo(4666L); + } + + /** * Creates an OBB file (with the given name), into the app's standard files directory * * @param name The name of the OBB file we want to create/write to @@ -526,4 +573,4 @@ public class StorageManagerBaseTest extends InstrumentationTestCase { doValidateIntContents(path + File.separator + "subdir2" + File.separator + "subdir2a" + File.separator + "subdir2a1", "OneToOneThousandInts", 0, 1000); } -}
\ No newline at end of file +} |