1 /* 2 * Copyright (C) 2010 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.server; 18 19 import android.content.Context; 20 import android.content.res.Resources; 21 import android.content.res.Resources.NotFoundException; 22 import android.os.FileUtils; 23 import android.os.storage.OnObbStateChangeListener; 24 import android.os.storage.StorageManager; 25 import android.test.AndroidTestCase; 26 import android.test.ComparisonFailure; 27 import android.test.suitebuilder.annotation.LargeTest; 28 import android.util.Log; 29 30 import static com.android.server.MountService.buildObbPath; 31 32 import com.android.frameworks.servicestests.R; 33 34 import java.io.File; 35 import java.io.InputStream; 36 37 public class MountServiceTests extends AndroidTestCase { 38 private static final String TAG = "MountServiceTests"; 39 40 private static final long MAX_WAIT_TIME = 25*1000; 41 private static final long WAIT_TIME_INCR = 5*1000; 42 43 private static final String OBB_MOUNT_PREFIX = "/mnt/obb/"; 44 45 @Override setUp()46 protected void setUp() throws Exception { 47 super.setUp(); 48 } 49 50 @Override tearDown()51 protected void tearDown() throws Exception { 52 super.tearDown(); 53 } 54 assertStartsWith(String message, String prefix, String actual)55 private static void assertStartsWith(String message, String prefix, String actual) { 56 if (!actual.startsWith(prefix)) { 57 throw new ComparisonFailure(message, prefix, actual); 58 } 59 } 60 61 private static class ObbObserver extends OnObbStateChangeListener { 62 private String path; 63 64 public int state = -1; 65 boolean done = false; 66 67 @Override onObbStateChange(String path, int state)68 public void onObbStateChange(String path, int state) { 69 Log.d(TAG, "Received message. path=" + path + ", state=" + state); 70 synchronized (this) { 71 this.path = path; 72 this.state = state; 73 done = true; 74 notifyAll(); 75 } 76 } 77 getPath()78 public String getPath() { 79 assertTrue("Expected ObbObserver to have received a state change.", done); 80 return path; 81 } 82 getState()83 public int getState() { 84 assertTrue("Expected ObbObserver to have received a state change.", done); 85 return state; 86 } 87 reset()88 public void reset() { 89 this.path = null; 90 this.state = -1; 91 done = false; 92 } 93 isDone()94 public boolean isDone() { 95 return done; 96 } 97 waitForCompletion()98 public boolean waitForCompletion() { 99 long waitTime = 0; 100 synchronized (this) { 101 while (!isDone() && waitTime < MAX_WAIT_TIME) { 102 try { 103 wait(WAIT_TIME_INCR); 104 waitTime += WAIT_TIME_INCR; 105 } catch (InterruptedException e) { 106 Log.i(TAG, "Interrupted during sleep", e); 107 } 108 } 109 } 110 111 return isDone(); 112 } 113 } 114 getFilePath(String name)115 private File getFilePath(String name) { 116 final File filesDir = mContext.getFilesDir(); 117 final File outFile = new File(filesDir, name); 118 return outFile; 119 } 120 copyRawToFile(int rawResId, File outFile)121 private void copyRawToFile(int rawResId, File outFile) { 122 Resources res = mContext.getResources(); 123 InputStream is = null; 124 try { 125 is = res.openRawResource(rawResId); 126 } catch (NotFoundException e) { 127 fail("Failed to load resource with id: " + rawResId); 128 } 129 FileUtils.setPermissions(outFile.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG 130 | FileUtils.S_IRWXO, -1, -1); 131 assertTrue(FileUtils.copyToFile(is, outFile)); 132 FileUtils.setPermissions(outFile.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG 133 | FileUtils.S_IRWXO, -1, -1); 134 } 135 getStorageManager()136 private StorageManager getStorageManager() { 137 return (StorageManager) getContext().getSystemService(Context.STORAGE_SERVICE); 138 } 139 mountObb(StorageManager sm, final int resource, final File file, int expectedState)140 private void mountObb(StorageManager sm, final int resource, final File file, 141 int expectedState) { 142 copyRawToFile(resource, file); 143 144 final ObbObserver observer = new ObbObserver(); 145 assertTrue("mountObb call on " + file.getPath() + " should succeed", 146 sm.mountObb(file.getPath(), null, observer)); 147 148 assertTrue("Mount should have completed", 149 observer.waitForCompletion()); 150 151 if (expectedState == OnObbStateChangeListener.MOUNTED) { 152 assertTrue("OBB should be mounted", sm.isObbMounted(file.getPath())); 153 } 154 155 assertEquals("Actual file and resolved file should be the same", 156 file.getPath(), observer.getPath()); 157 158 assertEquals(expectedState, observer.getState()); 159 } 160 mountObbWithoutWait(final StorageManager sm, final int resource, final File file)161 private ObbObserver mountObbWithoutWait(final StorageManager sm, final int resource, 162 final File file) { 163 copyRawToFile(resource, file); 164 165 final ObbObserver observer = new ObbObserver(); 166 assertTrue("mountObb call on " + file.getPath() + " should succeed", sm.mountObb(file 167 .getPath(), null, observer)); 168 169 return observer; 170 } 171 waitForObbActionCompletion(final StorageManager sm, final File file, final ObbObserver observer, int expectedState, boolean checkPath)172 private void waitForObbActionCompletion(final StorageManager sm, final File file, 173 final ObbObserver observer, int expectedState, boolean checkPath) { 174 assertTrue("Mount should have completed", observer.waitForCompletion()); 175 176 assertTrue("OBB should be mounted", sm.isObbMounted(file.getPath())); 177 178 if (checkPath) { 179 assertEquals("Actual file and resolved file should be the same", file.getPath(), 180 observer.getPath()); 181 } 182 183 assertEquals(expectedState, observer.getState()); 184 } 185 checkMountedPath(final StorageManager sm, final File file)186 private String checkMountedPath(final StorageManager sm, final File file) { 187 final String mountPath = sm.getMountedObbPath(file.getPath()); 188 assertStartsWith("Path should be in " + OBB_MOUNT_PREFIX, 189 OBB_MOUNT_PREFIX, 190 mountPath); 191 return mountPath; 192 } 193 unmountObb(final StorageManager sm, final File file, int expectedState)194 private void unmountObb(final StorageManager sm, final File file, int expectedState) { 195 final ObbObserver observer = new ObbObserver(); 196 197 assertTrue("unmountObb call on test1.obb should succeed", 198 sm.unmountObb(file.getPath(), 199 false, observer)); 200 201 assertTrue("Unmount should have completed", 202 observer.waitForCompletion()); 203 204 assertEquals(expectedState, observer.getState()); 205 206 if (expectedState == OnObbStateChangeListener.UNMOUNTED) { 207 assertFalse("OBB should not be mounted", sm.isObbMounted(file.getPath())); 208 } 209 } 210 211 @LargeTest testMountAndUnmountObbNormal()212 public void testMountAndUnmountObbNormal() { 213 StorageManager sm = getStorageManager(); 214 215 final File outFile = getFilePath("test1.obb"); 216 217 mountObb(sm, R.raw.test1, outFile, OnObbStateChangeListener.MOUNTED); 218 219 mountObb(sm, R.raw.test1, outFile, OnObbStateChangeListener.ERROR_ALREADY_MOUNTED); 220 221 final String mountPath = checkMountedPath(sm, outFile); 222 final File mountDir = new File(mountPath); 223 224 assertTrue("OBB mounted path should be a directory", 225 mountDir.isDirectory()); 226 227 unmountObb(sm, outFile, OnObbStateChangeListener.UNMOUNTED); 228 } 229 230 @LargeTest testAttemptMountNonObb()231 public void testAttemptMountNonObb() { 232 StorageManager sm = getStorageManager(); 233 234 final File outFile = getFilePath("test1_nosig.obb"); 235 236 mountObb(sm, R.raw.test1_nosig, outFile, OnObbStateChangeListener.ERROR_INTERNAL); 237 238 assertFalse("OBB should not be mounted", 239 sm.isObbMounted(outFile.getPath())); 240 241 assertNull("OBB's mounted path should be null", 242 sm.getMountedObbPath(outFile.getPath())); 243 } 244 245 @LargeTest testAttemptMountObbWrongPackage()246 public void testAttemptMountObbWrongPackage() { 247 StorageManager sm = getStorageManager(); 248 249 final File outFile = getFilePath("test1_wrongpackage.obb"); 250 251 mountObb(sm, R.raw.test1_wrongpackage, outFile, 252 OnObbStateChangeListener.ERROR_PERMISSION_DENIED); 253 254 assertFalse("OBB should not be mounted", 255 sm.isObbMounted(outFile.getPath())); 256 257 assertNull("OBB's mounted path should be null", 258 sm.getMountedObbPath(outFile.getPath())); 259 } 260 261 @LargeTest testMountAndUnmountTwoObbs()262 public void testMountAndUnmountTwoObbs() { 263 StorageManager sm = getStorageManager(); 264 265 final File file1 = getFilePath("test1.obb"); 266 final File file2 = getFilePath("test2.obb"); 267 268 ObbObserver oo1 = mountObbWithoutWait(sm, R.raw.test1, file1); 269 ObbObserver oo2 = mountObbWithoutWait(sm, R.raw.test1, file2); 270 271 Log.d(TAG, "Waiting for OBB #1 to complete mount"); 272 waitForObbActionCompletion(sm, file1, oo1, OnObbStateChangeListener.MOUNTED, false); 273 Log.d(TAG, "Waiting for OBB #2 to complete mount"); 274 waitForObbActionCompletion(sm, file2, oo2, OnObbStateChangeListener.MOUNTED, false); 275 276 final String mountPath1 = checkMountedPath(sm, file1); 277 final File mountDir1 = new File(mountPath1); 278 assertTrue("OBB mounted path should be a directory", mountDir1.isDirectory()); 279 280 final String mountPath2 = checkMountedPath(sm, file2); 281 final File mountDir2 = new File(mountPath2); 282 assertTrue("OBB mounted path should be a directory", mountDir2.isDirectory()); 283 284 unmountObb(sm, file1, OnObbStateChangeListener.UNMOUNTED); 285 unmountObb(sm, file2, OnObbStateChangeListener.UNMOUNTED); 286 } 287 testBuildObbPath()288 public void testBuildObbPath() { 289 final int userId = 10; 290 291 // Paths outside external storage should remain untouched 292 assertEquals("/storage/random/foo", 293 buildObbPath("/storage/random/foo", userId, true)); 294 assertEquals("/storage/random/foo", 295 buildObbPath("/storage/random/foo", userId, false)); 296 297 // Paths on user-specific emulated storage 298 assertEquals("/mnt/shell/emulated/10/foo", 299 buildObbPath("/storage/emulated_legacy/foo", userId, true)); 300 assertEquals("/storage/emulated/10/foo", 301 buildObbPath("/storage/emulated_legacy/foo", userId, false)); 302 assertEquals("/mnt/shell/emulated/10/foo", 303 buildObbPath("/storage/emulated/10/foo", userId, true)); 304 assertEquals("/storage/emulated/10/foo", 305 buildObbPath("/storage/emulated/10/foo", userId, false)); 306 307 // Paths on shared OBB emulated storage 308 assertEquals("/mnt/shell/emulated/obb/foo", 309 buildObbPath("/storage/emulated_legacy/Android/obb/foo", userId, true)); 310 assertEquals("/storage/emulated/0/Android/obb/foo", 311 buildObbPath("/storage/emulated_legacy/Android/obb/foo", userId, false)); 312 assertEquals("/mnt/shell/emulated/obb/foo", 313 buildObbPath("/storage/emulated/10/Android/obb/foo", userId, true)); 314 assertEquals("/storage/emulated/0/Android/obb/foo", 315 buildObbPath("/storage/emulated/10/Android/obb/foo", userId, false)); 316 } 317 } 318