1 /* 2 * Copyright (C) 2012 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.externalstorageapp; 18 19 import android.content.ContentResolver; 20 import android.content.ContentValues; 21 import android.content.Context; 22 import android.net.Uri; 23 import android.os.Environment; 24 import android.provider.MediaStore; 25 import android.provider.MediaStore.Images; 26 import android.test.AndroidTestCase; 27 import android.util.Log; 28 29 import java.io.BufferedReader; 30 import java.io.ByteArrayOutputStream; 31 import java.io.DataInputStream; 32 import java.io.DataOutputStream; 33 import java.io.File; 34 import java.io.FileInputStream; 35 import java.io.FileOutputStream; 36 import java.io.FileReader; 37 import java.io.IOException; 38 import java.io.InputStream; 39 import java.io.OutputStream; 40 import java.util.ArrayList; 41 import java.util.Arrays; 42 import java.util.Collections; 43 import java.util.List; 44 45 /** 46 * Tests common functionality that should be supported regardless of external 47 * storage status. 48 */ 49 public class CommonExternalStorageTest extends AndroidTestCase { 50 public static final String TAG = "CommonExternalStorageTest"; 51 52 public static final String PACKAGE_NONE = "com.android.cts.externalstorageapp"; 53 public static final String PACKAGE_READ = "com.android.cts.readexternalstorageapp"; 54 public static final String PACKAGE_WRITE = "com.android.cts.writeexternalstorageapp"; 55 56 /** 57 * Dump helpful debugging details. 58 */ testDumpDebug()59 public void testDumpDebug() throws Exception { 60 logCommand("/system/bin/id"); 61 logCommand("/system/bin/cat", "/proc/self/mountinfo"); 62 } 63 64 /** 65 * Primary storage must always be mounted. 66 */ testExternalStorageMounted()67 public void testExternalStorageMounted() { 68 assertEquals(Environment.MEDIA_MOUNTED, Environment.getExternalStorageState()); 69 } 70 71 /** 72 * Verify that single path is always first item in multiple. 73 */ testMultipleCacheDirs()74 public void testMultipleCacheDirs() throws Exception { 75 final File single = getContext().getExternalCacheDir(); 76 assertNotNull("Primary storage must always be available", single); 77 final File firstMultiple = getContext().getExternalCacheDirs()[0]; 78 assertEquals(single, firstMultiple); 79 } 80 81 /** 82 * Verify that single path is always first item in multiple. 83 */ testMultipleFilesDirs()84 public void testMultipleFilesDirs() throws Exception { 85 final File single = getContext().getExternalFilesDir(Environment.DIRECTORY_PICTURES); 86 assertNotNull("Primary storage must always be available", single); 87 final File firstMultiple = getContext() 88 .getExternalFilesDirs(Environment.DIRECTORY_PICTURES)[0]; 89 assertEquals(single, firstMultiple); 90 } 91 92 /** 93 * Verify that single path is always first item in multiple. 94 */ testMultipleObbDirs()95 public void testMultipleObbDirs() throws Exception { 96 final File single = getContext().getObbDir(); 97 assertNotNull("Primary storage must always be available", single); 98 final File firstMultiple = getContext().getObbDirs()[0]; 99 assertEquals(single, firstMultiple); 100 } 101 102 /** 103 * Verify we can write to our own package dirs. 104 */ testAllPackageDirsWritable()105 public void testAllPackageDirsWritable() throws Exception { 106 final List<File> paths = getAllPackageSpecificPaths(getContext()); 107 for (File path : paths) { 108 assertNotNull("Valid media must be inserted during CTS", path); 109 assertEquals("Valid media must be inserted during CTS", Environment.MEDIA_MOUNTED, 110 Environment.getExternalStorageState(path)); 111 112 assertDirReadWriteAccess(path); 113 114 final File directChild = new File(path, "directChild"); 115 final File subdir = new File(path, "subdir"); 116 final File subdirChild = new File(path, "subdirChild"); 117 118 writeInt(directChild, 32); 119 subdir.mkdirs(); 120 assertDirReadWriteAccess(subdir); 121 writeInt(subdirChild, 64); 122 123 assertEquals(32, readInt(directChild)); 124 assertEquals(64, readInt(subdirChild)); 125 } 126 127 for (File path : paths) { 128 deleteContents(path); 129 } 130 } 131 132 /** 133 * Return a set of several package-specific external storage paths. 134 */ getAllPackageSpecificPaths(Context context)135 public static List<File> getAllPackageSpecificPaths(Context context) { 136 final List<File> paths = new ArrayList<File>(); 137 Collections.addAll(paths, context.getExternalCacheDirs()); 138 Collections.addAll(paths, context.getExternalFilesDirs(null)); 139 Collections.addAll(paths, context.getExternalFilesDirs(Environment.DIRECTORY_PICTURES)); 140 Collections.addAll(paths, context.getObbDirs()); 141 return paths; 142 } 143 getAllPackageSpecificPathsExceptObb(Context context)144 public static List<File> getAllPackageSpecificPathsExceptObb(Context context) { 145 final List<File> paths = new ArrayList<File>(); 146 Collections.addAll(paths, context.getExternalCacheDirs()); 147 Collections.addAll(paths, context.getExternalFilesDirs(null)); 148 Collections.addAll(paths, context.getExternalFilesDirs(Environment.DIRECTORY_PICTURES)); 149 return paths; 150 } 151 getPrimaryPackageSpecificPaths(Context context)152 public static List<File> getPrimaryPackageSpecificPaths(Context context) { 153 final List<File> paths = new ArrayList<File>(); 154 Collections.addAll(paths, context.getExternalCacheDir()); 155 Collections.addAll(paths, context.getExternalFilesDir(null)); 156 Collections.addAll(paths, context.getExternalFilesDir(Environment.DIRECTORY_PICTURES)); 157 Collections.addAll(paths, context.getObbDir()); 158 return paths; 159 } 160 getSecondaryPackageSpecificPaths(Context context)161 public static List<File> getSecondaryPackageSpecificPaths(Context context) { 162 final List<File> paths = new ArrayList<File>(); 163 Collections.addAll(paths, dropFirst(context.getExternalCacheDirs())); 164 Collections.addAll(paths, dropFirst(context.getExternalFilesDirs(null))); 165 Collections.addAll( 166 paths, dropFirst(context.getExternalFilesDirs(Environment.DIRECTORY_PICTURES))); 167 Collections.addAll(paths, dropFirst(context.getObbDirs())); 168 return paths; 169 } 170 getMountPaths()171 public static List<File> getMountPaths() throws IOException { 172 final List<File> paths = new ArrayList<>(); 173 final BufferedReader br = new BufferedReader(new FileReader("/proc/self/mounts")); 174 try { 175 String line; 176 while ((line = br.readLine()) != null) { 177 final String[] fields = line.split(" "); 178 paths.add(new File(fields[1])); 179 } 180 } finally { 181 br.close(); 182 } 183 return paths; 184 } 185 dropFirst(File[] before)186 private static File[] dropFirst(File[] before) { 187 final File[] after = new File[before.length - 1]; 188 System.arraycopy(before, 1, after, 0, after.length); 189 return after; 190 } 191 buildGiftForPackage(Context context, String packageName)192 public static File buildGiftForPackage(Context context, String packageName) { 193 final File myCache = context.getExternalCacheDir(); 194 return new File(myCache.getAbsolutePath().replace(context.getPackageName(), packageName), 195 packageName + ".gift"); 196 } 197 buildProbeFile(File dir)198 public static File buildProbeFile(File dir) { 199 return new File(dir, ".probe_" + System.nanoTime()); 200 } 201 assertDirReadOnlyAccess(File path)202 public static void assertDirReadOnlyAccess(File path) { 203 Log.d(TAG, "Asserting read-only access to " + path); 204 205 assertTrue("exists", path.exists()); 206 assertTrue("read", path.canRead()); 207 assertTrue("execute", path.canExecute()); 208 assertNotNull("list", path.list()); 209 210 try { 211 final File probe = buildProbeFile(path); 212 probe.createNewFile(); 213 probe.delete(); 214 fail("able to create probe!"); 215 } catch (IOException e) { 216 // expected 217 } 218 } 219 assertDirReadWriteAccess(File path)220 public static void assertDirReadWriteAccess(File path) { 221 Log.d(TAG, "Asserting read/write access to " + path); 222 223 assertTrue("exists", path.exists()); 224 assertTrue("read", path.canRead()); 225 assertTrue("execute", path.canExecute()); 226 assertNotNull("list", path.list()); 227 228 try { 229 final File probe = buildProbeFile(path); 230 probe.createNewFile(); 231 probe.delete(); 232 } catch (IOException e) { 233 fail("failed to create probe!"); 234 } 235 } 236 assertDirNoAccess(File path)237 public static void assertDirNoAccess(File path) { 238 Log.d(TAG, "Asserting no access to " + path); 239 240 assertFalse("read", path.canRead()); 241 assertNull("list", path.list()); 242 243 try { 244 final File probe = buildProbeFile(path); 245 probe.createNewFile(); 246 probe.delete(); 247 fail("able to create probe!"); 248 } catch (IOException e) { 249 // expected 250 } 251 } 252 assertDirNoWriteAccess(File path)253 public static void assertDirNoWriteAccess(File path) { 254 Log.d(TAG, "Asserting no write access to " + path); 255 256 try { 257 final File probe = buildProbeFile(path); 258 probe.createNewFile(); 259 probe.delete(); 260 fail("able to create probe!"); 261 } catch (IOException e) { 262 // expected 263 } 264 } 265 assertFileReadOnlyAccess(File path)266 public static void assertFileReadOnlyAccess(File path) { 267 try { 268 new FileInputStream(path).close(); 269 } catch (IOException e) { 270 fail("failed to read!"); 271 } 272 273 try { 274 new FileOutputStream(path, true).close(); 275 fail("able to write!"); 276 } catch (IOException e) { 277 // expected 278 } 279 } 280 assertFileReadWriteAccess(File path)281 public static void assertFileReadWriteAccess(File path) { 282 try { 283 new FileInputStream(path).close(); 284 } catch (IOException e) { 285 fail("failed to read!"); 286 } 287 288 try { 289 new FileOutputStream(path, true).close(); 290 } catch (IOException e) { 291 fail("failed to write!"); 292 } 293 } 294 assertFileNoAccess(File path)295 public static void assertFileNoAccess(File path) { 296 try { 297 new FileInputStream(path).close(); 298 fail("able to read!"); 299 } catch (IOException e) { 300 // expected 301 } 302 303 try { 304 new FileOutputStream(path, true).close(); 305 fail("able to write!"); 306 } catch (IOException e) { 307 // expected 308 } 309 } 310 assertMediaNoAccess(ContentResolver resolver)311 public static void assertMediaNoAccess(ContentResolver resolver) throws Exception { 312 final ContentValues values = new ContentValues(); 313 values.put(Images.Media.MIME_TYPE, "image/jpeg"); 314 values.put(Images.Media.DATA, 315 buildProbeFile(Environment.getExternalStorageDirectory()).getAbsolutePath()); 316 317 try { 318 resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); 319 fail("Expected access to be blocked"); 320 } catch (Exception expected) { 321 } 322 } 323 assertMediaReadWriteAccess(ContentResolver resolver)324 public static void assertMediaReadWriteAccess(ContentResolver resolver) throws Exception { 325 final ContentValues values = new ContentValues(); 326 values.put(Images.Media.MIME_TYPE, "image/jpeg"); 327 values.put(Images.Media.DATA, 328 buildProbeFile(Environment.getExternalStorageDirectory()).getAbsolutePath()); 329 330 final Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); 331 try { 332 resolver.openFileDescriptor(uri, "rw").close(); 333 resolver.openFileDescriptor(uri, "w").close(); 334 resolver.openFileDescriptor(uri, "r").close(); 335 } finally { 336 resolver.delete(uri, null, null); 337 } 338 } 339 isWhiteList(File file)340 private static boolean isWhiteList(File file) { 341 final String[] whiteLists = { 342 "autorun.inf", ".android_secure", "android_secure" 343 }; 344 if (file.getParentFile().getAbsolutePath().equals( 345 Environment.getExternalStorageDirectory().getAbsolutePath())) { 346 for (String whiteList : whiteLists) { 347 if (file.getName().equalsIgnoreCase(whiteList)) { 348 return true; 349 } 350 } 351 } 352 return false; 353 } 354 removeWhiteList(File[] files)355 private static File[] removeWhiteList(File[] files) { 356 List<File> fileList = new ArrayList<File>(); 357 if (files == null) { 358 return null; 359 } 360 361 for (File file : files) { 362 if (!isWhiteList(file)) { 363 fileList.add(file); 364 } 365 } 366 return fileList.toArray(new File[fileList.size()]); 367 } 368 deleteContents(File dir)369 public static void deleteContents(File dir) throws IOException { 370 File[] files = dir.listFiles(); 371 files = removeWhiteList(files); 372 if (files != null) { 373 for (File file : files) { 374 if (file.isDirectory()) { 375 deleteContents(file); 376 } 377 assertTrue(file.delete()); 378 } 379 380 File[] dirs = removeWhiteList(dir.listFiles()); 381 if (dirs.length != 0) { 382 fail("Expected wiped storage but found: " + Arrays.toString(dirs)); 383 } 384 } 385 } 386 writeInt(File file, int value)387 public static void writeInt(File file, int value) throws IOException { 388 final DataOutputStream os = new DataOutputStream(new FileOutputStream(file)); 389 try { 390 os.writeInt(value); 391 } finally { 392 os.close(); 393 } 394 } 395 readInt(File file)396 public static int readInt(File file) throws IOException { 397 final DataInputStream is = new DataInputStream(new FileInputStream(file)); 398 try { 399 return is.readInt(); 400 } finally { 401 is.close(); 402 } 403 } 404 logCommand(String... cmd)405 public static void logCommand(String... cmd) throws Exception { 406 final Process proc = new ProcessBuilder(cmd).redirectErrorStream(true).start(); 407 408 final ByteArrayOutputStream buf = new ByteArrayOutputStream(); 409 copy(proc.getInputStream(), buf); 410 final int res = proc.waitFor(); 411 412 Log.d(TAG, Arrays.toString(cmd) + " result " + res + ":"); 413 Log.d(TAG, buf.toString()); 414 } 415 416 /** Shamelessly lifted from libcore.io.Streams */ copy(InputStream in, OutputStream out)417 public static int copy(InputStream in, OutputStream out) throws IOException { 418 int total = 0; 419 byte[] buffer = new byte[8192]; 420 int c; 421 while ((c = in.read(buffer)) != -1) { 422 total += c; 423 out.write(buffer, 0, c); 424 } 425 return total; 426 } 427 } 428