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.multiuserstorageapp;
18 
19 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertDirNoAccess;
20 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertFileNoAccess;
21 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.getAllPackageSpecificPathsExceptObb;
22 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.readInt;
23 import static com.android.cts.externalstorageapp.CommonExternalStorageTest.writeInt;
24 
25 import android.content.ContentResolver;
26 import android.content.ContentValues;
27 import android.net.Uri;
28 import android.os.Environment;
29 import android.test.AndroidTestCase;
30 import android.util.Log;
31 
32 import java.io.File;
33 import java.io.FileNotFoundException;
34 
35 /**
36  * Test multi-user emulated storage environment, ensuring that each user has
37  * isolated storage minus shared OBB directory.
38  */
39 public class MultiUserStorageTest extends AndroidTestCase {
40     private static final String TAG = "MultiUserStorageTest";
41 
42     private static final String FILE_PREFIX = "MUST_";
43 
44     private final String FILE_SINGLETON = FILE_PREFIX + "singleton";
45     private final String FILE_OBB_SINGLETON = FILE_PREFIX + "obb_singleton";
46     private final String FILE_OBB_API_SINGLETON = FILE_PREFIX + "obb_api_singleton";
47     private final String FILE_MY_UID = FILE_PREFIX + android.os.Process.myUid();
48 
49     private static final int OBB_API_VALUE = 0xcafe;
50     private static final int OBB_VALUE = 0xf00d;
51 
wipeTestFiles(File dir)52     private void wipeTestFiles(File dir) {
53         dir.mkdirs();
54         final File[] files = dir.listFiles();
55         if (files == null) return;
56         for (File file : files) {
57             if (file.getName().startsWith(FILE_PREFIX)) {
58                 Log.d(TAG, "Wiping " + file);
59                 file.delete();
60             }
61         }
62     }
63 
testCleanIsolatedStorage()64     public void testCleanIsolatedStorage() throws Exception {
65         wipeTestFiles(Environment.getExternalStorageDirectory());
66     }
67 
testWriteIsolatedStorage()68     public void testWriteIsolatedStorage() throws Exception {
69         final int uid = android.os.Process.myUid();
70 
71         writeInt(buildApiPath(FILE_SINGLETON), uid);
72         writeInt(buildApiPath(FILE_MY_UID), uid);
73 
74         // Write to every external path we think we have access to
75         for (File path : getAllPackageSpecificPathsExceptObb(getContext())) {
76             assertNotNull("Valid media must be inserted during CTS", path);
77             assertEquals("Valid media must be inserted during CTS", Environment.MEDIA_MOUNTED,
78                     Environment.getExternalStorageState(path));
79 
80             writeInt(new File(path, FILE_SINGLETON), uid);
81         }
82     }
83 
testReadIsolatedStorage()84     public void testReadIsolatedStorage() throws Exception {
85         final int uid = android.os.Process.myUid();
86 
87         // Expect that the value we wrote earlier is still valid and wasn't
88         // overwritten by us running as another user.
89         assertEquals("Failed to read singleton file from API path", uid,
90                 readInt(buildApiPath(FILE_SINGLETON)));
91         assertEquals("Failed to read singleton file from env path", uid,
92                 readInt(buildEnvPath(FILE_SINGLETON)));
93         assertEquals("Failed to read singleton file from raw path", uid,
94                 readInt(buildRawPath(FILE_SINGLETON)));
95 
96         assertEquals("Failed to read UID file from API path", uid,
97                 readInt(buildApiPath(FILE_MY_UID)));
98 
99         for (File path : getAllPackageSpecificPathsExceptObb(getContext())) {
100             assertNotNull("Valid media must be inserted during CTS", path);
101             assertEquals("Valid media must be inserted during CTS", Environment.MEDIA_MOUNTED,
102                     Environment.getExternalStorageState(path));
103 
104             assertEquals("Unexpected value in singleton file at " + path, uid,
105                     readInt(new File(path, FILE_SINGLETON)));
106         }
107     }
108 
testCleanObbStorage()109     public void testCleanObbStorage() throws Exception {
110         wipeTestFiles(getContext().getObbDir());
111     }
112 
testWriteObbStorage()113     public void testWriteObbStorage() throws Exception {
114         writeInt(buildApiObbPath(FILE_OBB_API_SINGLETON), OBB_API_VALUE);
115         writeInt(buildEnvObbPath(FILE_OBB_SINGLETON), OBB_VALUE);
116     }
117 
testReadObbStorage()118     public void testReadObbStorage() throws Exception {
119         assertEquals("Failed to read OBB file from API path", OBB_API_VALUE,
120                 readInt(buildApiObbPath(FILE_OBB_API_SINGLETON)));
121 
122         assertEquals("Failed to read OBB file from env path", OBB_VALUE,
123                 readInt(buildEnvObbPath(FILE_OBB_SINGLETON)));
124         assertEquals("Failed to read OBB file from raw path", OBB_VALUE,
125                 readInt(buildRawObbPath(FILE_OBB_SINGLETON)));
126     }
127 
128     /**
129      * Verify that we can't poke at storage of other users.
130      */
testUserIsolation()131     public void testUserIsolation() throws Exception {
132         final File myPath = Environment.getExternalStorageDirectory();
133         final int myId = android.os.Process.myUid() / 100000;
134         assertEquals(String.valueOf(myId), myPath.getName());
135 
136         Log.d(TAG, "My path is " + myPath);
137         final File basePath = myPath.getParentFile();
138         for (int i = 0; i < 128; i++) {
139             if (i == myId) continue;
140 
141             final File otherPath = new File(basePath, String.valueOf(i));
142             assertDirNoAccess(otherPath);
143         }
144     }
145 
146     /**
147      * Verify that files cannot be accessed through media provider.
148      */
testMediaProviderUserIsolation()149     public void testMediaProviderUserIsolation() throws Exception {
150         final File myPath = Environment.getExternalStorageDirectory();
151         final int myId = android.os.Process.myUid() / 100000;
152         assertEquals(String.valueOf(myId), myPath.getName());
153 
154         Log.d(TAG, "My path is " + myPath + " user id " + myId);
155         final File basePath = myPath.getParentFile();
156         for (int i = 0; i < 128; i++) {
157             if (i == myId) continue;
158             final File otherPath = new File(basePath,i + "/" + FILE_SINGLETON);
159             assertFileNoAccess(otherPath);
160 
161             final String URI_MEDIA_STRING = "content://media/external/audio/media/";
162             ContentResolver contentResolver = mContext.getContentResolver();
163             ContentValues cv = new ContentValues();
164             cv.put("_data", otherPath.getAbsolutePath());
165             contentResolver.insert(Uri.parse(URI_MEDIA_STRING), cv);
166 
167             try {
168                 mContext.getContentResolver().openInputStream(Uri.parse(URI_MEDIA_STRING));
169                 fail("Accessing through media provider should not be allowed. Path " + myPath);
170             } catch (FileNotFoundException expected) {
171                 // OK
172             }
173         }
174     }
175 
buildApiObbPath(String file)176     private File buildApiObbPath(String file) {
177         return new File(getContext().getObbDir(), file);
178     }
179 
buildEnvObbPath(String file)180     private File buildEnvObbPath(String file) {
181         return new File(new File(System.getenv("EXTERNAL_STORAGE"), "Android/obb"), file);
182     }
183 
buildRawObbPath(String file)184     private File buildRawObbPath(String file) {
185         return new File("/sdcard/Android/obb/", file);
186     }
187 
buildApiPath(String file)188     private static File buildApiPath(String file) {
189         return new File(Environment.getExternalStorageDirectory(), file);
190     }
191 
buildEnvPath(String file)192     private static File buildEnvPath(String file) {
193         return new File(System.getenv("EXTERNAL_STORAGE"), file);
194     }
195 
buildRawPath(String file)196     private static File buildRawPath(String file) {
197         return new File("/sdcard/", file);
198     }
199 }
200