summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/os/Environment.java22
-rw-r--r--core/java/android/os/storage/IMountService.java41
-rw-r--r--core/java/android/os/storage/StorageManager.java57
-rw-r--r--include/storage/IMountService.h7
-rw-r--r--libs/storage/IMountService.cpp5
-rw-r--r--native/android/storage_manager.cpp15
-rw-r--r--services/java/com/android/server/MountService.java252
-rw-r--r--services/tests/servicestests/res/raw/test1.obb (renamed from core/tests/coretests/res/raw/test1.obb)bin37440 -> 37444 bytes
-rw-r--r--services/tests/servicestests/res/raw/test1_nosig.obb (renamed from core/tests/coretests/res/raw/test1_nosig.obb)bin37376 -> 37376 bytes
-rw-r--r--services/tests/servicestests/res/raw/test1_wrongpackage.obb (renamed from core/tests/coretests/res/raw/test1_wrongpackage.obb)bin37431 -> 37431 bytes
-rw-r--r--services/tests/servicestests/src/com/android/server/MountServiceTests.java (renamed from core/tests/coretests/src/com/android/server/MountServiceTests.java)36
11 files changed, 273 insertions, 162 deletions
diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java
index 5c4c036..3315566 100644
--- a/core/java/android/os/Environment.java
+++ b/core/java/android/os/Environment.java
@@ -31,6 +31,7 @@ public class Environment {
private static final String TAG = "Environment";
private static final String ENV_EXTERNAL_STORAGE = "EXTERNAL_STORAGE";
+ private static final String ENV_EMULATED_STORAGE_SOURCE = "EMULATED_STORAGE_SOURCE";
private static final String ENV_EMULATED_STORAGE_TARGET = "EMULATED_STORAGE_TARGET";
private static final String ENV_MEDIA_STORAGE = "MEDIA_STORAGE";
@@ -134,6 +135,10 @@ public class Environment {
return mExternalStorage;
}
+ public File getExternalStorageObbDirectory() {
+ return mExternalStorageAndroidObb;
+ }
+
public File getExternalStoragePublicDirectory(String type) {
return new File(mExternalStorage, type);
}
@@ -302,6 +307,23 @@ public class Environment {
return new File(System.getenv(ENV_EXTERNAL_STORAGE));
}
+ /** {@hide} */
+ public static File getLegacyExternalStorageObbDirectory() {
+ return buildPath(getLegacyExternalStorageDirectory(), DIRECTORY_ANDROID, "obb");
+ }
+
+ /** {@hide} */
+ public static File getEmulatedStorageSource(int userId) {
+ // /mnt/shell/emulated/0
+ return new File(System.getenv(ENV_EMULATED_STORAGE_SOURCE), String.valueOf(userId));
+ }
+
+ /** {@hide} */
+ public static File getEmulatedStorageObbSource() {
+ // /mnt/shell/emulated/obb
+ return new File(System.getenv(ENV_EMULATED_STORAGE_SOURCE), "obb");
+ }
+
/**
* Standard directory in which to place any audio files that should be
* in the regular list of music for the user.
diff --git a/core/java/android/os/storage/IMountService.java b/core/java/android/os/storage/IMountService.java
index 0b16316..fc18617 100644
--- a/core/java/android/os/storage/IMountService.java
+++ b/core/java/android/os/storage/IMountService.java
@@ -489,13 +489,14 @@ public interface IMountService extends IInterface {
* IObbActionListener to inform it of the terminal state of the
* call.
*/
- public void mountObb(String filename, String key, IObbActionListener token, int nonce)
- throws RemoteException {
+ public void mountObb(String rawPath, String canonicalPath, String key,
+ IObbActionListener token, int nonce) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
- _data.writeString(filename);
+ _data.writeString(rawPath);
+ _data.writeString(canonicalPath);
_data.writeString(key);
_data.writeStrongBinder((token != null ? token.asBinder() : null));
_data.writeInt(nonce);
@@ -514,13 +515,14 @@ public interface IMountService extends IInterface {
* IObbActionListener to inform it of the terminal state of the
* call.
*/
- public void unmountObb(String filename, boolean force, IObbActionListener token,
- int nonce) throws RemoteException {
+ public void unmountObb(
+ String rawPath, boolean force, IObbActionListener token, int nonce)
+ throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
- _data.writeString(filename);
+ _data.writeString(rawPath);
_data.writeInt((force ? 1 : 0));
_data.writeStrongBinder((token != null ? token.asBinder() : null));
_data.writeInt(nonce);
@@ -536,13 +538,13 @@ public interface IMountService extends IInterface {
* Checks whether the specified Opaque Binary Blob (OBB) is mounted
* somewhere.
*/
- public boolean isObbMounted(String filename) throws RemoteException {
+ public boolean isObbMounted(String rawPath) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
boolean _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
- _data.writeString(filename);
+ _data.writeString(rawPath);
mRemote.transact(Stub.TRANSACTION_isObbMounted, _data, _reply, 0);
_reply.readException();
_result = 0 != _reply.readInt();
@@ -556,13 +558,13 @@ public interface IMountService extends IInterface {
/**
* Gets the path to the mounted Opaque Binary Blob (OBB).
*/
- public String getMountedObbPath(String filename) throws RemoteException {
+ public String getMountedObbPath(String rawPath) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
- _data.writeString(filename);
+ _data.writeString(rawPath);
mRemote.transact(Stub.TRANSACTION_getMountedObbPath, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
@@ -1042,15 +1044,14 @@ public interface IMountService extends IInterface {
}
case TRANSACTION_mountObb: {
data.enforceInterface(DESCRIPTOR);
- String filename;
- filename = data.readString();
- String key;
- key = data.readString();
+ final String rawPath = data.readString();
+ final String canonicalPath = data.readString();
+ final String key = data.readString();
IObbActionListener observer;
observer = IObbActionListener.Stub.asInterface(data.readStrongBinder());
int nonce;
nonce = data.readInt();
- mountObb(filename, key, observer, nonce);
+ mountObb(rawPath, canonicalPath, key, observer, nonce);
reply.writeNoException();
return true;
}
@@ -1194,7 +1195,7 @@ public interface IMountService extends IInterface {
/**
* Gets the path to the mounted Opaque Binary Blob (OBB).
*/
- public String getMountedObbPath(String filename) throws RemoteException;
+ public String getMountedObbPath(String rawPath) throws RemoteException;
/**
* Gets an Array of currently known secure container IDs
@@ -1220,7 +1221,7 @@ public interface IMountService extends IInterface {
* Checks whether the specified Opaque Binary Blob (OBB) is mounted
* somewhere.
*/
- public boolean isObbMounted(String filename) throws RemoteException;
+ public boolean isObbMounted(String rawPath) throws RemoteException;
/*
* Returns true if the specified container is mounted
@@ -1243,8 +1244,8 @@ public interface IMountService extends IInterface {
* MountService will call back to the supplied IObbActionListener to inform
* it of the terminal state of the call.
*/
- public void mountObb(String filename, String key, IObbActionListener token, int nonce)
- throws RemoteException;
+ public void mountObb(String rawPath, String canonicalPath, String key,
+ IObbActionListener token, int nonce) throws RemoteException;
/*
* Mount a secure container with the specified key and owner UID. Returns an
@@ -1287,7 +1288,7 @@ public interface IMountService extends IInterface {
* MountService will call back to the supplied IObbActionListener to inform
* it of the terminal state of the call.
*/
- public void unmountObb(String filename, boolean force, IObbActionListener token, int nonce)
+ public void unmountObb(String rawPath, boolean force, IObbActionListener token, int nonce)
throws RemoteException;
/*
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index 54c8709..862a95c 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -28,6 +28,10 @@ import android.os.ServiceManager;
import android.util.Log;
import android.util.SparseArray;
+import com.android.internal.util.Preconditions;
+
+import java.io.File;
+import java.io.IOException;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
@@ -443,25 +447,23 @@ public class StorageManager
* That is, shared UID applications can attempt to mount any other
* application's OBB that shares its UID.
*
- * @param filename the path to the OBB file
+ * @param rawPath the path to the OBB file
* @param key secret used to encrypt the OBB; may be <code>null</code> if no
* encryption was used on the OBB.
* @param listener will receive the success or failure of the operation
* @return whether the mount call was successfully queued or not
*/
- public boolean mountObb(String filename, String key, OnObbStateChangeListener listener) {
- if (filename == null) {
- throw new IllegalArgumentException("filename cannot be null");
- }
-
- if (listener == null) {
- throw new IllegalArgumentException("listener cannot be null");
- }
+ public boolean mountObb(String rawPath, String key, OnObbStateChangeListener listener) {
+ Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
+ Preconditions.checkNotNull(listener, "listener cannot be null");
try {
+ final String canonicalPath = new File(rawPath).getCanonicalPath();
final int nonce = mObbActionListener.addListener(listener);
- mMountService.mountObb(filename, key, mObbActionListener, nonce);
+ mMountService.mountObb(rawPath, canonicalPath, key, mObbActionListener, nonce);
return true;
+ } catch (IOException e) {
+ throw new IllegalArgumentException("Failed to resolve path: " + rawPath, e);
} catch (RemoteException e) {
Log.e(TAG, "Failed to mount OBB", e);
}
@@ -483,24 +485,19 @@ public class StorageManager
* application's OBB that shares its UID.
* <p>
*
- * @param filename path to the OBB file
+ * @param rawPath path to the OBB file
* @param force whether to kill any programs using this in order to unmount
* it
* @param listener will receive the success or failure of the operation
* @return whether the unmount call was successfully queued or not
*/
- public boolean unmountObb(String filename, boolean force, OnObbStateChangeListener listener) {
- if (filename == null) {
- throw new IllegalArgumentException("filename cannot be null");
- }
-
- if (listener == null) {
- throw new IllegalArgumentException("listener cannot be null");
- }
+ public boolean unmountObb(String rawPath, boolean force, OnObbStateChangeListener listener) {
+ Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
+ Preconditions.checkNotNull(listener, "listener cannot be null");
try {
final int nonce = mObbActionListener.addListener(listener);
- mMountService.unmountObb(filename, force, mObbActionListener, nonce);
+ mMountService.unmountObb(rawPath, force, mObbActionListener, nonce);
return true;
} catch (RemoteException e) {
Log.e(TAG, "Failed to mount OBB", e);
@@ -512,16 +509,14 @@ public class StorageManager
/**
* Check whether an Opaque Binary Blob (OBB) is mounted or not.
*
- * @param filename path to OBB image
+ * @param rawPath path to OBB image
* @return true if OBB is mounted; false if not mounted or on error
*/
- public boolean isObbMounted(String filename) {
- if (filename == null) {
- throw new IllegalArgumentException("filename cannot be null");
- }
+ public boolean isObbMounted(String rawPath) {
+ Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
try {
- return mMountService.isObbMounted(filename);
+ return mMountService.isObbMounted(rawPath);
} catch (RemoteException e) {
Log.e(TAG, "Failed to check if OBB is mounted", e);
}
@@ -534,17 +529,15 @@ public class StorageManager
* give you the path to where you can obtain access to the internals of the
* OBB.
*
- * @param filename path to OBB image
+ * @param rawPath path to OBB image
* @return absolute path to mounted OBB image data or <code>null</code> if
* not mounted or exception encountered trying to read status
*/
- public String getMountedObbPath(String filename) {
- if (filename == null) {
- throw new IllegalArgumentException("filename cannot be null");
- }
+ public String getMountedObbPath(String rawPath) {
+ Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
try {
- return mMountService.getMountedObbPath(filename);
+ return mMountService.getMountedObbPath(rawPath);
} catch (RemoteException e) {
Log.e(TAG, "Failed to find mounted path for OBB", e);
}
diff --git a/include/storage/IMountService.h b/include/storage/IMountService.h
index 43df7f0..c3d34d8 100644
--- a/include/storage/IMountService.h
+++ b/include/storage/IMountService.h
@@ -21,6 +21,8 @@
#include <storage/IMountShutdownObserver.h>
#include <storage/IObbActionListener.h>
+#include <utils/String8.h>
+
#include <binder/IInterface.h>
#include <binder/Parcel.h>
@@ -60,8 +62,9 @@ public:
String16*& containers) = 0;
virtual void shutdown(const sp<IMountShutdownObserver>& observer) = 0;
virtual void finishMediaUpdate() = 0;
- virtual void mountObb(const String16& filename, const String16& key,
- const sp<IObbActionListener>& token, const int32_t nonce) = 0;
+ virtual void mountObb(const String16& rawPath, const String16& canonicalPath,
+ const String16& key, const sp<IObbActionListener>& token,
+ const int32_t nonce) = 0;
virtual void unmountObb(const String16& filename, const bool force,
const sp<IObbActionListener>& token, const int32_t nonce) = 0;
virtual bool isObbMounted(const String16& filename) = 0;
diff --git a/libs/storage/IMountService.cpp b/libs/storage/IMountService.cpp
index 4ec8b25..5701678 100644
--- a/libs/storage/IMountService.cpp
+++ b/libs/storage/IMountService.cpp
@@ -433,12 +433,13 @@ public:
reply.readExceptionCode();
}
- void mountObb(const String16& filename, const String16& key,
+ void mountObb(const String16& rawPath, const String16& canonicalPath, const String16& key,
const sp<IObbActionListener>& token, int32_t nonce)
{
Parcel data, reply;
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
- data.writeString16(filename);
+ data.writeString16(rawPath);
+ data.writeString16(canonicalPath);
data.writeString16(key);
data.writeStrongBinder(token->asBinder());
data.writeInt32(nonce);
diff --git a/native/android/storage_manager.cpp b/native/android/storage_manager.cpp
index f2f36b62..399f1ff 100644
--- a/native/android/storage_manager.cpp
+++ b/native/android/storage_manager.cpp
@@ -125,11 +125,20 @@ public:
}
}
- void mountObb(const char* filename, const char* key, AStorageManager_obbCallbackFunc func, void* data) {
+ void mountObb(const char* rawPath, const char* key, AStorageManager_obbCallbackFunc func,
+ void* data) {
+ // Resolve path before sending to MountService
+ char canonicalPath[PATH_MAX];
+ if (realpath(rawPath, canonicalPath) == NULL) {
+ ALOGE("mountObb failed to resolve path %s: %s", rawPath, strerror(errno));
+ return;
+ }
+
ObbCallback* cb = registerObbCallback(func, data);
- String16 filename16(filename);
+ String16 rawPath16(rawPath);
+ String16 canonicalPath16(canonicalPath);
String16 key16(key);
- mMountService->mountObb(filename16, key16, mObbActionListener, cb->nonce);
+ mMountService->mountObb(rawPath16, canonicalPath16, key16, mObbActionListener, cb->nonce);
}
void unmountObb(const char* filename, const bool force, AStorageManager_obbCallbackFunc func, void* data) {
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index fe2f8d8..0312705 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -58,6 +58,7 @@ import android.util.Slog;
import android.util.Xml;
import com.android.internal.app.IMediaContainerService;
+import com.android.internal.util.Preconditions;
import com.android.internal.util.XmlUtils;
import com.android.server.NativeDaemonConnector.Command;
import com.android.server.am.ActivityManagerService;
@@ -224,22 +225,31 @@ class MountService extends IMountService.Stub
* OBBs.
*/
final private Map<IBinder, List<ObbState>> mObbMounts = new HashMap<IBinder, List<ObbState>>();
+
+ /** Map from raw paths to {@link ObbState}. */
final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
class ObbState implements IBinder.DeathRecipient {
- public ObbState(String filename, int callerUid, IObbActionListener token, int nonce)
- throws RemoteException {
- this.filename = filename;
- this.callerUid = callerUid;
+ public ObbState(String rawPath, String canonicalPath, int callingUid,
+ IObbActionListener token, int nonce) {
+ this.rawPath = rawPath;
+ this.canonicalPath = canonicalPath.toString();
+
+ final int userId = UserHandle.getUserId(callingUid);
+ this.ownerPath = buildObbPath(canonicalPath, userId, false);
+ this.voldPath = buildObbPath(canonicalPath, userId, true);
+
+ this.ownerGid = UserHandle.getSharedAppGid(callingUid);
this.token = token;
this.nonce = nonce;
}
- // OBB source filename
- String filename;
+ final String rawPath;
+ final String canonicalPath;
+ final String ownerPath;
+ final String voldPath;
- // Binder.callingUid()
- final public int callerUid;
+ final int ownerGid;
// Token of remote Binder caller
final IObbActionListener token;
@@ -268,12 +278,13 @@ class MountService extends IMountService.Stub
@Override
public String toString() {
StringBuilder sb = new StringBuilder("ObbState{");
- sb.append("filename=");
- sb.append(filename);
- sb.append(",token=");
- sb.append(token.toString());
- sb.append(",callerUid=");
- sb.append(callerUid);
+ sb.append("rawPath=").append(rawPath);
+ sb.append(",canonicalPath=").append(canonicalPath);
+ sb.append(",ownerPath=").append(ownerPath);
+ sb.append(",voldPath=").append(voldPath);
+ sb.append(",ownerGid=").append(ownerGid);
+ sb.append(",token=").append(token);
+ sb.append(",binder=").append(getBinder());
sb.append('}');
return sb.toString();
}
@@ -1853,17 +1864,24 @@ class MountService extends IMountService.Stub
return callerUid == packageUid;
}
- public String getMountedObbPath(String filename) {
- if (filename == null) {
- throw new IllegalArgumentException("filename cannot be null");
- }
+ public String getMountedObbPath(String rawPath) {
+ Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
waitForReady();
warnOnNotMounted();
+ final ObbState state;
+ synchronized (mObbPathToStateMap) {
+ state = mObbPathToStateMap.get(rawPath);
+ }
+ if (state == null) {
+ Slog.w(TAG, "Failed to find OBB mounted at " + rawPath);
+ return null;
+ }
+
final NativeDaemonEvent event;
try {
- event = mConnector.execute("obb", "path", filename);
+ event = mConnector.execute("obb", "path", state.voldPath);
event.checkCode(VoldResponseCode.AsecPathResult);
return event.getMessage();
} catch (NativeDaemonConnectorException e) {
@@ -1876,48 +1894,52 @@ class MountService extends IMountService.Stub
}
}
- public boolean isObbMounted(String filename) {
- if (filename == null) {
- throw new IllegalArgumentException("filename cannot be null");
- }
-
+ @Override
+ public boolean isObbMounted(String rawPath) {
+ Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
synchronized (mObbMounts) {
- return mObbPathToStateMap.containsKey(filename);
+ return mObbPathToStateMap.containsKey(rawPath);
}
}
- public void mountObb(String filename, String key, IObbActionListener token, int nonce)
- throws RemoteException {
- if (filename == null) {
- throw new IllegalArgumentException("filename cannot be null");
- }
-
- if (token == null) {
- throw new IllegalArgumentException("token cannot be null");
- }
-
- final int callerUid = Binder.getCallingUid();
- final ObbState obbState = new ObbState(filename, callerUid, token, nonce);
- final ObbAction action = new MountObbAction(obbState, key);
+ @Override
+ public void mountObb(
+ String rawPath, String canonicalPath, String key, IObbActionListener token, int nonce) {
+ Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
+ Preconditions.checkNotNull(canonicalPath, "canonicalPath cannot be null");
+ Preconditions.checkNotNull(token, "token cannot be null");
+
+ final int callingUid = Binder.getCallingUid();
+ final ObbState obbState = new ObbState(rawPath, canonicalPath, callingUid, token, nonce);
+ final ObbAction action = new MountObbAction(obbState, key, callingUid);
mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
if (DEBUG_OBB)
Slog.i(TAG, "Send to OBB handler: " + action.toString());
}
- public void unmountObb(String filename, boolean force, IObbActionListener token, int nonce)
- throws RemoteException {
- if (filename == null) {
- throw new IllegalArgumentException("filename cannot be null");
+ @Override
+ public void unmountObb(String rawPath, boolean force, IObbActionListener token, int nonce) {
+ Preconditions.checkNotNull(rawPath, "rawPath cannot be null");
+
+ final ObbState existingState;
+ synchronized (mObbPathToStateMap) {
+ existingState = mObbPathToStateMap.get(rawPath);
}
- final int callerUid = Binder.getCallingUid();
- final ObbState obbState = new ObbState(filename, callerUid, token, nonce);
- final ObbAction action = new UnmountObbAction(obbState, force);
- mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
+ if (existingState != null) {
+ // TODO: separate state object from request data
+ final int callingUid = Binder.getCallingUid();
+ final ObbState newState = new ObbState(
+ rawPath, existingState.canonicalPath, callingUid, token, nonce);
+ final ObbAction action = new UnmountObbAction(newState, force);
+ mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
- if (DEBUG_OBB)
- Slog.i(TAG, "Send to OBB handler: " + action.toString());
+ if (DEBUG_OBB)
+ Slog.i(TAG, "Send to OBB handler: " + action.toString());
+ } else {
+ Slog.w(TAG, "Unknown OBB mount at " + rawPath);
+ }
}
@Override
@@ -2094,7 +2116,7 @@ class MountService extends IMountService.Stub
mObbMounts.put(binder, obbStates);
} else {
for (final ObbState o : obbStates) {
- if (o.filename.equals(obbState.filename)) {
+ if (o.rawPath.equals(obbState.rawPath)) {
throw new IllegalStateException("Attempt to add ObbState twice. "
+ "This indicates an error in the MountService logic.");
}
@@ -2118,7 +2140,7 @@ class MountService extends IMountService.Stub
throw e;
}
- mObbPathToStateMap.put(obbState.filename, obbState);
+ mObbPathToStateMap.put(obbState.rawPath, obbState);
}
private void removeObbStateLocked(ObbState obbState) {
@@ -2133,7 +2155,7 @@ class MountService extends IMountService.Stub
}
}
- mObbPathToStateMap.remove(obbState.filename);
+ mObbPathToStateMap.remove(obbState.rawPath);
}
private class ObbActionHandler extends Handler {
@@ -2241,33 +2263,32 @@ class MountService extends IMountService.Stub
synchronized (mObbMounts) {
final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
- final Iterator<Entry<String, ObbState>> i =
- mObbPathToStateMap.entrySet().iterator();
+ final Iterator<ObbState> i = mObbPathToStateMap.values().iterator();
while (i.hasNext()) {
- final Entry<String, ObbState> obbEntry = i.next();
+ final ObbState state = i.next();
/*
* If this entry's source file is in the volume path
* that got unmounted, remove it because it's no
* longer valid.
*/
- if (obbEntry.getKey().startsWith(path)) {
- obbStatesToRemove.add(obbEntry.getValue());
+ if (state.canonicalPath.startsWith(path)) {
+ obbStatesToRemove.add(state);
}
}
for (final ObbState obbState : obbStatesToRemove) {
if (DEBUG_OBB)
- Slog.i(TAG, "Removing state for " + obbState.filename);
+ Slog.i(TAG, "Removing state for " + obbState.rawPath);
removeObbStateLocked(obbState);
try {
- obbState.token.onObbResult(obbState.filename, obbState.nonce,
+ obbState.token.onObbResult(obbState.rawPath, obbState.nonce,
OnObbStateChangeListener.UNMOUNTED);
} catch (RemoteException e) {
Slog.i(TAG, "Couldn't send unmount notification for OBB: "
- + obbState.filename);
+ + obbState.rawPath);
}
}
}
@@ -2339,14 +2360,14 @@ class MountService extends IMountService.Stub
protected ObbInfo getObbInfo() throws IOException {
ObbInfo obbInfo;
try {
- obbInfo = mContainerService.getObbInfo(mObbState.filename);
+ obbInfo = mContainerService.getObbInfo(mObbState.ownerPath);
} catch (RemoteException e) {
Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for "
- + mObbState.filename);
+ + mObbState.ownerPath);
obbInfo = null;
}
if (obbInfo == null) {
- throw new IOException("Couldn't read OBB file: " + mObbState.filename);
+ throw new IOException("Couldn't read OBB file: " + mObbState.ownerPath);
}
return obbInfo;
}
@@ -2357,7 +2378,7 @@ class MountService extends IMountService.Stub
}
try {
- mObbState.token.onObbResult(mObbState.filename, mObbState.nonce, status);
+ mObbState.token.onObbResult(mObbState.rawPath, mObbState.nonce, status);
} catch (RemoteException e) {
Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
}
@@ -2366,10 +2387,12 @@ class MountService extends IMountService.Stub
class MountObbAction extends ObbAction {
private final String mKey;
+ private final int mCallingUid;
- MountObbAction(ObbState obbState, String key) {
+ MountObbAction(ObbState obbState, String key, int callingUid) {
super(obbState);
mKey = key;
+ mCallingUid = callingUid;
}
@Override
@@ -2379,7 +2402,7 @@ class MountService extends IMountService.Stub
final ObbInfo obbInfo = getObbInfo();
- if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mObbState.callerUid)) {
+ if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mCallingUid)) {
Slog.w(TAG, "Denied attempt to mount OBB " + obbInfo.filename
+ " which is owned by " + obbInfo.packageName);
sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
@@ -2388,7 +2411,7 @@ class MountService extends IMountService.Stub
final boolean isMounted;
synchronized (mObbMounts) {
- isMounted = mObbPathToStateMap.containsKey(obbInfo.filename);
+ isMounted = mObbPathToStateMap.containsKey(mObbState.rawPath);
}
if (isMounted) {
Slog.w(TAG, "Attempt to mount OBB which is already mounted: " + obbInfo.filename);
@@ -2396,12 +2419,6 @@ class MountService extends IMountService.Stub
return;
}
- /*
- * The filename passed in might not be the canonical name, so just
- * set the filename to the canonicalized version.
- */
- mObbState.filename = obbInfo.filename;
-
final String hashedKey;
if (mKey == null) {
hashedKey = "none";
@@ -2428,7 +2445,7 @@ class MountService extends IMountService.Stub
int rc = StorageResultCode.OperationSucceeded;
try {
mConnector.execute(
- "obb", "mount", mObbState.filename, hashedKey, mObbState.callerUid);
+ "obb", "mount", mObbState.voldPath, hashedKey, mObbState.ownerGid);
} catch (NativeDaemonConnectorException e) {
int code = e.getCode();
if (code != VoldResponseCode.OpFailedStorageBusy) {
@@ -2438,7 +2455,7 @@ class MountService extends IMountService.Stub
if (rc == StorageResultCode.OperationSucceeded) {
if (DEBUG_OBB)
- Slog.d(TAG, "Successfully mounted OBB " + mObbState.filename);
+ Slog.d(TAG, "Successfully mounted OBB " + mObbState.voldPath);
synchronized (mObbMounts) {
addObbStateLocked(mObbState);
@@ -2461,14 +2478,7 @@ class MountService extends IMountService.Stub
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("MountObbAction{");
- sb.append("filename=");
- sb.append(mObbState.filename);
- sb.append(",callerUid=");
- sb.append(mObbState.callerUid);
- sb.append(",token=");
- sb.append(mObbState.token != null ? mObbState.token.toString() : "NULL");
- sb.append(",binder=");
- sb.append(mObbState.token != null ? mObbState.getBinder().toString() : "null");
+ sb.append(mObbState);
sb.append('}');
return sb.toString();
}
@@ -2489,28 +2499,26 @@ class MountService extends IMountService.Stub
final ObbInfo obbInfo = getObbInfo();
- final ObbState obbState;
+ final ObbState existingState;
synchronized (mObbMounts) {
- obbState = mObbPathToStateMap.get(obbInfo.filename);
+ existingState = mObbPathToStateMap.get(mObbState.rawPath);
}
- if (obbState == null) {
+ if (existingState == null) {
sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_NOT_MOUNTED);
return;
}
- if (obbState.callerUid != mObbState.callerUid) {
- Slog.w(TAG, "Permission denied attempting to unmount OBB " + obbInfo.filename
- + " (owned by " + obbInfo.packageName + ")");
+ if (existingState.ownerGid != mObbState.ownerGid) {
+ Slog.w(TAG, "Permission denied attempting to unmount OBB " + existingState.rawPath
+ + " (owned by GID " + existingState.ownerGid + ")");
sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
return;
}
- mObbState.filename = obbInfo.filename;
-
int rc = StorageResultCode.OperationSucceeded;
try {
- final Command cmd = new Command("obb", "unmount", mObbState.filename);
+ final Command cmd = new Command("obb", "unmount", mObbState.voldPath);
if (mForceUnmount) {
cmd.appendArg("force");
}
@@ -2529,12 +2537,12 @@ class MountService extends IMountService.Stub
if (rc == StorageResultCode.OperationSucceeded) {
synchronized (mObbMounts) {
- removeObbStateLocked(obbState);
+ removeObbStateLocked(existingState);
}
sendNewStatusOrIgnore(OnObbStateChangeListener.UNMOUNTED);
} else {
- Slog.w(TAG, "Could not mount OBB: " + mObbState.filename);
+ Slog.w(TAG, "Could not unmount OBB: " + existingState);
sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT);
}
}
@@ -2548,21 +2556,63 @@ class MountService extends IMountService.Stub
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("UnmountObbAction{");
- sb.append("filename=");
- sb.append(mObbState.filename != null ? mObbState.filename : "null");
+ sb.append(mObbState);
sb.append(",force=");
sb.append(mForceUnmount);
- sb.append(",callerUid=");
- sb.append(mObbState.callerUid);
- sb.append(",token=");
- sb.append(mObbState.token != null ? mObbState.token.toString() : "null");
- sb.append(",binder=");
- sb.append(mObbState.token != null ? mObbState.getBinder().toString() : "null");
sb.append('}');
return sb.toString();
}
}
+ // @VisibleForTesting
+ public static String buildObbPath(final String canonicalPath, int userId, boolean forVold) {
+ // TODO: allow caller to provide Environment for full testing
+
+ // Only adjust paths when storage is emulated
+ if (!Environment.isExternalStorageEmulated()) {
+ return canonicalPath;
+ }
+
+ String path = canonicalPath.toString();
+
+ // First trim off any external storage prefix
+ final UserEnvironment userEnv = new UserEnvironment(userId);
+
+ // /storage/emulated/0
+ final String externalPath = userEnv.getExternalStorageDirectory().toString();
+ // /storage/emulated_legacy
+ final String legacyExternalPath = Environment.getLegacyExternalStorageDirectory()
+ .toString();
+
+ if (path.startsWith(externalPath)) {
+ path = path.substring(externalPath.length() + 1);
+ } else if (path.startsWith(legacyExternalPath)) {
+ path = path.substring(legacyExternalPath.length() + 1);
+ } else {
+ return canonicalPath;
+ }
+
+ // Handle special OBB paths on emulated storage
+ final String obbPath = "Android/obb";
+ if (path.startsWith(obbPath)) {
+ path = path.substring(obbPath.length() + 1);
+
+ if (forVold) {
+ return new File(Environment.getEmulatedStorageObbSource(), path).toString();
+ } else {
+ final UserEnvironment ownerEnv = new UserEnvironment(UserHandle.USER_OWNER);
+ return new File(ownerEnv.getExternalStorageObbDirectory(), path).toString();
+ }
+ }
+
+ // Handle normal external storage paths
+ if (forVold) {
+ return new File(Environment.getEmulatedStorageSource(userId), path).toString();
+ } else {
+ return new File(userEnv.getExternalStorageDirectory(), path).toString();
+ }
+ }
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) {
diff --git a/core/tests/coretests/res/raw/test1.obb b/services/tests/servicestests/res/raw/test1.obb
index 8466588..7d2b4f6 100644
--- a/core/tests/coretests/res/raw/test1.obb
+++ b/services/tests/servicestests/res/raw/test1.obb
Binary files differ
diff --git a/core/tests/coretests/res/raw/test1_nosig.obb b/services/tests/servicestests/res/raw/test1_nosig.obb
index 5c3573f7..5c3573f7 100644
--- a/core/tests/coretests/res/raw/test1_nosig.obb
+++ b/services/tests/servicestests/res/raw/test1_nosig.obb
Binary files differ
diff --git a/core/tests/coretests/res/raw/test1_wrongpackage.obb b/services/tests/servicestests/res/raw/test1_wrongpackage.obb
index d0aafe1..d0aafe1 100644
--- a/core/tests/coretests/res/raw/test1_wrongpackage.obb
+++ b/services/tests/servicestests/res/raw/test1_wrongpackage.obb
Binary files differ
diff --git a/core/tests/coretests/src/com/android/server/MountServiceTests.java b/services/tests/servicestests/src/com/android/server/MountServiceTests.java
index 1f8c92e..9c88b40 100644
--- a/core/tests/coretests/src/com/android/server/MountServiceTests.java
+++ b/services/tests/servicestests/src/com/android/server/MountServiceTests.java
@@ -16,8 +16,6 @@
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;
@@ -29,6 +27,10 @@ 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;
@@ -282,4 +284,34 @@ public class MountServiceTests extends AndroidTestCase {
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));
+ }
}