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 try { 166 contentResolver.insert(Uri.parse(URI_MEDIA_STRING), cv); 167 fail("Inserting file belonging to another user succeeded; path=" 168 + otherPath.getAbsolutePath()); 169 } catch (IllegalArgumentException expected) { 170 // OK 171 } 172 } 173 } 174 buildApiObbPath(String file)175 private File buildApiObbPath(String file) { 176 return new File(getContext().getObbDir(), file); 177 } 178 buildEnvObbPath(String file)179 private File buildEnvObbPath(String file) { 180 return new File(new File(System.getenv("EXTERNAL_STORAGE"), "Android/obb"), file); 181 } 182 buildRawObbPath(String file)183 private File buildRawObbPath(String file) { 184 return new File("/sdcard/Android/obb/", file); 185 } 186 buildApiPath(String file)187 private static File buildApiPath(String file) { 188 return new File(Environment.getExternalStorageDirectory(), file); 189 } 190 buildEnvPath(String file)191 private static File buildEnvPath(String file) { 192 return new File(System.getenv("EXTERNAL_STORAGE"), file); 193 } 194 buildRawPath(String file)195 private static File buildRawPath(String file) { 196 return new File("/sdcard/", file); 197 } 198 } 199