Skip to content

Commit

Permalink
Handle multi-user mountObb() requests.
Browse files Browse the repository at this point in the history
Since emulated external storage paths differ based on execution
context, carefully fix up paths for various use-cases:

1. When sending paths to DefaultContainerService, always scope
   OBB paths as belonging to USER_OWNER.
2. When sending paths to vold, always build emulated storage paths
   visible to root.
3. Always use the original untouched path when talking with apps.

Mount OBB containers using shared app GID, so that an app can read
the mount point across users.

Handle legacy paths like "/sdcard" by resolving the canonical path
before sending to MountService.  Move tests to servicestests, and
add tests for new path generation logic.

Bug: 7212801
Change-Id: I078c52879cd08d9c8a52cc8c83ac7ced1e8035e7
  • Loading branch information
jsharkey committed Sep 26, 2012
1 parent 5e21bf9 commit 4fbbda4
Show file tree
Hide file tree
Showing 11 changed files with 273 additions and 162 deletions.
22 changes: 22 additions & 0 deletions core/java/android/os/Environment.java
Original file line number Diff line number Diff line change
Expand Up @@ -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";

Expand Down Expand Up @@ -134,6 +135,10 @@ public File getExternalStorageDirectory() {
return mExternalStorage;
}

public File getExternalStorageObbDirectory() {
return mExternalStorageAndroidObb;
}

public File getExternalStoragePublicDirectory(String type) {
return new File(mExternalStorage, type);
}
Expand Down Expand Up @@ -302,6 +307,23 @@ public static File getLegacyExternalStorageDirectory() {
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.
Expand Down
41 changes: 21 additions & 20 deletions core/java/android/os/storage/IMountService.java
Original file line number Diff line number Diff line change
Expand Up @@ -489,13 +489,14 @@ public void finishMediaUpdate() throws RemoteException {
* 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);
Expand All @@ -514,13 +515,14 @@ public void mountObb(String filename, String key, IObbActionListener token, int
* 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);
Expand All @@ -536,13 +538,13 @@ public void unmountObb(String filename, boolean force, IObbActionListener token,
* 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();
Expand All @@ -556,13 +558,13 @@ public boolean isObbMounted(String filename) throws RemoteException {
/**
* 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();
Expand Down Expand Up @@ -1042,15 +1044,14 @@ public boolean onTransact(int code, Parcel data, Parcel reply,
}
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;
}
Expand Down Expand Up @@ -1194,7 +1195,7 @@ public int createSecureContainer(String id, int sizeMb, String fstype, String ke
/**
* 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
Expand All @@ -1220,7 +1221,7 @@ public int createSecureContainer(String id, int sizeMb, String fstype, String ke
* 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
Expand All @@ -1243,8 +1244,8 @@ public int createSecureContainer(String id, int sizeMb, String fstype, String ke
* 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
Expand Down Expand Up @@ -1287,7 +1288,7 @@ public void mountObb(String filename, String key, IObbActionListener token, int
* 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;

/*
Expand Down
57 changes: 25 additions & 32 deletions core/java/android/os/storage/StorageManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,10 @@
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;
Expand Down Expand Up @@ -443,25 +447,23 @@ public boolean isUsbMassStorageEnabled() {
* 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);
}
Expand All @@ -483,24 +485,19 @@ public boolean mountObb(String filename, String key, OnObbStateChangeListener li
* 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);
Expand All @@ -512,16 +509,14 @@ public boolean unmountObb(String filename, boolean force, OnObbStateChangeListen
/**
* 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);
}
Expand All @@ -534,17 +529,15 @@ public boolean isObbMounted(String filename) {
* 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);
}
Expand Down
7 changes: 5 additions & 2 deletions include/storage/IMountService.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include <storage/IMountShutdownObserver.h>
#include <storage/IObbActionListener.h>

#include <utils/String8.h>

#include <binder/IInterface.h>
#include <binder/Parcel.h>

Expand Down Expand Up @@ -60,8 +62,9 @@ class IMountService: public IInterface {
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;
Expand Down
5 changes: 3 additions & 2 deletions libs/storage/IMountService.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -433,12 +433,13 @@ class BpMountService: public BpInterface<IMountService>
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);
Expand Down
15 changes: 12 additions & 3 deletions native/android/storage_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,20 @@ struct AStorageManager : public RefBase {
}
}

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) {
Expand Down
Loading

0 comments on commit 4fbbda4

Please sign in to comment.