Merge "Handle multi-user mountObb() requests." into jb-mr1-dev

This commit is contained in:
Jeff Sharkey
2012-09-25 17:52:13 -07:00
committed by Android (Google) Code Review
11 changed files with 272 additions and 161 deletions

View File

@@ -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.

View File

@@ -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;
/*

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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) {

View File

@@ -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");
}
@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");
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);
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) {

View File

@@ -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));
}
}