1 /*
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.cts.storagestatsapp;
18 
19 import static android.os.storage.StorageManager.UUID_DEFAULT;
20 
21 import static com.android.cts.storageapp.Utils.CACHE_ALL;
22 import static com.android.cts.storageapp.Utils.CACHE_EXT;
23 import static com.android.cts.storageapp.Utils.CODE_ALL;
24 import static com.android.cts.storageapp.Utils.DATA_ALL;
25 import static com.android.cts.storageapp.Utils.MB_IN_BYTES;
26 import static com.android.cts.storageapp.Utils.PKG_A;
27 import static com.android.cts.storageapp.Utils.PKG_B;
28 import static com.android.cts.storageapp.Utils.PKG_C;
29 import static com.android.cts.storageapp.Utils.REF_PROFILES_BASE_DIR;
30 import static com.android.cts.storageapp.Utils.CUR_PROFILES_BASE_DIR;
31 import static com.android.cts.storageapp.Utils.PROFILE_FILE_NAME;
32 import static com.android.cts.storageapp.Utils.TAG;
33 import static com.android.cts.storageapp.Utils.assertAtLeast;
34 import static com.android.cts.storageapp.Utils.assertMostlyEquals;
35 import static com.android.cts.storageapp.Utils.getSizeManual;
36 import static com.android.cts.storageapp.Utils.logCommand;
37 import static com.android.cts.storageapp.Utils.makeUniqueFile;
38 import static com.android.cts.storageapp.Utils.useFallocate;
39 import static com.android.cts.storageapp.Utils.useSpace;
40 import static com.android.cts.storageapp.Utils.useWrite;
41 
42 import static org.junit.Assert.assertEquals;
43 import static org.junit.Assume.assumeTrue;
44 
45 import org.junit.Before;
46 import org.junit.Rule;
47 
48 import android.app.Activity;
49 import android.app.usage.ExternalStorageStats;
50 import android.app.usage.Flags;
51 import android.app.usage.StorageStats;
52 import android.app.usage.StorageStatsManager;
53 import android.content.BroadcastReceiver;
54 import android.content.ComponentName;
55 import android.content.ContentProviderClient;
56 import android.content.Context;
57 import android.content.Intent;
58 import android.content.pm.ApplicationInfo;
59 import android.content.pm.PackageManager;
60 import android.os.Build;
61 import android.os.Bundle;
62 import android.os.Environment;
63 import android.os.SystemProperties;
64 import android.os.UserHandle;
65 import android.os.storage.StorageManager;
66 import android.platform.test.annotations.RequiresFlagsEnabled;
67 import android.platform.test.flag.junit.CheckFlagsRule;
68 import android.platform.test.flag.junit.DeviceFlagsValueProvider;
69 import android.provider.DeviceConfig;
70 import android.provider.MediaStore;
71 import android.test.InstrumentationTestCase;
72 import android.util.Log;
73 import android.util.MutableLong;
74 
75 import androidx.test.uiautomator.UiDevice;
76 
77 import com.android.compatibility.common.util.SystemUtil;
78 import com.android.cts.storageapp.UtilsReceiver;
79 
80 import junit.framework.AssertionFailedError;
81 
82 import java.io.File;
83 import java.util.UUID;
84 import java.util.concurrent.CountDownLatch;
85 import java.util.concurrent.TimeUnit;
86 
87 /**
88  * Tests to verify {@link StorageStatsManager} behavior.
89  */
90 public class StorageStatsTest extends InstrumentationTestCase {
91     private PackageManager pm;
92     private StorageStatsManager stats;
93     private UserHandle user;
94 
95     @SuppressWarnings("JUnit4ClassUsedInJUnit3")
96     @Rule
97     public final CheckFlagsRule mCheckFlagsRule =
98             DeviceFlagsValueProvider.createCheckFlagsRule();
99 
100     @Before
setUp()101     public void setUp() {
102         pm = getContext().getPackageManager();
103         stats = getContext().getSystemService(StorageStatsManager.class);
104         user = android.os.Process.myUserHandle();
105     }
getContext()106     private Context getContext() {
107         return getInstrumentation().getContext();
108     }
109 
110     /**
111      * Require that quota support be fully enabled on devices that first ship
112      * with P. This test verifies that both kernel options and the fstab 'quota'
113      * option are enabled.
114      */
testVerify()115     public void testVerify() throws Exception {
116         if (Build.VERSION.DEVICE_INITIAL_SDK_INT >= Build.VERSION_CODES.P) {
117             final StorageStatsManager stats = getContext()
118                     .getSystemService(StorageStatsManager.class);
119             assertTrue("Devices that first ship with P or newer must enable quotas to "
120                     + "support StorageStatsManager APIs. You may need to enable the "
121                     + "CONFIG_QUOTA, CONFIG_QFMT_V2, CONFIG_QUOTACTL kernel options "
122                     + "and add the 'quota' fstab option on /data.",
123                     stats.isQuotaSupported(UUID_DEFAULT));
124             assertTrue("Devices that first ship with P or newer must enable resgid to "
125                     + "preserve system stability in the face of abusive apps.",
126                     stats.isReservedSupported(UUID_DEFAULT));
127         }
128     }
129 
testVerifySummary()130     public void testVerifySummary() throws Exception {
131         final StorageStatsManager stats = getContext().getSystemService(StorageStatsManager.class);
132 
133         final long actualTotal = stats.getTotalBytes(UUID_DEFAULT);
134         final long expectedTotal = Environment.getDataDirectory().getTotalSpace();
135         assertAtLeast(expectedTotal, actualTotal);
136 
137         final long actualFree = stats.getFreeBytes(UUID_DEFAULT);
138         final long expectedFree = Environment.getDataDirectory().getUsableSpace();
139         assertAtLeast(expectedFree, actualFree);
140     }
141 
testVerifyStats()142     public void testVerifyStats() throws Exception {
143         final StorageStatsManager stats = getContext().getSystemService(StorageStatsManager.class);
144         final int uid = android.os.Process.myUid();
145         final UserHandle user = UserHandle.getUserHandleForUid(uid);
146 
147         final StorageStats beforeApp = stats.queryStatsForUid(UUID_DEFAULT, uid);
148         final StorageStats beforeUser = stats.queryStatsForUser(UUID_DEFAULT, user);
149 
150         useSpace(getContext());
151 
152         final StorageStats afterApp = stats.queryStatsForUid(UUID_DEFAULT, uid);
153         final StorageStats afterUser = stats.queryStatsForUser(UUID_DEFAULT, user);
154 
155         final long deltaCode = CODE_ALL;
156         assertMostlyEquals(deltaCode, afterApp.getAppBytes() - beforeApp.getAppBytes());
157         assertMostlyEquals(deltaCode, afterUser.getAppBytes() - beforeUser.getAppBytes());
158 
159         final long deltaData = DATA_ALL;
160         assertMostlyEquals(deltaData, afterApp.getDataBytes() - beforeApp.getDataBytes());
161         assertMostlyEquals(deltaData, afterUser.getDataBytes() - beforeUser.getDataBytes());
162 
163         final long deltaCache = CACHE_ALL;
164         assertMostlyEquals(deltaCache, afterApp.getCacheBytes() - beforeApp.getCacheBytes());
165         assertMostlyEquals(deltaCache, afterUser.getCacheBytes() - beforeUser.getCacheBytes());
166 
167         final long deltaExternalCache = CACHE_EXT;
168         assertMostlyEquals(deltaExternalCache,
169                 afterApp.getExternalCacheBytes() - beforeApp.getExternalCacheBytes());
170         assertMostlyEquals(deltaExternalCache,
171                 afterUser.getExternalCacheBytes() - beforeUser.getExternalCacheBytes());
172     }
173 
174     @RequiresFlagsEnabled(Flags.FLAG_GET_APP_BYTES_BY_DATA_TYPE_API)
testVerifyStatsByDataType()175     public void testVerifyStatsByDataType() throws Exception {
176         final ApplicationInfo appInfo = pm.getApplicationInfo(PKG_C, 0);
177         useSpace(getContext());
178 
179         String appSrcPath = appInfo.sourceDir;
180         File appSrcDir = new File(appInfo.sourceDir);
181         // sourceDir could return the base.apk with path, in that case, use the parent directory.
182         if (appSrcDir.isFile()) {
183             appSrcDir = appSrcDir.getParentFile();
184         }
185 
186         final StorageStats as = stats.queryStatsForPackage(UUID_DEFAULT, PKG_C, user);
187 
188         long apkSize = getSizeOfFilesEndWith(appSrcDir, ".apk");
189         assertEquals(apkSize, as.getAppBytesByDataType(StorageStats.APP_DATA_TYPE_FILE_TYPE_APK));
190 
191         long dmSize = getSizeOfFilesEndWith(appSrcDir, ".dm");
192         assertEquals(dmSize, as.getAppBytesByDataType(StorageStats.APP_DATA_TYPE_FILE_TYPE_DM));
193 
194         long libSize = getSizeOfDir(new File(appSrcPath + "/lib/"));
195         assertEquals(libSize, as.getAppBytesByDataType(StorageStats.APP_DATA_TYPE_LIB));
196 
197         // Check the profile sizes if they are fetched by ArtManagedFileStats.
198         long curProfileBytes =
199             as.getAppBytesByDataType(StorageStats.APP_DATA_TYPE_FILE_TYPE_CURRENT_PROFILE);
200         File curProfile = new File(new File(CUR_PROFILES_BASE_DIR + appInfo.uid + "/", PKG_C),
201             PROFILE_FILE_NAME);
202         assertEquals(curProfile.length(), curProfileBytes);
203 
204         long refProfileBytes =
205             as.getAppBytesByDataType(StorageStats.APP_DATA_TYPE_FILE_TYPE_REFERENCE_PROFILE);
206         assertTrue(refProfileBytes > 0);
207     }
208 
testVerifyStatsMultiple()209     public void testVerifyStatsMultiple() throws Exception {
210         final ApplicationInfo a = pm.getApplicationInfo(PKG_A, 0);
211         final ApplicationInfo b = pm.getApplicationInfo(PKG_B, 0);
212 
213         final StorageStats as = stats.queryStatsForUid(UUID_DEFAULT, a.uid);
214         final StorageStats bs = stats.queryStatsForUid(UUID_DEFAULT, b.uid);
215 
216         assertMostlyEquals(DATA_ALL * 2, as.getDataBytes());
217         assertMostlyEquals(CACHE_ALL * 2, as.getCacheBytes());
218 
219         assertMostlyEquals(DATA_ALL, bs.getDataBytes());
220         assertMostlyEquals(CACHE_ALL, bs.getCacheBytes());
221 
222         // Since OBB storage space may be shared or isolated between users,
223         // we'll accept either expected or double usage.
224         try {
225             assertMostlyEquals(CODE_ALL * 2, as.getAppBytes(), 5 * MB_IN_BYTES);
226             assertMostlyEquals(CODE_ALL * 1, bs.getAppBytes(), 5 * MB_IN_BYTES);
227         } catch (AssertionFailedError e) {
228             assertMostlyEquals(CODE_ALL * 4, as.getAppBytes(), 5 * MB_IN_BYTES);
229             assertMostlyEquals(CODE_ALL * 2, bs.getAppBytes(), 5 * MB_IN_BYTES);
230         }
231     }
232 
233     /**
234      * Create some external files of specific media types and ensure that
235      * they're tracked correctly.
236      */
testVerifyStatsExternal()237     public void testVerifyStatsExternal() throws Exception {
238         final StorageStatsManager stats = getContext().getSystemService(StorageStatsManager.class);
239         final int uid = android.os.Process.myUid();
240         final UserHandle user = UserHandle.getUserHandleForUid(uid);
241 
242         final ExternalStorageStats before = stats.queryExternalStatsForUser(UUID_DEFAULT, user);
243 
244         final File dir = Environment.getExternalStorageDirectory();
245         final File downloadsDir = Environment.getExternalStoragePublicDirectory(
246                 Environment.DIRECTORY_DOWNLOADS);
247         downloadsDir.mkdirs();
248 
249         final File image = new File(dir, System.nanoTime() + ".jpg");
250         final File video = new File(downloadsDir, System.nanoTime() + ".MP4");
251         final File audio = new File(dir, System.nanoTime() + ".png.WaV");
252         final File internal = new File(
253                 getContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES), "test.jpg");
254 
255         useWrite(image, 2 * MB_IN_BYTES);
256         useWrite(video, 3 * MB_IN_BYTES);
257         useWrite(audio, 5 * MB_IN_BYTES);
258         useWrite(internal, 7 * MB_IN_BYTES);
259 
260         final ExternalStorageStats afterInit = stats.queryExternalStatsForUser(UUID_DEFAULT, user);
261 
262         assertMostlyEquals(17 * MB_IN_BYTES, afterInit.getTotalBytes() - before.getTotalBytes());
263         assertMostlyEquals(5 * MB_IN_BYTES, afterInit.getAudioBytes() - before.getAudioBytes());
264         assertMostlyEquals(3 * MB_IN_BYTES, afterInit.getVideoBytes() - before.getVideoBytes());
265         assertMostlyEquals(2 * MB_IN_BYTES, afterInit.getImageBytes() - before.getImageBytes());
266         assertMostlyEquals(7 * MB_IN_BYTES, afterInit.getAppBytes() - before.getAppBytes());
267 
268         // Rename to ensure that stats are updated
269         video.renameTo(new File(dir, System.nanoTime() + ".PnG"));
270 
271         // Since we have MANAGE_EXTERNAL_STORAGE, need to ask for a re-scan
272         MediaStore.scanFile(getContext().getContentResolver(), dir);
273         MediaStore.scanFile(getContext().getContentResolver(), downloadsDir);
274         MediaStore.waitForIdle(getContext().getContentResolver());
275 
276         final ExternalStorageStats afterRename = stats.queryExternalStatsForUser(UUID_DEFAULT, user);
277 
278         assertMostlyEquals(17 * MB_IN_BYTES, afterRename.getTotalBytes() - before.getTotalBytes());
279         assertMostlyEquals(5 * MB_IN_BYTES, afterRename.getAudioBytes() - before.getAudioBytes());
280         assertMostlyEquals(0 * MB_IN_BYTES, afterRename.getVideoBytes() - before.getVideoBytes());
281         assertMostlyEquals(5 * MB_IN_BYTES, afterRename.getImageBytes() - before.getImageBytes());
282         assertMostlyEquals(7 * MB_IN_BYTES, afterRename.getAppBytes() - before.getAppBytes());
283     }
284 
285     /**
286      * Measuring external storage manually should always be consistent with
287      * whatever the stats APIs are returning.
288      */
testVerifyStatsExternalConsistent()289     public void testVerifyStatsExternalConsistent() throws Exception {
290         final StorageStatsManager stats = getContext().getSystemService(StorageStatsManager.class);
291         final UserHandle user = android.os.Process.myUserHandle();
292 
293         // Since scoped storage, apps can't access the package-specific Android/
294         // directories anymore. So we compute the current size, and assume the
295         // delta is in Android/*, which unfortunately may have data from
296         // test APKs that aren't cleaned up properly.
297         //
298         // Then, when we compute the new size and compare it with the stats,
299         // we expect the same delta
300         final long manualSizeBefore = getSizeManual(
301                 Environment.getExternalStorageDirectory(), true);
302         final long statsSizeBefore = stats.queryExternalStatsForUser(
303                 UUID_DEFAULT, user).getTotalBytes();
304 
305         final long deltaBefore = statsSizeBefore - manualSizeBefore;
306 
307         useSpace(getContext());
308 
309         final File top = Environment.getExternalStorageDirectory();
310         final File pics = Environment
311                 .getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
312         pics.mkdirs();
313 
314         useWrite(makeUniqueFile(top), 5 * MB_IN_BYTES);
315         useWrite(makeUniqueFile(pics), 5 * MB_IN_BYTES);
316         useWrite(makeUniqueFile(pics), 5 * MB_IN_BYTES);
317 
318         // for fuse file system
319         Thread.sleep(10000);
320 
321         // TODO: remove this once 34723223 is fixed
322         logCommand("sync");
323 
324         long manualSize = getSizeManual(Environment.getExternalStorageDirectory(), true);
325         // Since scoped storage, we can't walk the Android/ tree anymore; so pass in this
326         // app's files and cache dirs directly.
327         manualSize += getSizeManual(getContext().getExternalFilesDir(null), true);
328         manualSize += getSizeManual(getContext().getExternalCacheDir(), true);
329         final long statsSize = stats.queryExternalStatsForUser(UUID_DEFAULT, user).getTotalBytes();
330 
331         final long deltaAfter = statsSize - manualSize;
332         assertMostlyEquals(deltaBefore, deltaAfter);
333     }
334 
testVerifyCategory()335     public void testVerifyCategory() throws Exception {
336         final ApplicationInfo a = pm.getApplicationInfo(PKG_A, 0);
337         final ApplicationInfo b = pm.getApplicationInfo(PKG_B, 0);
338 
339         assertEquals(ApplicationInfo.CATEGORY_VIDEO, a.category);
340         assertEquals(ApplicationInfo.CATEGORY_UNDEFINED, b.category);
341     }
342 
testCacheClearing()343     public void testCacheClearing() throws Exception {
344         final int[] originalCacheReservePercents = new int[2];
345         setCacheReservePercentsToZero(originalCacheReservePercents);
346 
347         try {
348             testCacheClearing(originalCacheReservePercents);
349         } finally {
350             resetCacheReservePercents(originalCacheReservePercents);
351         }
352     }
353 
testCacheBehavior()354     public void testCacheBehavior() throws Exception {
355         final int[] originalCacheReservePercents = new int[2];
356         setCacheReservePercentsToZero(originalCacheReservePercents);
357 
358         try {
359             testCacheBehavior(originalCacheReservePercents);
360         } finally {
361             resetCacheReservePercents(originalCacheReservePercents);
362         }
363     }
364 
testCacheClearing(int[] originalCacheReservePercents)365     private void testCacheClearing(int[] originalCacheReservePercents) throws Exception {
366         final Context context = getContext();
367         final StorageManager sm = context.getSystemService(StorageManager.class);
368         final StorageStatsManager stats = context.getSystemService(StorageStatsManager.class);
369         final UserHandle user = android.os.Process.myUserHandle();
370 
371         final File filesDir = context.getFilesDir();
372         final UUID filesUuid = sm.getUuidForPath(filesDir);
373         final String pmUuid = filesUuid.equals(StorageManager.UUID_DEFAULT) ? "internal"
374                 : filesUuid.toString();
375 
376         final long beforeAllocatable = sm.getAllocatableBytes(filesUuid);
377         final long beforeFree = stats.getFreeBytes(filesUuid);
378         final long beforeRaw = filesDir.getUsableSpace();
379 
380         Log.d(TAG, "Before raw " + beforeRaw + ", free " + beforeFree + ", allocatable "
381                 + beforeAllocatable);
382 
383         assertMostlyEquals(0, getCacheBytes(PKG_A, user));
384         assertMostlyEquals(0, getCacheBytes(PKG_B, user));
385 
386         // Ask apps to allocate some cached data
387         final long targetA = doAllocateProvider(PKG_A, 0.5, 1262304000);
388         final long targetB = doAllocateProvider(PKG_B, 2.0, 1420070400);
389         final long totalAllocated = targetA + targetB;
390 
391         MediaStore.waitForIdle(getContext().getContentResolver());
392 
393         // Apps using up some cache space shouldn't change how much we can
394         // allocate, or how much we think is free; but it should decrease real
395         // disk space.
396         if (stats.isQuotaSupported(filesUuid)) {
397             assertMostlyEquals(beforeAllocatable,
398                     sm.getAllocatableBytes(filesUuid), 10 * MB_IN_BYTES);
399             assertMostlyEquals(beforeFree,
400                     stats.getFreeBytes(filesUuid), 10 * MB_IN_BYTES);
401         } else {
402             assertMostlyEquals(beforeAllocatable - totalAllocated,
403                     sm.getAllocatableBytes(filesUuid), 10 * MB_IN_BYTES);
404             assertMostlyEquals(beforeFree - totalAllocated,
405                     stats.getFreeBytes(filesUuid), 10 * MB_IN_BYTES);
406         }
407         assertMostlyEquals(beforeRaw - totalAllocated,
408                 filesDir.getUsableSpace(), 10 * MB_IN_BYTES);
409 
410         assertMostlyEquals(targetA, getCacheBytes(PKG_A, user));
411         assertMostlyEquals(targetB, getCacheBytes(PKG_B, user));
412 
413         // Allocate some space for ourselves, which should trim away at
414         // over-quota app first, even though its files are newer.
415         final long clear1 = filesDir.getUsableSpace() + (targetB / 2);
416         if (stats.isQuotaSupported(filesUuid)) {
417             sm.allocateBytes(filesUuid, clear1);
418         } else {
419             UiDevice.getInstance(getInstrumentation())
420                     .executeShellCommand("pm trim-caches " + clear1 + " " + pmUuid);
421         }
422 
423         assertMostlyEquals(targetA, getCacheBytes(PKG_A, user));
424         assertMostlyEquals(targetB / 2, getCacheBytes(PKG_B, user), 2 * MB_IN_BYTES);
425 
426         // Allocate some more space for ourselves, which should now start
427         // trimming away at older app. Since we pivot between the two apps once
428         // they're tied for cache ratios, we expect to clear about half of the
429         // remaining space from each of them.
430         final long clear2 = filesDir.getUsableSpace() + (targetB / 2);
431         if (stats.isQuotaSupported(filesUuid)) {
432             sm.allocateBytes(filesUuid, clear2);
433         } else {
434             UiDevice.getInstance(getInstrumentation())
435                     .executeShellCommand("pm trim-caches " + clear2 + " " + pmUuid);
436         }
437 
438         assertMostlyEquals(targetA / 2, getCacheBytes(PKG_A, user), 2 * MB_IN_BYTES);
439         assertMostlyEquals(targetA / 2, getCacheBytes(PKG_B, user), 2 * MB_IN_BYTES);
440     }
441 
testCacheBehavior(int[] originalCacheReservePercents)442     private void testCacheBehavior(int[] originalCacheReservePercents) throws Exception {
443         final Context context = getContext();
444         final StorageManager sm = context.getSystemService(StorageManager.class);
445         final StorageStatsManager stats = context.getSystemService(StorageStatsManager.class);
446 
447         final UUID filesUuid = sm.getUuidForPath(context.getFilesDir());
448         final String pmUuid = filesUuid.equals(StorageManager.UUID_DEFAULT) ? "internal"
449                 : filesUuid.toString();
450 
451         final File normal = new File(context.getCacheDir(), "normal");
452         final File group = new File(context.getCacheDir(), "group");
453         final File tomb = new File(context.getCacheDir(), "tomb");
454 
455         final long size = 2 * MB_IN_BYTES;
456 
457         final long normalTime = 1262304000;
458         final long groupTime = 1262303000;
459         final long tombTime = 1262302000;
460 
461         normal.mkdir();
462         group.mkdir();
463         tomb.mkdir();
464 
465         sm.setCacheBehaviorGroup(group, true);
466         sm.setCacheBehaviorTombstone(tomb, true);
467 
468         final File a = useFallocate(makeUniqueFile(normal), size, normalTime);
469         final File b = useFallocate(makeUniqueFile(normal), size, normalTime);
470         final File c = useFallocate(makeUniqueFile(normal), size, normalTime);
471 
472         final File d = useFallocate(makeUniqueFile(group), size, groupTime);
473         final File e = useFallocate(makeUniqueFile(group), size, groupTime);
474         final File f = useFallocate(makeUniqueFile(group), size, groupTime);
475 
476         final File g = useFallocate(makeUniqueFile(tomb), size, tombTime);
477         final File h = useFallocate(makeUniqueFile(tomb), size, tombTime);
478         final File i = useFallocate(makeUniqueFile(tomb), size, tombTime);
479 
480         normal.setLastModified(normalTime);
481         group.setLastModified(groupTime);
482         tomb.setLastModified(tombTime);
483 
484         final long clear1 = group.getUsableSpace() + (8 * MB_IN_BYTES);
485         if (stats.isQuotaSupported(filesUuid)) {
486             sm.allocateBytes(filesUuid, clear1);
487         } else {
488             UiDevice.getInstance(getInstrumentation())
489                     .executeShellCommand("pm trim-caches " + clear1 + " " + pmUuid);
490         }
491 
492         assertTrue(a.exists());
493         assertTrue(b.exists());
494         assertTrue(c.exists());
495         assertFalse(group.exists());
496         assertFalse(d.exists());
497         assertFalse(e.exists());
498         assertFalse(f.exists());
499         assertTrue(g.exists()); assertEquals(0, g.length());
500         assertTrue(h.exists()); assertEquals(0, h.length());
501         assertTrue(i.exists()); assertEquals(0, i.length());
502     }
503 
504     /* originalCacheReservePercents is an array of size 2 with CacheReservePercentHigh
505     *  at index 0 and CacheReservePercentLow at index 1.
506     */
setCacheReservePercentsToZero(int[] originalCacheReservePercents)507     private void setCacheReservePercentsToZero(int[] originalCacheReservePercents) {
508         SystemUtil.runWithShellPermissionIdentity(() -> {
509             originalCacheReservePercents[0] = DeviceConfig.getInt(
510                     DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
511                     StorageManager.CACHE_RESERVE_PERCENT_HIGH_KEY, -1);
512             DeviceConfig.setProperty(
513                     DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
514                     StorageManager.CACHE_RESERVE_PERCENT_HIGH_KEY,
515                     Integer.toString(0), /* makeDefault */ false);
516             originalCacheReservePercents[1] = DeviceConfig.getInt(
517                     DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
518                     StorageManager.CACHE_RESERVE_PERCENT_LOW_KEY, -1);
519             DeviceConfig.setProperty(
520                     DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
521                     StorageManager.CACHE_RESERVE_PERCENT_LOW_KEY,
522                     Integer.toString(0), /* makeDefault */ false);
523         });
524     }
525 
resetCacheReservePercents(int[] originalCacheReservePercents)526     private void resetCacheReservePercents(int[] originalCacheReservePercents) {
527         SystemUtil.runWithShellPermissionIdentity(() -> {
528             DeviceConfig.setProperty(
529                     DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
530                     StorageManager.CACHE_RESERVE_PERCENT_HIGH_KEY,
531                     (originalCacheReservePercents[0] != -1)
532                         ? Integer.toString(originalCacheReservePercents[0]) : null,
533                     /* makeDefault */ false);
534             DeviceConfig.setProperty(
535                     DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
536                     StorageManager.CACHE_RESERVE_PERCENT_LOW_KEY,
537                     (originalCacheReservePercents[1] != -1)
538                         ? Integer.toString(originalCacheReservePercents[1]) : null,
539                     /* makeDefault */ false);
540         });
541     }
542 
getCacheBytes(String pkg, UserHandle user)543     private long getCacheBytes(String pkg, UserHandle user) throws Exception {
544         return getContext().getSystemService(StorageStatsManager.class)
545                 .queryStatsForPackage(UUID_DEFAULT, pkg, user).getCacheBytes();
546     }
547 
doAllocateReceiver(String pkg, double fraction, long time)548     private long doAllocateReceiver(String pkg, double fraction, long time) throws Exception {
549         final CountDownLatch latch = new CountDownLatch(1);
550         final Intent intent = new Intent();
551         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
552         intent.setComponent(new ComponentName(pkg, UtilsReceiver.class.getName()));
553         intent.putExtra(UtilsReceiver.EXTRA_FRACTION, fraction);
554         intent.putExtra(UtilsReceiver.EXTRA_TIME, time);
555         final MutableLong bytes = new MutableLong(0);
556         getInstrumentation().getTargetContext().sendOrderedBroadcast(intent, null,
557                 new BroadcastReceiver() {
558                     @Override
559                     public void onReceive(Context context, Intent intent) {
560                         bytes.value = getResultExtras(false).getLong(UtilsReceiver.EXTRA_BYTES);
561                         latch.countDown();
562                     }
563                 }, null, Activity.RESULT_CANCELED, null, null);
564         latch.await(30, TimeUnit.SECONDS);
565         return bytes.value;
566     }
567 
doAllocateProvider(String pkg, double fraction, long time)568     private long doAllocateProvider(String pkg, double fraction, long time) throws Exception {
569         final Bundle args = new Bundle();
570         args.putDouble(UtilsReceiver.EXTRA_FRACTION, fraction);
571         args.putLong(UtilsReceiver.EXTRA_TIME, time);
572 
573         try (final ContentProviderClient client = getContext().getContentResolver()
574                 .acquireContentProviderClient(pkg)) {
575             final Bundle res = client.call(pkg, pkg, args);
576             return res.getLong(UtilsReceiver.EXTRA_BYTES);
577         }
578     }
579 
getSizeOfFilesEndWith(File dir, String suffix)580     private long getSizeOfFilesEndWith(File dir, String suffix) {
581         if (!dir.isDirectory()) {
582             return 0;
583         }
584 
585         long size = 0;
586         try {
587             for (File file : dir.listFiles()) {
588                 if (file.isFile() && file.getName().endsWith(suffix)) {
589                     size += file.length();
590                 }
591             }
592         } catch (NullPointerException e) {
593             size += 0;
594         }
595 
596         return size;
597     }
598 
getSizeOfDir(File dir)599     private long getSizeOfDir(File dir) {
600         if (!dir.isDirectory()) {
601             return 0;
602         }
603 
604         long size = 0;
605         try {
606             for (File file : dir.listFiles()) {
607                 if (file.isFile()) {
608                     size += file.length();
609                 } else if (file.isDirectory()) {
610                     size += getSizeOfDir(file);
611                 }
612             }
613         } catch (NullPointerException e) {
614             size += 0;
615         }
616 
617         return size;
618     }
619 }
620