Home | History | Annotate | Download | only in externalstorageapp
      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      */
     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      */
     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      */
     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      */
     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      */
     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      */
    105     public void testAllPackageDirsWritable() throws Exception {
    106         final long testValue = 12345000;
    107         final List<File> paths = getAllPackageSpecificPaths(getContext());
    108         for (File path : paths) {
    109             assertNotNull("Valid media must be inserted during CTS", path);
    110             assertEquals("Valid media must be inserted during CTS", Environment.MEDIA_MOUNTED,
    111                     Environment.getExternalStorageState(path));
    112 
    113             assertDirReadWriteAccess(path);
    114 
    115             final File directChild = new File(path, "directChild");
    116             final File subdir = new File(path, "subdir");
    117             final File subdirChild = new File(path, "subdirChild");
    118 
    119             writeInt(directChild, 32);
    120             subdir.mkdirs();
    121             assertDirReadWriteAccess(subdir);
    122             writeInt(subdirChild, 64);
    123 
    124             assertEquals(32, readInt(directChild));
    125             assertEquals(64, readInt(subdirChild));
    126 
    127             assertTrue("Must be able to set last modified", directChild.setLastModified(testValue));
    128             assertTrue("Must be able to set last modified", subdirChild.setLastModified(testValue));
    129 
    130             assertEquals(testValue, directChild.lastModified());
    131             assertEquals(testValue, subdirChild.lastModified());
    132         }
    133 
    134         for (File path : paths) {
    135             deleteContents(path);
    136         }
    137     }
    138 
    139     /**
    140      * Return a set of several package-specific external storage paths.
    141      */
    142     public static List<File> getAllPackageSpecificPaths(Context context) {
    143         final List<File> paths = new ArrayList<File>();
    144         Collections.addAll(paths, context.getExternalCacheDirs());
    145         Collections.addAll(paths, context.getExternalFilesDirs(null));
    146         Collections.addAll(paths, context.getExternalFilesDirs(Environment.DIRECTORY_PICTURES));
    147         Collections.addAll(paths, context.getExternalMediaDirs());
    148         Collections.addAll(paths, context.getObbDirs());
    149         return paths;
    150     }
    151 
    152     public static List<File> getAllPackageSpecificPathsExceptMedia(Context context) {
    153         final List<File> paths = new ArrayList<File>();
    154         Collections.addAll(paths, context.getExternalCacheDirs());
    155         Collections.addAll(paths, context.getExternalFilesDirs(null));
    156         Collections.addAll(paths, context.getExternalFilesDirs(Environment.DIRECTORY_PICTURES));
    157         Collections.addAll(paths, context.getObbDirs());
    158         return paths;
    159     }
    160 
    161     public static List<File> getAllPackageSpecificPathsExceptObb(Context context) {
    162         final List<File> paths = new ArrayList<File>();
    163         Collections.addAll(paths, context.getExternalCacheDirs());
    164         Collections.addAll(paths, context.getExternalFilesDirs(null));
    165         Collections.addAll(paths, context.getExternalFilesDirs(Environment.DIRECTORY_PICTURES));
    166         Collections.addAll(paths, context.getExternalMediaDirs());
    167         return paths;
    168     }
    169 
    170     public static List<File> getAllPackageSpecificObbGiftPaths(Context context,
    171             String targetPackageName) {
    172         final File[] files = context.getObbDirs();
    173         final List<File> targetFiles = new ArrayList<>();
    174         for (File file : files) {
    175             final File targetFile = new File(
    176                     file.getAbsolutePath().replace(context.getPackageName(), targetPackageName));
    177             targetFiles.add(new File(targetFile, targetPackageName + ".gift"));
    178         }
    179         return targetFiles;
    180     }
    181 
    182     /**
    183      * Return a set of several package-specific external storage paths pointing
    184      * at "gift" files designed to be exchanged with the target package.
    185      */
    186     public static List<File> getAllPackageSpecificGiftPaths(Context context,
    187             String targetPackageName) {
    188         final List<File> files = getPrimaryPackageSpecificPaths(context);
    189         final List<File> targetFiles = new ArrayList<>();
    190         for (File file : files) {
    191             final File targetFile = new File(
    192                     file.getAbsolutePath().replace(context.getPackageName(), targetPackageName));
    193             targetFiles.add(new File(targetFile, targetPackageName + ".gift"));
    194         }
    195         return targetFiles;
    196     }
    197 
    198     public static List<File> getPrimaryPackageSpecificPaths(Context context) {
    199         final List<File> paths = new ArrayList<File>();
    200         Collections.addAll(paths, context.getExternalCacheDir());
    201         Collections.addAll(paths, context.getExternalFilesDir(null));
    202         Collections.addAll(paths, context.getExternalFilesDir(Environment.DIRECTORY_PICTURES));
    203         Collections.addAll(paths, context.getObbDir());
    204         return paths;
    205     }
    206 
    207     public static List<File> getSecondaryPackageSpecificPaths(Context context) {
    208         final List<File> paths = new ArrayList<File>();
    209         Collections.addAll(paths, dropFirst(context.getExternalCacheDirs()));
    210         Collections.addAll(paths, dropFirst(context.getExternalFilesDirs(null)));
    211         Collections.addAll(
    212                 paths, dropFirst(context.getExternalFilesDirs(Environment.DIRECTORY_PICTURES)));
    213         Collections.addAll(paths, dropFirst(context.getObbDirs()));
    214         return paths;
    215     }
    216 
    217     public static List<File> getMountPaths() throws IOException {
    218         final List<File> paths = new ArrayList<>();
    219         final BufferedReader br = new BufferedReader(new FileReader("/proc/self/mounts"));
    220         try {
    221             String line;
    222             while ((line = br.readLine()) != null) {
    223                 final String[] fields = line.split(" ");
    224                 paths.add(new File(fields[1]));
    225             }
    226         } finally {
    227             br.close();
    228         }
    229         return paths;
    230     }
    231 
    232     private static File[] dropFirst(File[] before) {
    233         final File[] after = new File[before.length - 1];
    234         System.arraycopy(before, 1, after, 0, after.length);
    235         return after;
    236     }
    237 
    238     public static File buildProbeFile(File dir) {
    239         return new File(dir, ".probe_" + System.nanoTime());
    240     }
    241 
    242     public static File[] buildCommonChildDirs(File dir) {
    243         return new File[] {
    244                 new File(dir, Environment.DIRECTORY_MUSIC),
    245                 new File(dir, Environment.DIRECTORY_PODCASTS),
    246                 new File(dir, Environment.DIRECTORY_ALARMS),
    247                 new File(dir, Environment.DIRECTORY_RINGTONES),
    248                 new File(dir, Environment.DIRECTORY_NOTIFICATIONS),
    249                 new File(dir, Environment.DIRECTORY_PICTURES),
    250                 new File(dir, Environment.DIRECTORY_MOVIES),
    251                 new File(dir, Environment.DIRECTORY_DOWNLOADS),
    252                 new File(dir, Environment.DIRECTORY_DCIM),
    253                 new File(dir, Environment.DIRECTORY_DOCUMENTS),
    254         };
    255     }
    256 
    257     public static void assertDirReadOnlyAccess(File path) {
    258         Log.d(TAG, "Asserting read-only access to " + path);
    259 
    260         assertTrue("exists", path.exists());
    261         assertTrue("read", path.canRead());
    262         assertTrue("execute", path.canExecute());
    263         assertNotNull("list", path.list());
    264 
    265         try {
    266             final File probe = buildProbeFile(path);
    267             assertFalse(probe.createNewFile());
    268             assertFalse(probe.exists());
    269             assertFalse(probe.delete());
    270             fail("able to create probe!");
    271         } catch (IOException e) {
    272             // expected
    273         }
    274     }
    275 
    276     public static void assertDirReadWriteAccess(File path) {
    277         Log.d(TAG, "Asserting read/write access to " + path);
    278 
    279         assertTrue("exists", path.exists());
    280         assertTrue("read", path.canRead());
    281         assertTrue("execute", path.canExecute());
    282         assertNotNull("list", path.list());
    283 
    284         try {
    285             final File probe = buildProbeFile(path);
    286             assertTrue(probe.createNewFile());
    287             assertTrue(probe.exists());
    288             assertTrue(probe.delete());
    289             assertFalse(probe.exists());
    290         } catch (IOException e) {
    291             fail("failed to create probe!");
    292         }
    293     }
    294 
    295     public static void assertDirNoAccess(File path) {
    296         Log.d(TAG, "Asserting no access to " + path);
    297 
    298         assertFalse("read", path.canRead());
    299         assertNull("list", path.list());
    300 
    301         try {
    302             final File probe = buildProbeFile(path);
    303             assertFalse(probe.createNewFile());
    304             assertFalse(probe.exists());
    305             assertFalse(probe.delete());
    306             fail("able to create probe!");
    307         } catch (IOException e) {
    308             // expected
    309         }
    310     }
    311 
    312     public static void assertDirNoWriteAccess(File[] paths) {
    313         for (File path : paths) {
    314             assertDirNoWriteAccess(path);
    315         }
    316     }
    317 
    318     public static void assertDirNoWriteAccess(File path) {
    319         Log.d(TAG, "Asserting no write access to " + path);
    320 
    321         try {
    322             final File probe = buildProbeFile(path);
    323             assertFalse(probe.createNewFile());
    324             assertFalse(probe.exists());
    325             assertFalse(probe.delete());
    326             fail("able to create probe!");
    327         } catch (IOException e) {
    328             // expected
    329         }
    330     }
    331 
    332     public static void assertFileReadOnlyAccess(File path) {
    333         try {
    334             new FileInputStream(path).close();
    335         } catch (IOException e) {
    336             fail("failed to read!");
    337         }
    338 
    339         try {
    340             new FileOutputStream(path, true).close();
    341             fail("able to write!");
    342         } catch (IOException e) {
    343             // expected
    344         }
    345     }
    346 
    347     public static void assertFileReadWriteAccess(File path) {
    348         try {
    349             new FileInputStream(path).close();
    350         } catch (IOException e) {
    351             fail("failed to read!");
    352         }
    353 
    354         try {
    355             new FileOutputStream(path, true).close();
    356         } catch (IOException e) {
    357             fail("failed to write!");
    358         }
    359     }
    360 
    361     public static void assertFileNoAccess(File path) {
    362         try {
    363             new FileInputStream(path).close();
    364             fail("able to read!");
    365         } catch (IOException e) {
    366             // expected
    367         }
    368 
    369         try {
    370             new FileOutputStream(path, true).close();
    371             fail("able to write!");
    372         } catch (IOException e) {
    373             // expected
    374         }
    375     }
    376 
    377     public static void assertFileNotPresent(File path) {
    378         assertFalse(path + " exists!", path.exists());
    379     }
    380 
    381     public static void assertMediaNoAccess(ContentResolver resolver, boolean legacyApp)
    382             throws Exception {
    383         final ContentValues values = new ContentValues();
    384         values.put(Images.Media.MIME_TYPE, "image/jpeg");
    385         values.put(Images.Media.DATA,
    386                 buildProbeFile(Environment.getExternalStorageDirectory()).getAbsolutePath());
    387 
    388         try {
    389             Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
    390             if (legacyApp) {
    391                 // For legacy apps we do not crash - just make the operation do nothing
    392                 assertEquals(MediaStore.Images.Media.EXTERNAL_CONTENT_URI
    393                         .buildUpon().appendPath("0").build().toString(), uri.toString());
    394             } else {
    395                 fail("Expected access to be blocked");
    396             }
    397         } catch (Exception expected) {
    398         }
    399     }
    400 
    401     public static void assertMediaReadWriteAccess(ContentResolver resolver) throws Exception {
    402         final ContentValues values = new ContentValues();
    403         values.put(Images.Media.MIME_TYPE, "image/jpeg");
    404         values.put(Images.Media.DATA,
    405                 buildProbeFile(Environment.getExternalStorageDirectory()).getAbsolutePath());
    406 
    407         final Uri uri = resolver.insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
    408         try {
    409             resolver.openFileDescriptor(uri, "rw").close();
    410             resolver.openFileDescriptor(uri, "w").close();
    411             resolver.openFileDescriptor(uri, "r").close();
    412         } finally {
    413             resolver.delete(uri, null, null);
    414         }
    415     }
    416 
    417     private static boolean isWhiteList(File file) {
    418         final String[] whiteLists = {
    419                 "autorun.inf", ".android_secure", "android_secure"
    420         };
    421         if (file.getParentFile().getAbsolutePath().equals(
    422                 Environment.getExternalStorageDirectory().getAbsolutePath())) {
    423             for (String whiteList : whiteLists) {
    424                 if (file.getName().equalsIgnoreCase(whiteList)) {
    425                     return true;
    426                 }
    427             }
    428         }
    429         return false;
    430     }
    431 
    432     private static File[] removeWhiteList(File[] files) {
    433         List<File> fileList = new ArrayList<File>();
    434         if (files == null) {
    435             return null;
    436         }
    437 
    438         for (File file : files) {
    439             if (!isWhiteList(file)) {
    440                 fileList.add(file);
    441             }
    442         }
    443         return fileList.toArray(new File[fileList.size()]);
    444     }
    445 
    446     public static void deleteContents(File dir) throws IOException {
    447         File[] files = dir.listFiles();
    448         files = removeWhiteList(files);
    449         if (files != null) {
    450             for (File file : files) {
    451                 if (file.isDirectory()) {
    452                     deleteContents(file);
    453                 }
    454                 assertTrue(file.delete());
    455             }
    456 
    457             File[] dirs = removeWhiteList(dir.listFiles());
    458             if (dirs.length != 0) {
    459                 fail("Expected wiped storage but found: " + Arrays.toString(dirs));
    460             }
    461         }
    462     }
    463 
    464     public static void writeInt(File file, int value) throws IOException {
    465         final DataOutputStream os = new DataOutputStream(new FileOutputStream(file));
    466         try {
    467             os.writeInt(value);
    468         } finally {
    469             os.close();
    470         }
    471     }
    472 
    473     public static int readInt(File file) throws IOException {
    474         final DataInputStream is = new DataInputStream(new FileInputStream(file));
    475         try {
    476             return is.readInt();
    477         } finally {
    478             is.close();
    479         }
    480     }
    481 
    482     public static void logCommand(String... cmd) throws Exception {
    483         final Process proc = new ProcessBuilder(cmd).redirectErrorStream(true).start();
    484 
    485         final ByteArrayOutputStream buf = new ByteArrayOutputStream();
    486         copy(proc.getInputStream(), buf);
    487         final int res = proc.waitFor();
    488 
    489         Log.d(TAG, Arrays.toString(cmd) + " result " + res + ":");
    490         Log.d(TAG, buf.toString());
    491     }
    492 
    493     /** Shamelessly lifted from libcore.io.Streams */
    494     public static int copy(InputStream in, OutputStream out) throws IOException {
    495         int total = 0;
    496         byte[] buffer = new byte[8192];
    497         int c;
    498         while ((c = in.read(buffer)) != -1) {
    499             total += c;
    500             out.write(buffer, 0, c);
    501         }
    502         return total;
    503     }
    504 }
    505