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