/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server; import android.content.Context; import android.content.res.Resources; import android.content.res.Resources.NotFoundException; import android.os.FileUtils; import android.os.storage.OnObbStateChangeListener; import android.os.storage.StorageManager; import android.test.AndroidTestCase; import android.test.ComparisonFailure; import android.test.suitebuilder.annotation.LargeTest; import android.util.Log; import static com.android.server.MountService.buildObbPath; import com.android.frameworks.servicestests.R; import java.io.File; import java.io.InputStream; public class MountServiceTests extends AndroidTestCase { private static final String TAG = "MountServiceTests"; private static final long MAX_WAIT_TIME = 25*1000; private static final long WAIT_TIME_INCR = 5*1000; private static final String OBB_MOUNT_PREFIX = "/mnt/obb/"; @Override protected void setUp() throws Exception { super.setUp(); } @Override protected void tearDown() throws Exception { super.tearDown(); } private static void assertStartsWith(String message, String prefix, String actual) { if (!actual.startsWith(prefix)) { throw new ComparisonFailure(message, prefix, actual); } } private static class ObbObserver extends OnObbStateChangeListener { private String path; public int state = -1; boolean done = false; @Override public void onObbStateChange(String path, int state) { Log.d(TAG, "Received message. path=" + path + ", state=" + state); synchronized (this) { this.path = path; this.state = state; done = true; notifyAll(); } } public String getPath() { assertTrue("Expected ObbObserver to have received a state change.", done); return path; } public int getState() { assertTrue("Expected ObbObserver to have received a state change.", done); return state; } public void reset() { this.path = null; this.state = -1; done = false; } public boolean isDone() { return done; } public boolean waitForCompletion() { long waitTime = 0; synchronized (this) { while (!isDone() && waitTime < MAX_WAIT_TIME) { try { wait(WAIT_TIME_INCR); waitTime += WAIT_TIME_INCR; } catch (InterruptedException e) { Log.i(TAG, "Interrupted during sleep", e); } } } return isDone(); } } private File getFilePath(String name) { final File filesDir = mContext.getFilesDir(); final File outFile = new File(filesDir, name); return outFile; } private void copyRawToFile(int rawResId, File outFile) { Resources res = mContext.getResources(); InputStream is = null; try { is = res.openRawResource(rawResId); } catch (NotFoundException e) { fail("Failed to load resource with id: " + rawResId); } FileUtils.setPermissions(outFile.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IRWXO, -1, -1); assertTrue(FileUtils.copyToFile(is, outFile)); FileUtils.setPermissions(outFile.getPath(), FileUtils.S_IRWXU | FileUtils.S_IRWXG | FileUtils.S_IRWXO, -1, -1); } private StorageManager getStorageManager() { return (StorageManager) getContext().getSystemService(Context.STORAGE_SERVICE); } private void mountObb(StorageManager sm, final int resource, final File file, int expectedState) { copyRawToFile(resource, file); final ObbObserver observer = new ObbObserver(); assertTrue("mountObb call on " + file.getPath() + " should succeed", sm.mountObb(file.getPath(), null, observer)); assertTrue("Mount should have completed", observer.waitForCompletion()); if (expectedState == OnObbStateChangeListener.MOUNTED) { assertTrue("OBB should be mounted", sm.isObbMounted(file.getPath())); } assertEquals("Actual file and resolved file should be the same", file.getPath(), observer.getPath()); assertEquals(expectedState, observer.getState()); } private ObbObserver mountObbWithoutWait(final StorageManager sm, final int resource, final File file) { copyRawToFile(resource, file); final ObbObserver observer = new ObbObserver(); assertTrue("mountObb call on " + file.getPath() + " should succeed", sm.mountObb(file .getPath(), null, observer)); return observer; } private void waitForObbActionCompletion(final StorageManager sm, final File file, final ObbObserver observer, int expectedState, boolean checkPath) { assertTrue("Mount should have completed", observer.waitForCompletion()); assertTrue("OBB should be mounted", sm.isObbMounted(file.getPath())); if (checkPath) { assertEquals("Actual file and resolved file should be the same", file.getPath(), observer.getPath()); } assertEquals(expectedState, observer.getState()); } private String checkMountedPath(final StorageManager sm, final File file) { final String mountPath = sm.getMountedObbPath(file.getPath()); assertStartsWith("Path should be in " + OBB_MOUNT_PREFIX, OBB_MOUNT_PREFIX, mountPath); return mountPath; } private void unmountObb(final StorageManager sm, final File file, int expectedState) { final ObbObserver observer = new ObbObserver(); assertTrue("unmountObb call on test1.obb should succeed", sm.unmountObb(file.getPath(), false, observer)); assertTrue("Unmount should have completed", observer.waitForCompletion()); assertEquals(expectedState, observer.getState()); if (expectedState == OnObbStateChangeListener.UNMOUNTED) { assertFalse("OBB should not be mounted", sm.isObbMounted(file.getPath())); } } @LargeTest public void testMountAndUnmountObbNormal() { StorageManager sm = getStorageManager(); final File outFile = getFilePath("test1.obb"); mountObb(sm, R.raw.test1, outFile, OnObbStateChangeListener.MOUNTED); mountObb(sm, R.raw.test1, outFile, OnObbStateChangeListener.ERROR_ALREADY_MOUNTED); final String mountPath = checkMountedPath(sm, outFile); final File mountDir = new File(mountPath); assertTrue("OBB mounted path should be a directory", mountDir.isDirectory()); unmountObb(sm, outFile, OnObbStateChangeListener.UNMOUNTED); } @LargeTest public void testAttemptMountNonObb() { StorageManager sm = getStorageManager(); final File outFile = getFilePath("test1_nosig.obb"); mountObb(sm, R.raw.test1_nosig, outFile, OnObbStateChangeListener.ERROR_INTERNAL); assertFalse("OBB should not be mounted", sm.isObbMounted(outFile.getPath())); assertNull("OBB's mounted path should be null", sm.getMountedObbPath(outFile.getPath())); } @LargeTest public void testAttemptMountObbWrongPackage() { StorageManager sm = getStorageManager(); final File outFile = getFilePath("test1_wrongpackage.obb"); mountObb(sm, R.raw.test1_wrongpackage, outFile, OnObbStateChangeListener.ERROR_PERMISSION_DENIED); assertFalse("OBB should not be mounted", sm.isObbMounted(outFile.getPath())); assertNull("OBB's mounted path should be null", sm.getMountedObbPath(outFile.getPath())); } @LargeTest public void testMountAndUnmountTwoObbs() { StorageManager sm = getStorageManager(); final File file1 = getFilePath("test1.obb"); final File file2 = getFilePath("test2.obb"); ObbObserver oo1 = mountObbWithoutWait(sm, R.raw.test1, file1); ObbObserver oo2 = mountObbWithoutWait(sm, R.raw.test1, file2); Log.d(TAG, "Waiting for OBB #1 to complete mount"); waitForObbActionCompletion(sm, file1, oo1, OnObbStateChangeListener.MOUNTED, false); Log.d(TAG, "Waiting for OBB #2 to complete mount"); waitForObbActionCompletion(sm, file2, oo2, OnObbStateChangeListener.MOUNTED, false); final String mountPath1 = checkMountedPath(sm, file1); final File mountDir1 = new File(mountPath1); assertTrue("OBB mounted path should be a directory", mountDir1.isDirectory()); final String mountPath2 = checkMountedPath(sm, file2); final File mountDir2 = new File(mountPath2); assertTrue("OBB mounted path should be a directory", mountDir2.isDirectory()); unmountObb(sm, file1, OnObbStateChangeListener.UNMOUNTED); unmountObb(sm, file2, OnObbStateChangeListener.UNMOUNTED); } public void testBuildObbPath() { final int userId = 10; // Paths outside external storage should remain untouched assertEquals("/storage/random/foo", buildObbPath("/storage/random/foo", userId, true)); assertEquals("/storage/random/foo", buildObbPath("/storage/random/foo", userId, false)); // Paths on user-specific emulated storage assertEquals("/mnt/shell/emulated/10/foo", buildObbPath("/storage/emulated_legacy/foo", userId, true)); assertEquals("/storage/emulated/10/foo", buildObbPath("/storage/emulated_legacy/foo", userId, false)); assertEquals("/mnt/shell/emulated/10/foo", buildObbPath("/storage/emulated/10/foo", userId, true)); assertEquals("/storage/emulated/10/foo", buildObbPath("/storage/emulated/10/foo", userId, false)); // Paths on shared OBB emulated storage assertEquals("/mnt/shell/emulated/obb/foo", buildObbPath("/storage/emulated_legacy/Android/obb/foo", userId, true)); assertEquals("/storage/emulated/0/Android/obb/foo", buildObbPath("/storage/emulated_legacy/Android/obb/foo", userId, false)); assertEquals("/mnt/shell/emulated/obb/foo", buildObbPath("/storage/emulated/10/Android/obb/foo", userId, true)); assertEquals("/storage/emulated/0/Android/obb/foo", buildObbPath("/storage/emulated/10/Android/obb/foo", userId, false)); } }