diff options
| -rw-r--r-- | core/tests/coretests/res/raw/test1.obb | bin | 0 -> 37432 bytes | |||
| -rw-r--r-- | core/tests/coretests/res/raw/test1_nosig.obb | bin | 0 -> 37376 bytes | |||
| -rw-r--r-- | core/tests/coretests/res/raw/test1_wrongpackage.obb | bin | 0 -> 37423 bytes | |||
| -rw-r--r-- | core/tests/coretests/src/com/android/server/MountServiceTests.java | 210 | ||||
| -rw-r--r-- | services/java/com/android/server/MountService.java | 66 |
5 files changed, 256 insertions, 20 deletions
diff --git a/core/tests/coretests/res/raw/test1.obb b/core/tests/coretests/res/raw/test1.obb Binary files differnew file mode 100644 index 000000000000..170e36f4b9bf --- /dev/null +++ b/core/tests/coretests/res/raw/test1.obb diff --git a/core/tests/coretests/res/raw/test1_nosig.obb b/core/tests/coretests/res/raw/test1_nosig.obb Binary files differnew file mode 100644 index 000000000000..5c3573f72a1e --- /dev/null +++ b/core/tests/coretests/res/raw/test1_nosig.obb diff --git a/core/tests/coretests/res/raw/test1_wrongpackage.obb b/core/tests/coretests/res/raw/test1_wrongpackage.obb Binary files differnew file mode 100644 index 000000000000..2e02eaa51e4e --- /dev/null +++ b/core/tests/coretests/res/raw/test1_wrongpackage.obb diff --git a/core/tests/coretests/src/com/android/server/MountServiceTests.java b/core/tests/coretests/src/com/android/server/MountServiceTests.java new file mode 100644 index 000000000000..83e9d18eb01e --- /dev/null +++ b/core/tests/coretests/src/com/android/server/MountServiceTests.java @@ -0,0 +1,210 @@ +/* + * 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 com.android.frameworks.coretests.R; + +import android.content.Context; +import android.content.res.Resources; +import android.content.res.Resources.NotFoundException; +import android.os.Environment; +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 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 interface CompletableTask { + public boolean isDone(); + } + + private static class ObbObserver extends OnObbStateChangeListener implements CompletableTask { + public String path; + public String state; + boolean done = false; + + @Override + public void onObbStateChange(String path, String state) { + synchronized (this) { + this.path = path; + this.state = state; + done = true; + notifyAll(); + } + } + + public void reset() { + this.path = null; + this.state = null; + done = false; + } + + public boolean isDone() { + return done; + } + } + + private boolean waitForCompletion(CompletableTask task) { + long waitTime = 0; + synchronized (task) { + while (!task.isDone() && waitTime < MAX_WAIT_TIME) { + try { + task.wait(WAIT_TIME_INCR); + waitTime += WAIT_TIME_INCR; + } catch (InterruptedException e) { + Log.i(TAG, "Interrupted during sleep", e); + } + } + } + + return task.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, + String expectedState) { + copyRawToFile(resource, file); + + ObbObserver observer = new ObbObserver(); + assertTrue("mountObb call on " + file.getPath() + " should succeed", + sm.mountObb(file.getPath(), null, observer)); + + assertTrue("Mount should have completed", + waitForCompletion(observer)); + + assertEquals("Actual file and resolved file should be the same", + file.getPath(), observer.path); + + assertEquals(expectedState, observer.state); + } + + private String checkMountedPath(StorageManager sm, 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(StorageManager sm, final File outFile) { + ObbObserver observer = new ObbObserver(); + assertTrue("unmountObb call on test1.obb should succeed", + sm.unmountObb(outFile.getPath(), false, observer)); + + assertTrue("Unmount should have completed", + waitForCompletion(observer)); + } + + @LargeTest + public void testMountAndUnmountObbNormal() { + StorageManager sm = getStorageManager(); + + final File outFile = getFilePath("test1.obb"); + + mountObb(sm, R.raw.test1, outFile, Environment.MEDIA_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); + } + + @LargeTest + public void testAttemptMountNonObb() { + StorageManager sm = getStorageManager(); + + final File outFile = getFilePath("test1_nosig.obb"); + + mountObb(sm, R.raw.test1_nosig, outFile, Environment.MEDIA_BAD_REMOVAL); + + 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, Environment.MEDIA_BAD_REMOVAL); + + assertFalse("OBB should not be mounted", + sm.isObbMounted(outFile.getPath())); + + assertNull("OBB's mounted path should be null", + sm.getMountedObbPath(outFile.getPath())); + } +} diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java index 8e5cdc2e8937..06f9c41e707d 100644 --- a/services/java/com/android/server/MountService.java +++ b/services/java/com/android/server/MountService.java @@ -170,8 +170,6 @@ class MountService extends IMountService.Stub this.token = token; this.callerUid = callerUid; mounted = false; - - getBinder().linkToDeath(this, 0); } // OBB source filename @@ -196,7 +194,11 @@ class MountService extends IMountService.Stub mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action)); } - public void cleanUp() { + public void link() throws RemoteException { + getBinder().linkToDeath(this, 0); + } + + public void unlink() { getBinder().unlinkToDeath(this, 0); } @@ -1659,14 +1661,39 @@ class MountService extends IMountService.Stub Slog.i(TAG, "Send to OBB handler: " + action.toString()); } - private void addObbState(ObbState obbState) { + private void addObbState(ObbState obbState) throws RemoteException { synchronized (mObbMounts) { - List<ObbState> obbStates = mObbMounts.get(obbState.getBinder()); + final IBinder binder = obbState.getBinder(); + List<ObbState> obbStates = mObbMounts.get(binder); + final boolean unique; + if (obbStates == null) { obbStates = new ArrayList<ObbState>(); - mObbMounts.put(obbState.getBinder(), obbStates); + mObbMounts.put(binder, obbStates); + unique = true; + } else { + unique = obbStates.contains(obbState); + } + + if (unique) { + obbStates.add(obbState); + try { + obbState.link(); + } catch (RemoteException e) { + /* + * The binder died before we could link it, so clean up our + * state and return failure. + */ + obbStates.remove(obbState); + if (obbStates.isEmpty()) { + mObbMounts.remove(binder); + } + + // Rethrow the error so mountObb can get it + throw e; + } } - obbStates.add(obbState); + mObbPathToStateMap.put(obbState.filename, obbState); // Track the number of OBBs used by this UID. @@ -1682,14 +1709,17 @@ class MountService extends IMountService.Stub private void removeObbState(ObbState obbState) { synchronized (mObbMounts) { - final List<ObbState> obbStates = mObbMounts.get(obbState.getBinder()); + final IBinder binder = obbState.getBinder(); + final List<ObbState> obbStates = mObbMounts.get(binder); if (obbStates != null) { - obbStates.remove(obbState); - } - if (obbStates == null || obbStates.isEmpty()) { - mObbMounts.remove(obbState.getBinder()); - obbState.cleanUp(); + if (obbStates.remove(obbState)) { + obbState.unlink(); + } + if (obbStates.isEmpty()) { + mObbMounts.remove(binder); + } } + mObbPathToStateMap.remove(obbState.filename); // Track the number of OBBs used by this UID. @@ -1708,7 +1738,7 @@ class MountService extends IMountService.Stub } } - private void replaceObbState(ObbState oldObbState, ObbState newObbState) { + private void replaceObbState(ObbState oldObbState, ObbState newObbState) throws RemoteException { synchronized (mObbMounts) { removeObbState(oldObbState); addObbState(newObbState); @@ -1743,15 +1773,9 @@ class MountService extends IMountService.Stub action.handleError(); return; } - - mActions.add(action); - break; } - // Once we bind to the service, the first - // pending request will be processed. mActions.add(action); - mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND); break; } case OBB_MCS_BOUND: { @@ -1821,6 +1845,7 @@ class MountService extends IMountService.Stub if (DEBUG_OBB) Slog.i(TAG, "OBB_MCS_GIVE_UP"); mActions.remove(0); + mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND); break; } } @@ -1879,6 +1904,7 @@ class MountService extends IMountService.Stub if (DEBUG_OBB) Slog.d(TAG, "Error handling OBB action", e); handleError(); + mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND); } } |