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         // Difference caused by other operations when writing to cache
394         // The longer the writing time, the greater the difference
395         final long difference = Math.max(10 * MB_IN_BYTES, totalAllocated / 100);
396 
397         // Apps using up some cache space shouldn't change how much we can
398         // allocate, or how much we think is free; but it should decrease real
399         // disk space.
400         if (stats.isQuotaSupported(filesUuid)) {
401             assertMostlyEquals(beforeAllocatable,
402                     sm.getAllocatableBytes(filesUuid), difference);
403             assertMostlyEquals(beforeFree,
404                     stats.getFreeBytes(filesUuid), difference);
405         } else {
406             assertMostlyEquals(beforeAllocatable - totalAllocated,
407                     sm.getAllocatableBytes(filesUuid), difference);
408             assertMostlyEquals(beforeFree - totalAllocated,
409                     stats.getFreeBytes(filesUuid), difference);
410         }
411         assertMostlyEquals(beforeRaw - totalAllocated,
412                 filesDir.getUsableSpace(), difference);
413 
414         assertMostlyEquals(targetA, getCacheBytes(PKG_A, user), 2 * MB_IN_BYTES);
415         assertMostlyEquals(targetB, getCacheBytes(PKG_B, user), 2 * MB_IN_BYTES);
416 
417         // Allocate some space for ourselves, which should trim away at
418         // over-quota app first, even though its files are newer.
419         final long clear1 = filesDir.getUsableSpace() + (targetB / 2);
420         if (stats.isQuotaSupported(filesUuid)) {
421             sm.allocateBytes(filesUuid, clear1);
422         } else {
423             UiDevice.getInstance(getInstrumentation())
424                     .executeShellCommand("pm trim-caches " + clear1 + " " + pmUuid);
425         }
426 
427         assertMostlyEquals(targetA, getCacheBytes(PKG_A, user), 2 * MB_IN_BYTES);
428         assertMostlyEquals(targetB / 2, getCacheBytes(PKG_B, user), 2 * MB_IN_BYTES);
429 
430         // Allocate some more space for ourselves, which should now start
431         // trimming away at older app. Since we pivot between the two apps once
432         // they're tied for cache ratios, we expect to clear about half of the
433         // remaining space from each of them.
434         final long clear2 = filesDir.getUsableSpace() + (targetB / 2);
435         if (stats.isQuotaSupported(filesUuid)) {
436             sm.allocateBytes(filesUuid, clear2);
437         } else {
438             UiDevice.getInstance(getInstrumentation())
439                     .executeShellCommand("pm trim-caches " + clear2 + " " + pmUuid);
440         }
441 
442         assertMostlyEquals(targetA / 2, getCacheBytes(PKG_A, user), 2 * MB_IN_BYTES);
443         assertMostlyEquals(targetA / 2, getCacheBytes(PKG_B, user), 2 * MB_IN_BYTES);
444     }
445 
testCacheBehavior(int[] originalCacheReservePercents)446     private void testCacheBehavior(int[] originalCacheReservePercents) throws Exception {
447         final Context context = getContext();
448         final StorageManager sm = context.getSystemService(StorageManager.class);
449         final StorageStatsManager stats = context.getSystemService(StorageStatsManager.class);
450 
451         final UUID filesUuid = sm.getUuidForPath(context.getFilesDir());
452         final String pmUuid = filesUuid.equals(StorageManager.UUID_DEFAULT) ? "internal"
453                 : filesUuid.toString();
454 
455         final File normal = new File(context.getCacheDir(), "normal");
456         final File group = new File(context.getCacheDir(), "group");
457         final File tomb = new File(context.getCacheDir(), "tomb");
458 
459         final long size = 2 * MB_IN_BYTES;
460 
461         final long normalTime = 1262304000;
462         final long groupTime = 1262303000;
463         final long tombTime = 1262302000;
464 
465         normal.mkdir();
466         group.mkdir();
467         tomb.mkdir();
468 
469         sm.setCacheBehaviorGroup(group, true);
470         sm.setCacheBehaviorTombstone(tomb, true);
471 
472         final File a = useFallocate(makeUniqueFile(normal), size, normalTime);
473         final File b = useFallocate(makeUniqueFile(normal), size, normalTime);
474         final File c = useFallocate(makeUniqueFile(normal), size, normalTime);
475 
476         final File d = useFallocate(makeUniqueFile(group), size, groupTime);
477         final File e = useFallocate(makeUniqueFile(group), size, groupTime);
478         final File f = useFallocate(makeUniqueFile(group), size, groupTime);
479 
480         final File g = useFallocate(makeUniqueFile(tomb), size, tombTime);
481         final File h = useFallocate(makeUniqueFile(tomb), size, tombTime);
482         final File i = useFallocate(makeUniqueFile(tomb), size, tombTime);
483 
484         normal.setLastModified(normalTime);
485         group.setLastModified(groupTime);
486         tomb.setLastModified(tombTime);
487 
488         final long clear1 = group.getUsableSpace() + (8 * MB_IN_BYTES);
489         if (stats.isQuotaSupported(filesUuid)) {
490             sm.allocateBytes(filesUuid, clear1);
491         } else {
492             UiDevice.getInstance(getInstrumentation())
493                     .executeShellCommand("pm trim-caches " + clear1 + " " + pmUuid);
494         }
495 
496         assertTrue(a.exists());
497         assertTrue(b.exists());
498         assertTrue(c.exists());
499         assertFalse(group.exists());
500         assertFalse(d.exists());
501         assertFalse(e.exists());
502         assertFalse(f.exists());
503         assertTrue(g.exists()); assertEquals(0, g.length());
504         assertTrue(h.exists()); assertEquals(0, h.length());
505         assertTrue(i.exists()); assertEquals(0, i.length());
506     }
507 
508     /* originalCacheReservePercents is an array of size 2 with CacheReservePercentHigh
509     *  at index 0 and CacheReservePercentLow at index 1.
510     */
setCacheReservePercentsToZero(int[] originalCacheReservePercents)511     private void setCacheReservePercentsToZero(int[] originalCacheReservePercents) {
512         SystemUtil.runWithShellPermissionIdentity(() -> {
513             originalCacheReservePercents[0] = DeviceConfig.getInt(
514                     DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
515                     StorageManager.CACHE_RESERVE_PERCENT_HIGH_KEY, -1);
516             DeviceConfig.setProperty(
517                     DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
518                     StorageManager.CACHE_RESERVE_PERCENT_HIGH_KEY,
519                     Integer.toString(0), /* makeDefault */ false);
520             originalCacheReservePercents[1] = DeviceConfig.getInt(
521                     DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
522                     StorageManager.CACHE_RESERVE_PERCENT_LOW_KEY, -1);
523             DeviceConfig.setProperty(
524                     DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
525                     StorageManager.CACHE_RESERVE_PERCENT_LOW_KEY,
526                     Integer.toString(0), /* makeDefault */ false);
527         });
528     }
529 
resetCacheReservePercents(int[] originalCacheReservePercents)530     private void resetCacheReservePercents(int[] originalCacheReservePercents) {
531         SystemUtil.runWithShellPermissionIdentity(() -> {
532             DeviceConfig.setProperty(
533                     DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
534                     StorageManager.CACHE_RESERVE_PERCENT_HIGH_KEY,
535                     (originalCacheReservePercents[0] != -1)
536                         ? Integer.toString(originalCacheReservePercents[0]) : null,
537                     /* makeDefault */ false);
538             DeviceConfig.setProperty(
539                     DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT,
540                     StorageManager.CACHE_RESERVE_PERCENT_LOW_KEY,
541                     (originalCacheReservePercents[1] != -1)
542                         ? Integer.toString(originalCacheReservePercents[1]) : null,
543                     /* makeDefault */ false);
544         });
545     }
546 
getCacheBytes(String pkg, UserHandle user)547     private long getCacheBytes(String pkg, UserHandle user) throws Exception {
548         return getContext().getSystemService(StorageStatsManager.class)
549                 .queryStatsForPackage(UUID_DEFAULT, pkg, user).getCacheBytes();
550     }
551 
doAllocateReceiver(String pkg, double fraction, long time)552     private long doAllocateReceiver(String pkg, double fraction, long time) throws Exception {
553         final CountDownLatch latch = new CountDownLatch(1);
554         final Intent intent = new Intent();
555         intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
556         intent.setComponent(new ComponentName(pkg, UtilsReceiver.class.getName()));
557         intent.putExtra(UtilsReceiver.EXTRA_FRACTION, fraction);
558         intent.putExtra(UtilsReceiver.EXTRA_TIME, time);
559         final MutableLong bytes = new MutableLong(0);
560         getInstrumentation().getTargetContext().sendOrderedBroadcast(intent, null,
561                 new BroadcastReceiver() {
562                     @Override
563                     public void onReceive(Context context, Intent intent) {
564                         bytes.value = getResultExtras(false).getLong(UtilsReceiver.EXTRA_BYTES);
565                         latch.countDown();
566                     }
567                 }, null, Activity.RESULT_CANCELED, null, null);
568         latch.await(30, TimeUnit.SECONDS);
569         return bytes.value;
570     }
571 
doAllocateProvider(String pkg, double fraction, long time)572     private long doAllocateProvider(String pkg, double fraction, long time) throws Exception {
573         final Bundle args = new Bundle();
574         args.putDouble(UtilsReceiver.EXTRA_FRACTION, fraction);
575         args.putLong(UtilsReceiver.EXTRA_TIME, time);
576 
577         try (final ContentProviderClient client = getContext().getContentResolver()
578                 .acquireContentProviderClient(pkg)) {
579             final Bundle res = client.call(pkg, pkg, args);
580             return res.getLong(UtilsReceiver.EXTRA_BYTES);
581         }
582     }
583 
getSizeOfFilesEndWith(File dir, String suffix)584     private long getSizeOfFilesEndWith(File dir, String suffix) {
585         if (!dir.isDirectory()) {
586             return 0;
587         }
588 
589         long size = 0;
590         try {
591             for (File file : dir.listFiles()) {
592                 if (file.isFile() && file.getName().endsWith(suffix)) {
593                     size += file.length();
594                 }
595             }
596         } catch (NullPointerException e) {
597             size += 0;
598         }
599 
600         return size;
601     }
602 
getSizeOfDir(File dir)603     private long getSizeOfDir(File dir) {
604         if (!dir.isDirectory()) {
605             return 0;
606         }
607 
608         long size = 0;
609         try {
610             for (File file : dir.listFiles()) {
611                 if (file.isFile()) {
612                     size += file.length();
613                 } else if (file.isDirectory()) {
614                     size += getSizeOfDir(file);
615                 }
616             }
617         } catch (NullPointerException e) {
618             size += 0;
619         }
620 
621         return size;
622     }
623 }
624