Home | History | Annotate | Download | only in storageapp
      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.storageapp;
     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.CACHE_INT;
     24 import static com.android.cts.storageapp.Utils.DATA_EXT;
     25 import static com.android.cts.storageapp.Utils.DATA_INT;
     26 import static com.android.cts.storageapp.Utils.MB_IN_BYTES;
     27 import static com.android.cts.storageapp.Utils.PKG_B;
     28 import static com.android.cts.storageapp.Utils.assertMostlyEquals;
     29 import static com.android.cts.storageapp.Utils.getSizeManual;
     30 import static com.android.cts.storageapp.Utils.makeUniqueFile;
     31 import static com.android.cts.storageapp.Utils.useSpace;
     32 
     33 import android.app.Activity;
     34 import android.app.usage.StorageStats;
     35 import android.app.usage.StorageStatsManager;
     36 import android.content.ComponentName;
     37 import android.content.Context;
     38 import android.content.Intent;
     39 import android.content.pm.ApplicationInfo;
     40 import android.content.pm.PackageManager;
     41 import android.net.Uri;
     42 import android.os.Environment;
     43 import android.os.ParcelFileDescriptor;
     44 import android.os.UserHandle;
     45 import android.os.storage.StorageManager;
     46 import android.provider.Settings;
     47 import android.support.test.uiautomator.UiDevice;
     48 import android.support.test.uiautomator.UiSelector;
     49 import android.test.InstrumentationTestCase;
     50 
     51 import java.io.File;
     52 import java.io.IOException;
     53 import java.util.ArrayList;
     54 import java.util.Collections;
     55 import java.util.LinkedList;
     56 import java.util.List;
     57 import java.util.UUID;
     58 
     59 /**
     60  * Client app for verifying storage behaviors.
     61  */
     62 public class StorageTest extends InstrumentationTestCase {
     63     private Context getContext() {
     64         return getInstrumentation().getContext();
     65     }
     66 
     67     public void testAllocate() throws Exception {
     68         useSpace(getContext());
     69     }
     70 
     71     public void testFullDisk() throws Exception {
     72         final StorageStatsManager stats = getContext()
     73                 .getSystemService(StorageStatsManager.class);
     74         if (stats.isReservedSupported(UUID_DEFAULT)) {
     75             final File dataDir = getContext().getDataDir();
     76             Hoarder.doBlocks(dataDir, true);
     77         } else {
     78             fail("Skipping full disk test due to missing quota support");
     79         }
     80     }
     81 
     82     public void testTweakComponent() throws Exception {
     83         getContext().getPackageManager().setComponentEnabledSetting(
     84                 new ComponentName(getContext().getPackageName(), UtilsReceiver.class.getName()),
     85                 PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
     86     }
     87 
     88     public void testClearSpace() throws Exception {
     89         // First, disk better be full!
     90         assertTrue(getContext().getDataDir().getUsableSpace() < 256_000_000);
     91 
     92         final Activity activity = launchActivity("com.android.cts.storageapp_a",
     93                 UtilsActivity.class, null);
     94 
     95         final Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
     96         intent.setData(Uri.fromParts("package", "com.android.cts.storageapp_b", null));
     97         activity.startActivity(intent);
     98 
     99         final UiDevice device = UiDevice.getInstance(getInstrumentation());
    100         device.waitForIdle();
    101 
    102         // Hunt around to clear storage of other app
    103         device.findObject(new UiSelector().textContains("internal storage")).click();
    104         device.waitForIdle();
    105         device.findObject(new UiSelector().textContains("Clear")).click();
    106         device.waitForIdle();
    107         device.findObject(new UiSelector().text("OK")).click();
    108         device.waitForIdle();
    109 
    110         // Now, disk better be less-full!
    111         assertTrue(getContext().getDataDir().getUsableSpace() > 256_000_000);
    112     }
    113 
    114     /**
    115      * Measure ourselves manually.
    116      */
    117     public void testVerifySpaceManual() throws Exception {
    118         assertMostlyEquals(DATA_INT,
    119                 getSizeManual(getContext().getDataDir()));
    120         assertMostlyEquals(DATA_EXT,
    121                 getSizeManual(getContext().getExternalCacheDir().getParentFile()));
    122     }
    123 
    124     /**
    125      * Measure ourselves using platform APIs.
    126      */
    127     public void testVerifySpaceApi() throws Exception {
    128         final StorageManager sm = getContext().getSystemService(StorageManager.class);
    129         final StorageStatsManager stats = getContext().getSystemService(StorageStatsManager.class);
    130 
    131         final long cacheSize = sm.getCacheSizeBytes(
    132                 sm.getUuidForPath(getContext().getCacheDir()));
    133         final long extCacheSize = sm.getCacheSizeBytes(
    134                 sm.getUuidForPath(getContext().getExternalCacheDir()));
    135         if (cacheSize == extCacheSize) {
    136             assertMostlyEquals(CACHE_ALL, cacheSize);
    137         } else {
    138             assertMostlyEquals(CACHE_INT, cacheSize);
    139             assertMostlyEquals(CACHE_EXT, extCacheSize);
    140         }
    141 
    142         // Verify APIs that don't require any special permissions
    143         assertTrue(stats.getTotalBytes(StorageManager.UUID_DEFAULT) >= Environment
    144                 .getDataDirectory().getTotalSpace());
    145         assertTrue(stats.getFreeBytes(StorageManager.UUID_DEFAULT) >= Environment
    146                 .getDataDirectory().getUsableSpace());
    147 
    148         // Verify that we can see our own stats, and that they look sane
    149         ApplicationInfo ai = getContext().getApplicationInfo();
    150         final StorageStats pstats = stats.queryStatsForPackage(ai.storageUuid, ai.packageName,
    151                 UserHandle.getUserHandleForUid(ai.uid));
    152         final StorageStats ustats = stats.queryStatsForUid(ai.storageUuid, ai.uid);
    153         assertEquals(cacheSize, pstats.getCacheBytes());
    154         assertEquals(cacheSize, ustats.getCacheBytes());
    155 
    156         // Verify that other packages are off-limits
    157         ai = getContext().getPackageManager().getApplicationInfo(PKG_B, 0);
    158         try {
    159             stats.queryStatsForPackage(ai.storageUuid, ai.packageName,
    160                     UserHandle.getUserHandleForUid(ai.uid));
    161             fail("Unexpected access");
    162         } catch (SecurityException expected) {
    163         }
    164         try {
    165             stats.queryStatsForUid(ai.storageUuid, ai.uid);
    166             fail("Unexpected access");
    167         } catch (SecurityException expected) {
    168         }
    169         try {
    170             stats.queryExternalStatsForUser(StorageManager.UUID_DEFAULT,
    171                     android.os.Process.myUserHandle());
    172             fail("Unexpected access");
    173         } catch (SecurityException expected) {
    174         }
    175     }
    176 
    177     public void testVerifyQuotaApi() throws Exception {
    178         final StorageManager sm = getContext().getSystemService(StorageManager.class);
    179 
    180         final long cacheSize = sm.getCacheQuotaBytes(
    181                 sm.getUuidForPath(getContext().getCacheDir()));
    182         assertTrue("Apps must have at least 10MB quota", cacheSize > 10 * MB_IN_BYTES);
    183     }
    184 
    185     public void testVerifyAllocateApi() throws Exception {
    186         final StorageManager sm = getContext().getSystemService(StorageManager.class);
    187 
    188         final File filesDir = getContext().getFilesDir();
    189         final File extDir = Environment.getExternalStorageDirectory();
    190 
    191         final UUID filesUuid = sm.getUuidForPath(filesDir);
    192         final UUID extUuid = sm.getUuidForPath(extDir);
    193 
    194         assertTrue("Apps must be able to allocate internal space",
    195                 sm.getAllocatableBytes(filesUuid) > 10 * MB_IN_BYTES);
    196         assertTrue("Apps must be able to allocate external space",
    197                 sm.getAllocatableBytes(extUuid) > 10 * MB_IN_BYTES);
    198 
    199         // Should always be able to allocate 1MB indirectly
    200         sm.allocateBytes(filesUuid, 1 * MB_IN_BYTES);
    201 
    202         // Should always be able to allocate 1MB directly
    203         final File filesFile = makeUniqueFile(filesDir);
    204         assertEquals(0L, filesFile.length());
    205         try (ParcelFileDescriptor pfd = ParcelFileDescriptor.open(filesFile,
    206                 ParcelFileDescriptor.parseMode("rwt"))) {
    207             sm.allocateBytes(pfd.getFileDescriptor(), 1 * MB_IN_BYTES);
    208         }
    209         assertEquals(1 * MB_IN_BYTES, filesFile.length());
    210     }
    211 
    212     public void testBehaviorNormal() throws Exception {
    213         final StorageManager sm = getContext().getSystemService(StorageManager.class);
    214 
    215         final File dir = makeUniqueFile(getContext().getCacheDir());
    216         dir.mkdir();
    217         assertFalse(sm.isCacheBehaviorGroup(dir));
    218         assertFalse(sm.isCacheBehaviorTombstone(dir));
    219 
    220         final File ext = makeUniqueFile(getContext().getExternalCacheDir());
    221         ext.mkdir();
    222         try { sm.isCacheBehaviorGroup(ext); fail(); } catch (IOException expected) { }
    223         try { sm.isCacheBehaviorTombstone(ext); fail(); } catch (IOException expected) { }
    224     }
    225 
    226     public void testBehaviorGroup() throws Exception {
    227         final StorageManager sm = getContext().getSystemService(StorageManager.class);
    228 
    229         final File dir = makeUniqueFile(getContext().getCacheDir());
    230         dir.mkdir();
    231         sm.setCacheBehaviorGroup(dir, true);
    232         assertTrue(sm.isCacheBehaviorGroup(dir));
    233 
    234         final File ext = makeUniqueFile(getContext().getExternalCacheDir());
    235         ext.mkdir();
    236         try { sm.setCacheBehaviorGroup(ext, true); fail(); } catch (IOException expected) { }
    237         try { sm.setCacheBehaviorGroup(ext, false); fail(); } catch (IOException expected) { }
    238     }
    239 
    240     public void testBehaviorTombstone() throws Exception {
    241         final StorageManager sm = getContext().getSystemService(StorageManager.class);
    242 
    243         final File dir = makeUniqueFile(getContext().getCacheDir());
    244         dir.mkdir();
    245         sm.setCacheBehaviorTombstone(dir, true);
    246         assertTrue(sm.isCacheBehaviorTombstone(dir));
    247 
    248         final File ext = makeUniqueFile(getContext().getExternalCacheDir());
    249         ext.mkdir();
    250         try { sm.setCacheBehaviorTombstone(ext, true); fail(); } catch (IOException expected) { }
    251         try { sm.setCacheBehaviorTombstone(ext, false); fail(); } catch (IOException expected) { }
    252     }
    253 
    254     /**
    255      * Create "cts" probe files in every possible common storage location that
    256      * we can think of.
    257      */
    258     public void testExternalStorageIsolatedWrite() throws Exception {
    259         final Context context = getContext();
    260         final List<File> paths = new ArrayList<File>();
    261         Collections.addAll(paths, Environment.getExternalStorageDirectory());
    262         Collections.addAll(paths,
    263                 Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES));
    264         Collections.addAll(paths, context.getExternalCacheDirs());
    265         Collections.addAll(paths, context.getExternalFilesDirs(null));
    266         Collections.addAll(paths, context.getExternalFilesDirs(Environment.DIRECTORY_PICTURES));
    267         Collections.addAll(paths, context.getExternalMediaDirs());
    268         Collections.addAll(paths, context.getObbDirs());
    269 
    270         final String name = "cts_" + System.nanoTime();
    271         for (File path : paths) {
    272             final File otherPath = new File(path.getAbsolutePath()
    273                     .replace("com.android.cts.storageapp_a", "com.android.cts.storageapp_b"));
    274 
    275             path.mkdirs();
    276             otherPath.mkdirs();
    277 
    278             final File file = new File(path, name);
    279             final File otherFile = new File(otherPath, name);
    280 
    281             file.createNewFile();
    282             otherFile.createNewFile();
    283 
    284             assertTrue(file.exists());
    285             assertTrue(otherFile.exists());
    286         }
    287     }
    288 
    289     /**
    290      * Verify that we can't see any of the "cts" probe files created above,
    291      * since our storage should be fully isolated.
    292      */
    293     public void testExternalStorageIsolatedRead() throws Exception {
    294         final LinkedList<File> traverse = new LinkedList<>();
    295         traverse.push(Environment.getStorageDirectory());
    296         traverse.push(Environment.getExternalStorageDirectory());
    297 
    298         while (!traverse.isEmpty()) {
    299             final File dir = traverse.poll();
    300             for (File f : dir.listFiles()) {
    301                 if (f.getName().startsWith("cts_")) {
    302                     fail("Found leaked file " + f.getAbsolutePath());
    303                 }
    304                 if (f.isDirectory()) {
    305                     traverse.push(f);
    306                 }
    307             }
    308         }
    309     }
    310 
    311     public void testExternalStorageIsolatedLegacy() throws Exception {
    312         assertTrue(new File("/sdcard/cts_top").exists());
    313     }
    314 
    315     public void testExternalStorageIsolatedNonLegacy() throws Exception {
    316         assertFalse(new File("/sdcard/cts_top").exists());
    317     }
    318 }
    319