• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 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      */
getAllPackageSpecificPaths(Context context)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 
getAllPackageSpecificPathsExceptMedia(Context context)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 
getAllPackageSpecificPathsExceptObb(Context context)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 
getAllPackageSpecificObbGiftPaths(Context context, String targetPackageName)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      */
getAllPackageSpecificGiftPaths(Context context, String targetPackageName)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 
getPrimaryPackageSpecificPaths(Context context)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 
getSecondaryPackageSpecificPaths(Context context)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 
getMountPaths()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 
dropFirst(File[] before)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 
buildProbeFile(File dir)238     public static File buildProbeFile(File dir) {
239         return new File(dir, ".probe_" + System.nanoTime());
240     }
241 
buildCommonChildDirs(File dir)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 
assertDirReadOnlyAccess(File path)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 
assertDirReadWriteAccess(File path)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 
assertDirNoAccess(File path)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 
assertDirNoWriteAccess(File[] paths)312     public static void assertDirNoWriteAccess(File[] paths) {
313         for (File path : paths) {
314             assertDirNoWriteAccess(path);
315         }
316     }
317 
assertDirNoWriteAccess(File path)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 
assertFileReadOnlyAccess(File path)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 
assertFileReadWriteAccess(File path)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 
assertFileNoAccess(File path)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 
assertFileNotPresent(File path)377     public static void assertFileNotPresent(File path) {
378         assertFalse(path + " exists!", path.exists());
379     }
380 
assertMediaNoAccess(ContentResolver resolver, boolean legacyApp)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 
assertMediaReadWriteAccess(ContentResolver resolver)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 
isWhiteList(File file)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 
removeWhiteList(File[] files)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 
deleteContents(File dir)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 
writeInt(File file, int value)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 
readInt(File file)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 
logCommand(String... cmd)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 */
copy(InputStream in, OutputStream out)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