• 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.writeexternalstorageapp;
18 
19 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.PACKAGE_NONE;
20 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.PACKAGE_READ;
21 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.PACKAGE_WRITE;
22 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.TAG;
23 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertDirNoWriteAccess;
24 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertDirReadOnlyAccess;
25 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertDirReadWriteAccess;
26 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertFileReadWriteAccess;
27 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.buildGiftForPackage;
28 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.buildProbeFile;
29 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.deleteContents;
30 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.getAllPackageSpecificPaths;
31 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.getPrimaryPackageSpecificPaths;
32 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.getSecondaryPackageSpecificPaths;
33 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.readInt;
34 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.writeInt;
35 
36 import android.os.Environment;
37 import android.test.AndroidTestCase;
38 import android.util.Log;
39 
40 import com.android.cts.externalstorageapp.CommonExternalStorageTest;
41 
42 import java.io.BufferedReader;
43 import java.io.File;
44 import java.io.FileReader;
45 import java.util.List;
46 import java.util.Random;
47 
48 /**
49  * Test external storage from an application that has
50  * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE}.
51  */
52 public class WriteExternalStorageTest extends AndroidTestCase {
53 
54     private static final File TEST_FILE = new File(
55             Environment.getExternalStorageDirectory(), "meow");
56 
57     /**
58      * Set of file paths that should all refer to the same location to verify
59      * support for legacy paths.
60      */
61     private static final File[] IDENTICAL_FILES = {
62             new File("/sdcard/caek"),
63             new File(System.getenv("EXTERNAL_STORAGE"), "caek"),
64             new File(Environment.getExternalStorageDirectory(), "caek"),
65     };
66 
67     @Override
tearDown()68     protected void tearDown() throws Exception {
69         try {
70             TEST_FILE.delete();
71             for (File file : IDENTICAL_FILES) {
72                 file.delete();
73             }
74         } finally {
75             super.tearDown();
76         }
77     }
78 
assertExternalStorageMounted()79     private void assertExternalStorageMounted() {
80         assertEquals(Environment.MEDIA_MOUNTED, Environment.getExternalStorageState());
81     }
82 
testReadExternalStorage()83     public void testReadExternalStorage() throws Exception {
84         assertExternalStorageMounted();
85         Environment.getExternalStorageDirectory().list();
86     }
87 
testWriteExternalStorage()88     public void testWriteExternalStorage() throws Exception {
89         assertExternalStorageMounted();
90 
91         // Write a value and make sure we can read it back
92         writeInt(TEST_FILE, 32);
93         assertEquals(readInt(TEST_FILE), 32);
94     }
95 
96     /**
97      * Verify that legacy filesystem paths continue working, and that they all
98      * point to same location.
99      */
testLegacyPaths()100     public void testLegacyPaths() throws Exception {
101         final Random r = new Random();
102         for (File target : IDENTICAL_FILES) {
103             // Ensure we're starting with clean slate
104             for (File file : IDENTICAL_FILES) {
105                 file.delete();
106             }
107 
108             // Write value to our current target
109             final int value = r.nextInt();
110             writeInt(target, value);
111 
112             // Ensure that identical files all contain the value
113             for (File file : IDENTICAL_FILES) {
114                 assertEquals(readInt(file), value);
115             }
116         }
117     }
118 
testPrimaryReadWrite()119     public void testPrimaryReadWrite() throws Exception {
120         assertDirReadWriteAccess(Environment.getExternalStorageDirectory());
121     }
122 
123     /**
124      * Verify that above our package directories (on primary storage) we always
125      * have write access.
126      */
testPrimaryWalkingUpTreeReadWrite()127     public void testPrimaryWalkingUpTreeReadWrite() throws Exception {
128         final List<File> paths = getPrimaryPackageSpecificPaths(getContext());
129         final String packageName = getContext().getPackageName();
130 
131         for (File path : paths) {
132             assertNotNull("Valid media must be inserted during CTS", path);
133             assertEquals("Valid media must be inserted during CTS", Environment.MEDIA_MOUNTED,
134                     Environment.getStorageState(path));
135 
136             assertTrue(path.getAbsolutePath().contains(packageName));
137 
138             // Walk until we leave device, writing the whole way
139             while (Environment.MEDIA_MOUNTED.equals(Environment.getStorageState(path))) {
140                 assertDirReadWriteAccess(path);
141                 path = path.getParentFile();
142             }
143         }
144     }
145 
146     /**
147      * Verify that we have write access in other packages on primary external
148      * storage.
149      */
testPrimaryOtherPackageWriteAccess()150     public void testPrimaryOtherPackageWriteAccess() throws Exception {
151         deleteContents(Environment.getExternalStorageDirectory());
152 
153         final File ourCache = getContext().getExternalCacheDir();
154         final File otherCache = new File(ourCache.getAbsolutePath()
155                 .replace(getContext().getPackageName(), PACKAGE_NONE));
156 
157         assertTrue(otherCache.mkdirs());
158         assertDirReadWriteAccess(otherCache);
159     }
160 
161     /**
162      * Verify we have valid mount status until we leave the device.
163      */
testMountStatusWalkingUpTree()164     public void testMountStatusWalkingUpTree() {
165         final File top = Environment.getExternalStorageDirectory();
166         File path = getContext().getExternalCacheDir();
167 
168         int depth = 0;
169         while (depth++ < 32) {
170             assertDirReadWriteAccess(path);
171             assertEquals(Environment.MEDIA_MOUNTED, Environment.getStorageState(path));
172 
173             if (path.getAbsolutePath().equals(top.getAbsolutePath())) {
174                 break;
175             }
176 
177             path = path.getParentFile();
178         }
179 
180         // Make sure we hit the top
181         assertEquals(top.getAbsolutePath(), path.getAbsolutePath());
182 
183         // And going one step further should be outside our reach
184         path = path.getParentFile();
185         assertDirNoWriteAccess(path);
186         assertEquals(Environment.MEDIA_UNKNOWN, Environment.getStorageState(path));
187     }
188 
189     /**
190      * Verify mount status for random paths.
191      */
testMountStatus()192     public void testMountStatus() {
193         assertEquals(Environment.MEDIA_UNKNOWN,
194                 Environment.getStorageState(new File("/meow-should-never-exist")));
195 
196         // Internal data isn't a mount point
197         assertEquals(Environment.MEDIA_UNKNOWN,
198                 Environment.getStorageState(getContext().getCacheDir()));
199     }
200 
201     /**
202      * Verify that we have write access in our package-specific directories on
203      * secondary storage devices, but it becomes read-only access above them.
204      */
testSecondaryWalkingUpTreeReadOnly()205     public void testSecondaryWalkingUpTreeReadOnly() throws Exception {
206         final List<File> paths = getSecondaryPackageSpecificPaths(getContext());
207         final String packageName = getContext().getPackageName();
208 
209         for (File path : paths) {
210             assertNotNull("Valid media must be inserted during CTS", path);
211             assertEquals("Valid media must be inserted during CTS", Environment.MEDIA_MOUNTED,
212                     Environment.getStorageState(path));
213 
214             assertTrue(path.getAbsolutePath().contains(packageName));
215 
216             // Walk up until we drop our package
217             while (path.getAbsolutePath().contains(packageName)) {
218                 assertDirReadWriteAccess(path);
219                 path = path.getParentFile();
220             }
221 
222             // Keep walking up until we leave device
223             while (Environment.MEDIA_MOUNTED.equals(Environment.getStorageState(path))) {
224                 assertDirReadOnlyAccess(path);
225                 path = path.getParentFile();
226             }
227         }
228     }
229 
230     /**
231      * Verify that .nomedia is created correctly.
232      */
testVerifyNoMediaCreated()233     public void testVerifyNoMediaCreated() throws Exception {
234         deleteContents(Environment.getExternalStorageDirectory());
235 
236         final List<File> paths = getAllPackageSpecificPaths(getContext());
237 
238         // Require that .nomedia was created somewhere above each dir
239         for (File path : paths) {
240             assertNotNull("Valid media must be inserted during CTS", path);
241             assertEquals("Valid media must be inserted during CTS", Environment.MEDIA_MOUNTED,
242                     Environment.getStorageState(path));
243 
244             final File start = path;
245 
246             boolean found = false;
247             while (Environment.MEDIA_MOUNTED.equals(Environment.getStorageState(path))) {
248                 final File test = new File(path, ".nomedia");
249                 if (test.exists()) {
250                     found = true;
251                     break;
252                 }
253                 path = path.getParentFile();
254             }
255 
256             if (!found) {
257                 fail("Missing .nomedia file above package-specific directory " + start
258                         + "; gave up at " + path);
259             }
260         }
261     }
262 
263     /**
264      * Secondary external storage mount points must always be read-only, per
265      * CDD, <em>except</em> for the package specific directories tested by
266      * {@link CommonExternalStorageTest#testAllPackageDirsWritable()}.
267      */
testSecondaryMountPointsNotWritable()268     public void testSecondaryMountPointsNotWritable() throws Exception {
269         final File probe = buildProbeFile(Environment.getExternalStorageDirectory());
270         assertTrue(probe.createNewFile());
271 
272         final BufferedReader br = new BufferedReader(new FileReader("/proc/self/mounts"));
273         try {
274             String line;
275             while ((line = br.readLine()) != null) {
276                 final String[] fields = line.split(" ");
277                 final File testMount = new File(fields[1]);
278                 final File testProbe = new File(testMount, probe.getName());
279                 if (testProbe.exists()) {
280                     Log.d(TAG, "Primary external mountpoint " + testMount);
281                 } else {
282                     // This mountpoint is not primary external storage; we must
283                     // not be able to write.
284                     Log.d(TAG, "Other mountpoint " + testMount);
285                     assertDirNoWriteAccess(testProbe.getParentFile());
286                 }
287             }
288        } finally {
289            br.close();
290            probe.delete();
291        }
292     }
293 
294     /**
295      * Leave gifts for other packages in their primary external cache dirs.
296      */
doWriteGifts()297     public void doWriteGifts() throws Exception {
298         final File none = buildGiftForPackage(getContext(), PACKAGE_NONE);
299         none.getParentFile().mkdirs();
300         none.createNewFile();
301         assertFileReadWriteAccess(none);
302 
303         writeInt(none, 100);
304         assertEquals(100, readInt(none));
305 
306         final File read = buildGiftForPackage(getContext(), PACKAGE_READ);
307         read.getParentFile().mkdirs();
308         read.createNewFile();
309         assertFileReadWriteAccess(read);
310 
311         writeInt(read, 101);
312         assertEquals(101, readInt(read));
313 
314         final File write = buildGiftForPackage(getContext(), PACKAGE_WRITE);
315         write.getParentFile().mkdirs();
316         write.createNewFile();
317         assertFileReadWriteAccess(write);
318 
319         writeInt(write, 102);
320         assertEquals(102, readInt(write));
321     }
322 }
323