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