summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKenny Root <kroot@google.com>2010-07-01 08:10:18 -0700
committerKenny Root <kroot@google.com>2010-07-15 21:31:58 -0700
commit02c8730c1bf19daf48bec8c6995df676a00a73b1 (patch)
treef4d832a5308a18272cc6be0464a9824ad1e776ec
parentc5ed5910c9ef066cec6a13bbb404ec57b1e92637 (diff)
downloadframeworks_base-02c8730c1bf19daf48bec8c6995df676a00a73b1.zip
frameworks_base-02c8730c1bf19daf48bec8c6995df676a00a73b1.tar.gz
frameworks_base-02c8730c1bf19daf48bec8c6995df676a00a73b1.tar.bz2
Add API to call to vold for mounting OBBs
* Unhide StorageService class; hide all the USB-related items * Add application-visible API to StorageManager for OBB files * Add class for parceling OBB info across binders (ObbInfo) * Add a JNI glue class to libutils/ObbFile (ObbScanner) * Add API to MountService to deal with calling into vold and checking permissions Change-Id: I33ecf9606b8ff535f3a2ada83931da6bbef41cfd
-rw-r--r--api/current.xml79
-rw-r--r--core/java/android/content/Context.java3
-rwxr-xr-xcore/java/android/content/res/ObbInfo.aidl19
-rw-r--r--core/java/android/content/res/ObbInfo.java71
-rw-r--r--core/java/android/content/res/ObbScanner.java40
-rw-r--r--core/java/android/os/storage/IMountService.aidl22
-rw-r--r--core/java/android/os/storage/StorageManager.java64
-rwxr-xr-xcore/java/com/android/internal/app/IMediaContainerService.aidl4
-rw-r--r--core/jni/Android.mk3
-rw-r--r--core/jni/AndroidRuntime.cpp3
-rw-r--r--core/jni/android_content_res_ObbScanner.cpp94
-rw-r--r--services/java/com/android/server/MountService.java156
12 files changed, 548 insertions, 10 deletions
diff --git a/api/current.xml b/api/current.xml
index 0626cd5..1ed5811 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -38302,6 +38302,17 @@
visibility="public"
>
</field>
+<field name="STORAGE_SERVICE"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ value="&quot;storage&quot;"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
<field name="TELEPHONY_SERVICE"
type="java.lang.String"
transient="false"
@@ -125618,6 +125629,74 @@
</method>
</class>
</package>
+<package name="android.os.storage"
+>
+<class name="StorageManager"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getMountedObbPath"
+ return="java.lang.String"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="filename" type="java.lang.String">
+</parameter>
+</method>
+<method name="isObbMounted"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="filename" type="java.lang.String">
+</parameter>
+</method>
+<method name="mountObb"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="filename" type="java.lang.String">
+</parameter>
+<parameter name="key" type="java.lang.String">
+</parameter>
+</method>
+<method name="unmountObb"
+ return="boolean"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="filename" type="java.lang.String">
+</parameter>
+<parameter name="force" type="boolean">
+</parameter>
+</method>
+</class>
+</package>
<package name="android.preference"
>
<class name="CheckBoxPreference"
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index a14bd8f..e3b7731 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1372,9 +1372,8 @@ public abstract class Context {
public static final String SENSOR_SERVICE = "sensor";
/**
- * @hide
* Use with {@link #getSystemService} to retrieve a {@link
- * android.os.storage.StorageManager} for accesssing system storage
+ * android.os.storage.StorageManager} for accessing system storage
* functions.
*
* @see #getSystemService
diff --git a/core/java/android/content/res/ObbInfo.aidl b/core/java/android/content/res/ObbInfo.aidl
new file mode 100755
index 0000000..636ad6a
--- /dev/null
+++ b/core/java/android/content/res/ObbInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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 android.content.res;
+
+parcelable ObbInfo;
diff --git a/core/java/android/content/res/ObbInfo.java b/core/java/android/content/res/ObbInfo.java
new file mode 100644
index 0000000..b18d784
--- /dev/null
+++ b/core/java/android/content/res/ObbInfo.java
@@ -0,0 +1,71 @@
+/*
+ * 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 android.content.res;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Basic information about a Opaque Binary Blob (OBB) that reflects
+ * the info from the footer on the OBB file.
+ * @hide
+ */
+public class ObbInfo implements Parcelable {
+ /**
+ * The name of the package to which the OBB file belongs.
+ */
+ public String packageName;
+
+ /**
+ * The version of the package to which the OBB file belongs.
+ */
+ public int version;
+
+ public ObbInfo() {
+ }
+
+ public String toString() {
+ return "ObbInfo{"
+ + Integer.toHexString(System.identityHashCode(this))
+ + " packageName=" + packageName + ",version=" + version + "}";
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel dest, int parcelableFlags) {
+ dest.writeString(packageName);
+ dest.writeInt(version);
+ }
+
+ public static final Parcelable.Creator<ObbInfo> CREATOR
+ = new Parcelable.Creator<ObbInfo>() {
+ public ObbInfo createFromParcel(Parcel source) {
+ return new ObbInfo(source);
+ }
+
+ public ObbInfo[] newArray(int size) {
+ return new ObbInfo[size];
+ }
+ };
+
+ private ObbInfo(Parcel source) {
+ packageName = source.readString();
+ version = source.readInt();
+ }
+}
diff --git a/core/java/android/content/res/ObbScanner.java b/core/java/android/content/res/ObbScanner.java
new file mode 100644
index 0000000..eb383c3
--- /dev/null
+++ b/core/java/android/content/res/ObbScanner.java
@@ -0,0 +1,40 @@
+/*
+ * 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 android.content.res;
+
+/**
+ * Class to scan Opaque Binary Blob (OBB) files.
+ * @hide
+ */
+public class ObbScanner {
+ // Don't allow others to instantiate this class
+ private ObbScanner() {}
+
+ public static ObbInfo getObbInfo(String filePath) {
+ if (filePath == null) {
+ return null;
+ }
+
+ ObbInfo obbInfo = new ObbInfo();
+ if (!getObbInfo_native(filePath, obbInfo)) {
+ throw new IllegalArgumentException("Could not read OBB file: " + filePath);
+ }
+ return obbInfo;
+ }
+
+ private native static boolean getObbInfo_native(String filePath, ObbInfo obbInfo);
+}
diff --git a/core/java/android/os/storage/IMountService.aidl b/core/java/android/os/storage/IMountService.aidl
index 4862f80..ca7efe7 100644
--- a/core/java/android/os/storage/IMountService.aidl
+++ b/core/java/android/os/storage/IMountService.aidl
@@ -152,4 +152,26 @@ interface IMountService
* processing the media status update request.
*/
void finishMediaUpdate();
+
+ /**
+ * Mounts an Opaque Binary Blob (OBB) with the specified decryption key and only
+ * allows the calling process's UID access to the contents.
+ */
+ int mountObb(String filename, String key);
+
+ /**
+ * Unmounts an Opaque Binary Blob (OBB). When the force flag is specified, any
+ * program using it will be forcibly killed to unmount the image.
+ */
+ int unmountObb(String filename, boolean force);
+
+ /**
+ * Checks whether the specified Opaque Binary Blob (OBB) is mounted somewhere.
+ */
+ boolean isObbMounted(String filename);
+
+ /**
+ * Gets the path to the mounted Opaque Binary Blob (OBB).
+ */
+ String getMountedObbPath(String filename);
}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index a12603c..96bf2d5 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -44,9 +44,6 @@ import java.util.List;
* Get an instance of this class by calling
* {@link android.content.Context#getSystemService(java.lang.String)} with an argument
* of {@link android.content.Context#STORAGE_SERVICE}.
- *
- * @hide
- *
*/
public class StorageManager
@@ -209,6 +206,7 @@ public class StorageManager
*
* @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
*
+ * @hide
*/
public void registerListener(StorageEventListener listener) {
if (listener == null) {
@@ -225,6 +223,7 @@ public class StorageManager
*
* @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object.
*
+ * @hide
*/
public void unregisterListener(StorageEventListener listener) {
if (listener == null) {
@@ -245,6 +244,8 @@ public class StorageManager
/**
* Enables USB Mass Storage (UMS) on the device.
+ *
+ * @hide
*/
public void enableUsbMassStorage() {
try {
@@ -256,6 +257,8 @@ public class StorageManager
/**
* Disables USB Mass Storage (UMS) on the device.
+ *
+ * @hide
*/
public void disableUsbMassStorage() {
try {
@@ -268,6 +271,8 @@ public class StorageManager
/**
* Query if a USB Mass Storage (UMS) host is connected.
* @return true if UMS host is connected.
+ *
+ * @hide
*/
public boolean isUsbMassStorageConnected() {
try {
@@ -281,6 +286,8 @@ public class StorageManager
/**
* Query if a USB Mass Storage (UMS) is enabled on the device.
* @return true if UMS host is enabled.
+ *
+ * @hide
*/
public boolean isUsbMassStorageEnabled() {
try {
@@ -290,4 +297,55 @@ public class StorageManager
}
return false;
}
+
+ /**
+ * Mount an OBB file.
+ */
+ public boolean mountObb(String filename, String key) {
+ try {
+ return mMountService.mountObb(filename, key)
+ == StorageResultCode.OperationSucceeded;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to mount OBB", e);
+ }
+
+ return false;
+ }
+
+ /**
+ * Mount an OBB file.
+ */
+ public boolean unmountObb(String filename, boolean force) {
+ try {
+ return mMountService.unmountObb(filename, force)
+ == StorageResultCode.OperationSucceeded;
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to mount OBB", e);
+ }
+
+ return false;
+ }
+
+ public boolean isObbMounted(String filename) {
+ try {
+ return mMountService.isObbMounted(filename);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to check if OBB is mounted", e);
+ }
+
+ return false;
+ }
+
+ /**
+ * Check the mounted path of an OBB file.
+ */
+ public String getMountedObbPath(String filename) {
+ try {
+ return mMountService.getMountedObbPath(filename);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to find mounted path for OBB", e);
+ }
+
+ return null;
+ }
}
diff --git a/core/java/com/android/internal/app/IMediaContainerService.aidl b/core/java/com/android/internal/app/IMediaContainerService.aidl
index 0f817b7..a59c14b 100755
--- a/core/java/com/android/internal/app/IMediaContainerService.aidl
+++ b/core/java/com/android/internal/app/IMediaContainerService.aidl
@@ -19,6 +19,7 @@ package com.android.internal.app;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.content.pm.PackageInfoLite;
+import android.content.res.ObbInfo;
interface IMediaContainerService {
String copyResourceToContainer(in Uri packageURI,
@@ -28,4 +29,5 @@ interface IMediaContainerService {
in ParcelFileDescriptor outStream);
PackageInfoLite getMinimalPackageInfo(in Uri fileUri, int flags);
boolean checkFreeStorage(boolean external, in Uri fileUri);
-} \ No newline at end of file
+ ObbInfo getObbInfo(in Uri fileUri);
+}
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 468f844..89fea41 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -134,7 +134,8 @@ LOCAL_SRC_FILES:= \
android_backup_BackupDataInput.cpp \
android_backup_BackupDataOutput.cpp \
android_backup_FileBackupHelperBase.cpp \
- android_backup_BackupHelperDispatcher.cpp
+ android_backup_BackupHelperDispatcher.cpp \
+ android_content_res_ObbScanner.cpp
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp
index 6fb1369..fc1f488 100644
--- a/core/jni/AndroidRuntime.cpp
+++ b/core/jni/AndroidRuntime.cpp
@@ -166,6 +166,7 @@ extern int register_android_view_InputChannel(JNIEnv* env);
extern int register_android_view_InputQueue(JNIEnv* env);
extern int register_android_view_KeyEvent(JNIEnv* env);
extern int register_android_view_MotionEvent(JNIEnv* env);
+extern int register_android_content_res_ObbScanner(JNIEnv* env);
static AndroidRuntime* gCurRuntime = NULL;
@@ -1298,6 +1299,8 @@ static const RegJNIRec gRegJNI[] = {
REG_JNI(register_android_view_InputQueue),
REG_JNI(register_android_view_KeyEvent),
REG_JNI(register_android_view_MotionEvent),
+
+ REG_JNI(register_android_content_res_ObbScanner),
};
/*
diff --git a/core/jni/android_content_res_ObbScanner.cpp b/core/jni/android_content_res_ObbScanner.cpp
new file mode 100644
index 0000000..1239274
--- /dev/null
+++ b/core/jni/android_content_res_ObbScanner.cpp
@@ -0,0 +1,94 @@
+/*
+ * Copyright 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.
+ */
+
+#define LOG_TAG "ObbScanner"
+
+#include <utils/Log.h>
+#include <utils/String8.h>
+#include <utils/ObbFile.h>
+
+#include "jni.h"
+#include "utils/misc.h"
+#include "android_runtime/AndroidRuntime.h"
+
+namespace android {
+
+static struct {
+ jclass clazz;
+
+ jfieldID packageName;
+ jfieldID version;
+} gObbInfoClassInfo;
+
+static jboolean android_content_res_ObbScanner_getObbInfo(JNIEnv* env, jobject clazz, jstring file,
+ jobject obbInfo)
+{
+ const char* filePath = env->GetStringUTFChars(file, JNI_FALSE);
+
+ sp<ObbFile> obb = new ObbFile();
+ if (!obb->readFrom(filePath)) {
+ env->ReleaseStringUTFChars(file, filePath);
+ return JNI_FALSE;
+ }
+
+ env->ReleaseStringUTFChars(file, filePath);
+
+ const char* packageNameStr = obb->getPackageName().string();
+
+ jstring packageName = env->NewStringUTF(packageNameStr);
+ if (packageName == NULL) {
+ return JNI_FALSE;
+ }
+
+ env->SetObjectField(obbInfo, gObbInfoClassInfo.packageName, packageName);
+ env->SetIntField(obbInfo, gObbInfoClassInfo.version, obb->getVersion());
+
+ return JNI_TRUE;
+}
+
+/*
+ * JNI registration.
+ */
+static JNINativeMethod gMethods[] = {
+ /* name, signature, funcPtr */
+ { "getObbInfo_native", "(Ljava/lang/String;Landroid/content/res/ObbInfo;)Z",
+ (void*) android_content_res_ObbScanner_getObbInfo },
+};
+
+#define FIND_CLASS(var, className) \
+ var = env->FindClass(className); \
+ LOG_FATAL_IF(! var, "Unable to find class " className); \
+ var = jclass(env->NewGlobalRef(var));
+
+#define GET_FIELD_ID(var, clazz, fieldName, fieldDescriptor) \
+ var = env->GetFieldID(clazz, fieldName, fieldDescriptor); \
+ LOG_FATAL_IF(! var, "Unable to find field " fieldName);
+
+int register_android_content_res_ObbScanner(JNIEnv* env)
+{
+ FIND_CLASS(gObbInfoClassInfo.clazz, "android/content/res/ObbInfo");
+
+ GET_FIELD_ID(gObbInfoClassInfo.packageName, gObbInfoClassInfo.clazz,
+ "packageName", "Ljava/lang/String;");
+ GET_FIELD_ID(gObbInfoClassInfo.version, gObbInfoClassInfo.clazz,
+ "version", "I");
+
+ return AndroidRuntime::registerNativeMethods(env, "android/content/res/ObbScanner", gMethods,
+ NELEM(gMethods));
+}
+
+}; // namespace android
+
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index 6c2f1b2..d7b92ec 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -23,11 +23,14 @@ import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
+import android.content.res.ObbInfo;
+import android.content.res.ObbScanner;
import android.net.Uri;
import android.os.storage.IMountService;
import android.os.storage.IMountServiceListener;
import android.os.storage.IMountShutdownObserver;
import android.os.storage.StorageResultCode;
+import android.os.Binder;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
@@ -53,7 +56,8 @@ class MountService extends IMountService.Stub
private static final boolean LOCAL_LOGD = false;
private static final boolean DEBUG_UNMOUNT = false;
private static final boolean DEBUG_EVENTS = false;
-
+ private static final boolean DEBUG_OBB = true;
+
private static final String TAG = "MountService";
/*
@@ -130,6 +134,12 @@ class MountService extends IMountService.Stub
*/
final private HashSet<String> mAsecMountSet = new HashSet<String>();
+ /**
+ * Private hash of currently mounted filesystem images.
+ */
+ final private HashSet<String> mObbMountSet = new HashSet<String>();
+
+ // Handler messages
private static final int H_UNMOUNT_PM_UPDATE = 1;
private static final int H_UNMOUNT_PM_DONE = 2;
private static final int H_UNMOUNT_MS = 3;
@@ -287,7 +297,7 @@ class MountService extends IMountService.Stub
Slog.w(TAG, "Waiting too long for mReady!");
}
}
-
+
private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
@@ -344,7 +354,7 @@ class MountService extends IMountService.Stub
MountServiceBinderListener(IMountServiceListener listener) {
mListener = listener;
-
+
}
public void binderDied() {
@@ -1327,5 +1337,145 @@ class MountService extends IMountService.Stub
public void finishMediaUpdate() {
mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
}
+
+ private boolean isCallerOwnerOfPackage(String packageName) {
+ final int callerUid = Binder.getCallingUid();
+ return isUidOwnerOfPackage(packageName, callerUid);
+ }
+
+ private boolean isUidOwnerOfPackage(String packageName, int callerUid) {
+ if (packageName == null) {
+ return false;
+ }
+
+ final int packageUid = mPms.getPackageUid(packageName);
+
+ if (DEBUG_OBB) {
+ Slog.d(TAG, "packageName = " + packageName + ", packageUid = " +
+ packageUid + ", callerUid = " + callerUid);
+ }
+
+ return callerUid == packageUid;
+ }
+
+ public String getMountedObbPath(String filename) {
+ waitForReady();
+ warnOnNotMounted();
+
+ // XXX replace with call to IMediaContainerService
+ ObbInfo obbInfo = ObbScanner.getObbInfo(filename);
+ if (!isCallerOwnerOfPackage(obbInfo.packageName)) {
+ throw new IllegalArgumentException("Caller package does not match OBB file");
+ }
+
+ try {
+ ArrayList<String> rsp = mConnector.doCommand(String.format("obb path %s", filename));
+ String []tok = rsp.get(0).split(" ");
+ int code = Integer.parseInt(tok[0]);
+ if (code != VoldResponseCode.AsecPathResult) {
+ throw new IllegalStateException(String.format("Unexpected response code %d", code));
+ }
+ return tok[1];
+ } catch (NativeDaemonConnectorException e) {
+ int code = e.getCode();
+ if (code == VoldResponseCode.OpFailedStorageNotFound) {
+ throw new IllegalArgumentException(String.format("OBB '%s' not found", filename));
+ } else {
+ throw new IllegalStateException(String.format("Unexpected response code %d", code));
+ }
+ }
+ }
+
+ public boolean isObbMounted(String filename) {
+ waitForReady();
+ warnOnNotMounted();
+
+ // XXX replace with call to IMediaContainerService
+ ObbInfo obbInfo = ObbScanner.getObbInfo(filename);
+ if (!isCallerOwnerOfPackage(obbInfo.packageName)) {
+ throw new IllegalArgumentException("Caller package does not match OBB file");
+ }
+
+ synchronized (mObbMountSet) {
+ return mObbMountSet.contains(filename);
+ }
+ }
+
+ public int mountObb(String filename, String key) {
+ waitForReady();
+ warnOnNotMounted();
+
+ synchronized (mObbMountSet) {
+ if (mObbMountSet.contains(filename)) {
+ return StorageResultCode.OperationFailedStorageMounted;
+ }
+ }
+
+ final int callerUid = Binder.getCallingUid();
+
+ // XXX replace with call to IMediaContainerService
+ ObbInfo obbInfo = ObbScanner.getObbInfo(filename);
+ if (!isUidOwnerOfPackage(obbInfo.packageName, callerUid)) {
+ throw new IllegalArgumentException("Caller package does not match OBB file");
+ }
+
+ if (key == null) {
+ key = "none";
+ }
+
+ int rc = StorageResultCode.OperationSucceeded;
+ String cmd = String.format("obb mount %s %s %d", filename, key, callerUid);
+ try {
+ mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ int code = e.getCode();
+ if (code != VoldResponseCode.OpFailedStorageBusy) {
+ rc = StorageResultCode.OperationFailedInternalError;
+ }
+ }
+
+ if (rc == StorageResultCode.OperationSucceeded) {
+ synchronized (mObbMountSet) {
+ mObbMountSet.add(filename);
+ }
+ }
+ return rc;
+ }
+
+ public int unmountObb(String filename, boolean force) {
+ waitForReady();
+ warnOnNotMounted();
+
+ ObbInfo obbInfo = ObbScanner.getObbInfo(filename);
+ if (!isCallerOwnerOfPackage(obbInfo.packageName)) {
+ throw new IllegalArgumentException("Caller package does not match OBB file");
+ }
+
+ synchronized (mObbMountSet) {
+ if (!mObbMountSet.contains(filename)) {
+ return StorageResultCode.OperationFailedStorageNotMounted;
+ }
+ }
+
+ int rc = StorageResultCode.OperationSucceeded;
+ String cmd = String.format("obb unmount %s%s", filename, (force ? " force" : ""));
+ try {
+ mConnector.doCommand(cmd);
+ } catch (NativeDaemonConnectorException e) {
+ int code = e.getCode();
+ if (code == VoldResponseCode.OpFailedStorageBusy) {
+ rc = StorageResultCode.OperationFailedStorageBusy;
+ } else {
+ rc = StorageResultCode.OperationFailedInternalError;
+ }
+ }
+
+ if (rc == StorageResultCode.OperationSucceeded) {
+ synchronized (mObbMountSet) {
+ mObbMountSet.remove(filename);
+ }
+ }
+ return rc;
+ }
}