summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorKenny Root <kroot@google.com>2010-09-22 17:29:43 -0700
committerKenny Root <kroot@google.com>2010-09-28 17:23:26 -0700
commit05105f7abe02b2dff91d6260b3628c8b97816bab (patch)
treef42676d818548d76b2c55045a1bcc4866d6feec1
parentea2cf2f936b03f1720bc43863e41c89ea2a7903a (diff)
downloadframeworks_base-05105f7abe02b2dff91d6260b3628c8b97816bab.zip
frameworks_base-05105f7abe02b2dff91d6260b3628c8b97816bab.tar.gz
frameworks_base-05105f7abe02b2dff91d6260b3628c8b97816bab.tar.bz2
Update OBB API to include callbacks
Add a callback for users of the StorageManager API to be able to receive notifications when the requested operation completes for mountObb and unmountObb. Add NDK API to get to ObbInfo like the Java API has. Also update the docs for the API and remove the "STOPSHIP" comments. Change-Id: I23a4409c7f8b74d3169614beba920b4d667990a4
-rw-r--r--api/current.xml148
-rw-r--r--core/java/android/content/res/ObbInfo.java9
-rw-r--r--core/java/android/content/res/ObbScanner.java34
-rw-r--r--core/java/android/os/storage/OnObbStateChangeListener.java31
-rw-r--r--core/java/android/os/storage/StorageManager.java167
-rw-r--r--core/jni/android_content_res_ObbScanner.cpp23
-rw-r--r--include/storage/IMountService.h3
-rw-r--r--libs/storage/IMountService.cpp6
-rw-r--r--native/android/Android.mk1
-rw-r--r--native/android/obb.cpp54
-rw-r--r--native/android/storage_manager.cpp31
-rw-r--r--native/include/android/obb.h63
-rw-r--r--native/include/android/storage_manager.h15
-rw-r--r--packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java7
-rw-r--r--services/java/com/android/server/MountService.java61
15 files changed, 576 insertions, 77 deletions
diff --git a/api/current.xml b/api/current.xml
index 179c515..a37a533 100644
--- a/api/current.xml
+++ b/api/current.xml
@@ -52619,6 +52619,118 @@
>
</field>
</class>
+<class name="ObbInfo"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<implements name="android.os.Parcelable">
+</implements>
+<method name="describeContents"
+ return="int"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</method>
+<method name="writeToParcel"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="dest" type="android.os.Parcel">
+</parameter>
+<parameter name="parcelableFlags" type="int">
+</parameter>
+</method>
+<field name="CREATOR"
+ type="android.os.Parcelable.Creator"
+ transient="false"
+ volatile="false"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="OBB_OVERLAY"
+ type="int"
+ transient="false"
+ volatile="false"
+ value="1"
+ static="true"
+ final="true"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="flags"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="packageName"
+ type="java.lang.String"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+<field name="version"
+ type="int"
+ transient="false"
+ volatile="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</field>
+</class>
+<class name="ObbScanner"
+ extends="java.lang.Object"
+ abstract="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<method name="getObbInfo"
+ return="android.content.res.ObbInfo"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="true"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="filePath" type="java.lang.String">
+</parameter>
+<exception name="IOException" type="java.io.IOException">
+</exception>
+</method>
+</class>
<class name="Resources"
extends="java.lang.Object"
abstract="false"
@@ -129032,6 +129144,38 @@
</package>
<package name="android.os.storage"
>
+<class name="OnObbStateChangeListener"
+ extends="java.lang.Object"
+ abstract="true"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<constructor name="OnObbStateChangeListener"
+ type="android.os.storage.OnObbStateChangeListener"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+</constructor>
+<method name="onObbStateChange"
+ return="void"
+ abstract="false"
+ native="false"
+ synchronized="false"
+ static="false"
+ final="false"
+ deprecated="not deprecated"
+ visibility="public"
+>
+<parameter name="path" type="java.lang.String">
+</parameter>
+<parameter name="state" type="java.lang.String">
+</parameter>
+</method>
+</class>
<class name="StorageManager"
extends="java.lang.Object"
abstract="false"
@@ -129082,6 +129226,8 @@
</parameter>
<parameter name="key" type="java.lang.String">
</parameter>
+<parameter name="listener" type="android.os.storage.OnObbStateChangeListener">
+</parameter>
</method>
<method name="unmountObb"
return="boolean"
@@ -129097,6 +129243,8 @@
</parameter>
<parameter name="force" type="boolean">
</parameter>
+<parameter name="listener" type="android.os.storage.OnObbStateChangeListener">
+</parameter>
<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
</exception>
</method>
diff --git a/core/java/android/content/res/ObbInfo.java b/core/java/android/content/res/ObbInfo.java
index 838c5ff..7b962e5 100644
--- a/core/java/android/content/res/ObbInfo.java
+++ b/core/java/android/content/res/ObbInfo.java
@@ -20,9 +20,9 @@ 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
+ * Basic information about a Opaque Binary Blob (OBB) that reflects the info
+ * from the footer on the OBB file. This information may be manipulated by a
+ * developer with the <code>obbtool</code> program in the Android SDK.
*/
public class ObbInfo implements Parcelable {
/** Flag noting that this OBB is an overlay patch for a base OBB. */
@@ -43,7 +43,8 @@ public class ObbInfo implements Parcelable {
*/
public int flags;
- public ObbInfo() {
+ // Only allow things in this package to instantiate.
+ /* package */ ObbInfo() {
}
public String toString() {
diff --git a/core/java/android/content/res/ObbScanner.java b/core/java/android/content/res/ObbScanner.java
index eb383c3..a3f141e 100644
--- a/core/java/android/content/res/ObbScanner.java
+++ b/core/java/android/content/res/ObbScanner.java
@@ -16,25 +16,43 @@
package android.content.res;
+import java.io.File;
+import java.io.IOException;
+
/**
- * Class to scan Opaque Binary Blob (OBB) files.
- * @hide
+ * Class to scan Opaque Binary Blob (OBB) files. Use this to get information
+ * about an OBB file for use in a program via {@link ObbInfo}.
*/
public class ObbScanner {
// Don't allow others to instantiate this class
private ObbScanner() {}
- public static ObbInfo getObbInfo(String filePath) {
+ /**
+ * Scan a file for OBB information.
+ *
+ * @param filePath path to the OBB file to be scanned.
+ * @return ObbInfo object information corresponding to the file path
+ * @throws IllegalArgumentException if the OBB file couldn't be found
+ * @throws IOException if the OBB file couldn't be read
+ */
+ public static ObbInfo getObbInfo(String filePath) throws IOException {
if (filePath == null) {
- return null;
+ throw new IllegalArgumentException("file path cannot be null");
}
- ObbInfo obbInfo = new ObbInfo();
- if (!getObbInfo_native(filePath, obbInfo)) {
- throw new IllegalArgumentException("Could not read OBB file: " + filePath);
+ final File obbFile = new File(filePath);
+ if (!obbFile.exists()) {
+ throw new IllegalArgumentException("OBB file does nto exist: " + filePath);
}
+
+ final String canonicalFilePath = obbFile.getCanonicalPath();
+
+ ObbInfo obbInfo = new ObbInfo();
+ getObbInfo_native(canonicalFilePath, obbInfo);
+
return obbInfo;
}
- private native static boolean getObbInfo_native(String filePath, ObbInfo obbInfo);
+ private native static void getObbInfo_native(String filePath, ObbInfo obbInfo)
+ throws IOException;
}
diff --git a/core/java/android/os/storage/OnObbStateChangeListener.java b/core/java/android/os/storage/OnObbStateChangeListener.java
new file mode 100644
index 0000000..a2d0a56
--- /dev/null
+++ b/core/java/android/os/storage/OnObbStateChangeListener.java
@@ -0,0 +1,31 @@
+/*
+ * 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.os.storage;
+
+/**
+ * Used for receiving notifications from {@link StorageManager}.
+ */
+public abstract class OnObbStateChangeListener {
+ /**
+ * Called when an OBB has changed states.
+ *
+ * @param path path to the OBB file the state change has happened on
+ * @param state the current state of the OBB
+ */
+ public void onObbStateChange(String path, String state) {
+ }
+}
diff --git a/core/java/android/os/storage/StorageManager.java b/core/java/android/os/storage/StorageManager.java
index df0b69c..14da00a 100644
--- a/core/java/android/os/storage/StorageManager.java
+++ b/core/java/android/os/storage/StorageManager.java
@@ -23,13 +23,28 @@ import android.os.RemoteException;
import android.os.ServiceManager;
import android.util.Log;
+import java.lang.ref.WeakReference;
import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
/**
- * StorageManager is the interface to the systems storage service.
+ * StorageManager is the interface to the systems storage service. The storage
+ * manager handles storage-related items such as Opaque Binary Blobs (OBBs).
+ * <p>
+ * OBBs contain a filesystem that maybe be encrypted on disk and mounted
+ * on-demand from an application. OBBs are a good way of providing large amounts
+ * of binary assets without packaging them into APKs as they may be multiple
+ * gigabytes in size. However, due to their size, they're most likely stored in
+ * a shared storage pool accessible from all programs. The system does not
+ * guarantee the security of the OBB file itself: if any program modifies the
+ * OBB, there is no guarantee that a read from that OBB will produce the
+ * expected output.
+ * <p>
* 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}.
+ * {@link android.content.Context#getSystemService(java.lang.String)} with an
+ * argument of {@link android.content.Context#STORAGE_SERVICE}.
*/
public class StorageManager
@@ -75,11 +90,113 @@ public class StorageManager
/**
* Binder listener for OBB action results.
*/
- private final ObbActionBinderListener mObbActionListener = new ObbActionBinderListener();
- private class ObbActionBinderListener extends IObbActionListener.Stub {
+ private final ObbActionListener mObbActionListener = new ObbActionListener();
+
+ private class ObbActionListener extends IObbActionListener.Stub {
+ private List<WeakReference<ObbListenerDelegate>> mListeners = new LinkedList<WeakReference<ObbListenerDelegate>>();
+
@Override
public void onObbResult(String filename, String status) throws RemoteException {
- Log.i(TAG, "filename = " + filename + ", result = " + status);
+ synchronized (mListeners) {
+ final Iterator<WeakReference<ObbListenerDelegate>> iter = mListeners.iterator();
+ while (iter.hasNext()) {
+ final WeakReference<ObbListenerDelegate> ref = iter.next();
+
+ final ObbListenerDelegate delegate = (ref == null) ? null : ref.get();
+ if (delegate == null) {
+ iter.remove();
+ continue;
+ }
+
+ delegate.sendObbStateChanged(filename, status);
+ }
+ }
+ }
+
+ public void addListener(OnObbStateChangeListener listener) {
+ if (listener == null) {
+ return;
+ }
+
+ synchronized (mListeners) {
+ final Iterator<WeakReference<ObbListenerDelegate>> iter = mListeners.iterator();
+ while (iter.hasNext()) {
+ final WeakReference<ObbListenerDelegate> ref = iter.next();
+
+ final ObbListenerDelegate delegate = (ref == null) ? null : ref.get();
+ if (delegate == null) {
+ iter.remove();
+ continue;
+ }
+
+ /*
+ * If we're already in the listeners, we don't need to be in
+ * there again.
+ */
+ if (listener.equals(delegate.getListener())) {
+ return;
+ }
+ }
+
+ final ObbListenerDelegate delegate = new ObbListenerDelegate(listener);
+ mListeners.add(new WeakReference<ObbListenerDelegate>(delegate));
+ }
+ }
+ }
+
+ /**
+ * Private class containing sender and receiver code for StorageEvents.
+ */
+ private class ObbListenerDelegate {
+ private final WeakReference<OnObbStateChangeListener> mObbEventListenerRef;
+ private final Handler mHandler;
+
+ ObbListenerDelegate(OnObbStateChangeListener listener) {
+ mObbEventListenerRef = new WeakReference<OnObbStateChangeListener>(listener);
+ mHandler = new Handler(mTgtLooper) {
+ @Override
+ public void handleMessage(Message msg) {
+ final OnObbStateChangeListener listener = getListener();
+ if (listener == null) {
+ return;
+ }
+
+ StorageEvent e = (StorageEvent) msg.obj;
+
+ if (msg.what == StorageEvent.EVENT_OBB_STATE_CHANGED) {
+ ObbStateChangedStorageEvent ev = (ObbStateChangedStorageEvent) e;
+ listener.onObbStateChange(ev.path, ev.state);
+ } else {
+ Log.e(TAG, "Unsupported event " + msg.what);
+ }
+ }
+ };
+ }
+
+ OnObbStateChangeListener getListener() {
+ if (mObbEventListenerRef == null) {
+ return null;
+ }
+ return mObbEventListenerRef.get();
+ }
+
+ void sendObbStateChanged(String path, String state) {
+ ObbStateChangedStorageEvent e = new ObbStateChangedStorageEvent(path, state);
+ mHandler.sendMessage(e.getMessage());
+ }
+ }
+
+ /**
+ * Message sent during an OBB status change event.
+ */
+ private class ObbStateChangedStorageEvent extends StorageEvent {
+ public final String path;
+ public final String state;
+
+ public ObbStateChangedStorageEvent(String path, String state) {
+ super(EVENT_OBB_STATE_CHANGED);
+ this.path = path;
+ this.state = state;
}
}
@@ -88,8 +205,9 @@ public class StorageManager
* and the target looper handler.
*/
private class StorageEvent {
- public static final int EVENT_UMS_CONNECTION_CHANGED = 1;
- public static final int EVENT_STORAGE_STATE_CHANGED = 2;
+ static final int EVENT_UMS_CONNECTION_CHANGED = 1;
+ static final int EVENT_STORAGE_STATE_CHANGED = 2;
+ static final int EVENT_OBB_STATE_CHANGED = 3;
private Message mMessage;
@@ -300,19 +418,27 @@ public class StorageManager
* specified, it is supplied to the mounting process to be used in any
* encryption used in the OBB.
* <p>
+ * The OBB will remain mounted for as long as the StorageManager reference
+ * is held by the application. As soon as this reference is lost, the OBBs
+ * in use will be unmounted. The {@link OnObbStateChangeListener} registered with
+ * this call will receive all further OBB-related events until it goes out
+ * of scope. If the caller is not interested in whether the call succeeds,
+ * the <code>listener</code> may be specified as <code>null</code>.
+ * <p>
* <em>Note:</em> you can only mount OBB files for which the OBB tag on the
* file matches a package ID that is owned by the calling program's UID.
- * That is, shared UID applications can obtain access to any other
+ * That is, shared UID applications can attempt to mount any other
* application's OBB that shares its UID.
- * <p>
- * STOPSHIP document more; discuss lack of guarantees of security
*
* @param filename the path to the OBB file
- * @param key decryption key
+ * @param key secret used to encrypt the OBB; may be <code>null</code> if no
+ * encryption was used on the OBB.
* @return whether the mount call was successfully queued or not
+ * @throws IllegalArgumentException when the OBB is already mounted
*/
- public boolean mountObb(String filename, String key) {
+ public boolean mountObb(String filename, String key, OnObbStateChangeListener listener) {
try {
+ mObbActionListener.addListener(listener);
mMountService.mountObb(filename, key, mObbActionListener);
return true;
} catch (RemoteException e) {
@@ -323,15 +449,20 @@ public class StorageManager
}
/**
- * Unmount an Opaque Binary Blob (OBB) file. If the <code>force</code> flag
- * is true, it will kill any application needed to unmount the given OBB.
+ * Unmount an Opaque Binary Blob (OBB) file asynchronously. If the
+ * <code>force</code> flag is true, it will kill any application needed to
+ * unmount the given OBB (even the calling application).
+ * <p>
+ * The {@link OnObbStateChangeListener} registered with this call will receive all
+ * further OBB-related events until it goes out of scope. If the caller is
+ * not interested in whether the call succeeded, the listener may be
+ * specified as <code>null</code>.
* <p>
* <em>Note:</em> you can only mount OBB files for which the OBB tag on the
* file matches a package ID that is owned by the calling program's UID.
* That is, shared UID applications can obtain access to any other
* application's OBB that shares its UID.
* <p>
- * STOPSHIP document more; discuss lack of guarantees of security
*
* @param filename path to the OBB file
* @param force whether to kill any programs using this in order to unmount
@@ -339,8 +470,10 @@ public class StorageManager
* @return whether the unmount call was successfully queued or not
* @throws IllegalArgumentException when OBB is not already mounted
*/
- public boolean unmountObb(String filename, boolean force) throws IllegalArgumentException {
+ public boolean unmountObb(String filename, boolean force, OnObbStateChangeListener listener)
+ throws IllegalArgumentException {
try {
+ mObbActionListener.addListener(listener);
mMountService.unmountObb(filename, force, mObbActionListener);
return true;
} catch (RemoteException e) {
diff --git a/core/jni/android_content_res_ObbScanner.cpp b/core/jni/android_content_res_ObbScanner.cpp
index 62c89fc..2a9eacf 100644
--- a/core/jni/android_content_res_ObbScanner.cpp
+++ b/core/jni/android_content_res_ObbScanner.cpp
@@ -34,7 +34,17 @@ static struct {
jfieldID flags;
} gObbInfoClassInfo;
-static jboolean android_content_res_ObbScanner_getObbInfo(JNIEnv* env, jobject clazz, jstring file,
+static void doThrow(JNIEnv* env, const char* exc, const char* msg = NULL)
+{
+ jclass npeClazz;
+
+ npeClazz = env->FindClass(exc);
+ LOG_FATAL_IF(npeClazz == NULL, "Unable to find class %s", exc);
+
+ env->ThrowNew(npeClazz, msg);
+}
+
+static void android_content_res_ObbScanner_getObbInfo(JNIEnv* env, jobject clazz, jstring file,
jobject obbInfo)
{
const char* filePath = env->GetStringUTFChars(file, JNI_FALSE);
@@ -42,7 +52,8 @@ static jboolean android_content_res_ObbScanner_getObbInfo(JNIEnv* env, jobject c
sp<ObbFile> obb = new ObbFile();
if (!obb->readFrom(filePath)) {
env->ReleaseStringUTFChars(file, filePath);
- return JNI_FALSE;
+ doThrow(env, "java/io/IOException", "Could not read OBB file");
+ return;
}
env->ReleaseStringUTFChars(file, filePath);
@@ -51,13 +62,13 @@ static jboolean android_content_res_ObbScanner_getObbInfo(JNIEnv* env, jobject c
jstring packageName = env->NewStringUTF(packageNameStr);
if (packageName == NULL) {
- return JNI_FALSE;
+ doThrow(env, "java/io/IOException", "Could not read OBB file");
+ return;
}
env->SetObjectField(obbInfo, gObbInfoClassInfo.packageName, packageName);
env->SetIntField(obbInfo, gObbInfoClassInfo.version, obb->getVersion());
-
- return JNI_TRUE;
+ env->SetIntField(obbInfo, gObbInfoClassInfo.flags, obb->getFlags());
}
/*
@@ -65,7 +76,7 @@ static jboolean android_content_res_ObbScanner_getObbInfo(JNIEnv* env, jobject c
*/
static JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
- { "getObbInfo_native", "(Ljava/lang/String;Landroid/content/res/ObbInfo;)Z",
+ { "getObbInfo_native", "(Ljava/lang/String;Landroid/content/res/ObbInfo;)V",
(void*) android_content_res_ObbScanner_getObbInfo },
};
diff --git a/include/storage/IMountService.h b/include/storage/IMountService.h
index a2735a4..436fc38 100644
--- a/include/storage/IMountService.h
+++ b/include/storage/IMountService.h
@@ -62,7 +62,8 @@ public:
virtual void finishMediaUpdate() = 0;
virtual void mountObb(const String16& filename, const String16& key,
const sp<IObbActionListener>& token) = 0;
- virtual void unmountObb(const String16& filename, const bool force) = 0;
+ virtual void unmountObb(const String16& filename, const bool force,
+ const sp<IObbActionListener>& token) = 0;
virtual bool isObbMounted(const String16& filename) = 0;
virtual bool getMountedObbPath(const String16& filename, String16& path) = 0;
};
diff --git a/libs/storage/IMountService.cpp b/libs/storage/IMountService.cpp
index 902bb27..3ad9319 100644
--- a/libs/storage/IMountService.cpp
+++ b/libs/storage/IMountService.cpp
@@ -429,8 +429,8 @@ public:
reply.readExceptionCode();
}
- void mountObb(const String16& filename, const String16& key, const sp<
- IObbActionListener>& token)
+ void mountObb(const String16& filename, const String16& key,
+ const sp<IObbActionListener>& token)
{
Parcel data, reply;
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
@@ -448,7 +448,7 @@ public:
}
}
- void unmountObb(const String16& filename, const bool force)
+ void unmountObb(const String16& filename, const bool force, const sp<IObbActionListener>& token)
{
Parcel data, reply;
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
diff --git a/native/android/Android.mk b/native/android/Android.mk
index cc35a3a..44ec83f 100644
--- a/native/android/Android.mk
+++ b/native/android/Android.mk
@@ -12,6 +12,7 @@ LOCAL_SRC_FILES:= \
looper.cpp \
native_activity.cpp \
native_window.cpp \
+ obb.cpp \
sensor.cpp \
storage_manager.cpp
diff --git a/native/android/obb.cpp b/native/android/obb.cpp
new file mode 100644
index 0000000..e0cb1a6
--- /dev/null
+++ b/native/android/obb.cpp
@@ -0,0 +1,54 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "NObb"
+
+#include <android/obb.h>
+
+#include <utils/Log.h>
+#include <utils/ObbFile.h>
+
+using namespace android;
+
+struct AObbInfo : public ObbFile {};
+
+AObbInfo* AObbScanner_getObbInfo(const char* filename) {
+ AObbInfo* obbFile = new AObbInfo();
+ if (obbFile == NULL || !obbFile->readFrom(filename)) {
+ delete obbFile;
+ return NULL;
+ }
+ obbFile->incStrong((void*)AObbScanner_getObbInfo);
+ return static_cast<AObbInfo*>(obbFile);
+}
+
+void AObbInfo_delete(AObbInfo* obbInfo) {
+ if (obbInfo != NULL) {
+ obbInfo->decStrong((void*)AObbScanner_getObbInfo);
+ }
+}
+
+const char* AObbInfo_getPackageName(AObbInfo* obbInfo) {
+ return obbInfo->getPackageName();
+}
+
+int32_t AObbInfo_getVersion(AObbInfo* obbInfo) {
+ return obbInfo->getVersion();
+}
+
+int32_t AObbInfo_getFlags(AObbInfo* obbInfo) {
+ return obbInfo->getFlags();
+}
diff --git a/native/android/storage_manager.cpp b/native/android/storage_manager.cpp
index 6dbe746..2f20641 100644
--- a/native/android/storage_manager.cpp
+++ b/native/android/storage_manager.cpp
@@ -38,20 +38,20 @@ public:
mStorageManager(mgr)
{}
- virtual void onObbResult(const android::String16& filename, const android::String16& state) {
- LOGD("Got obb result (%s, %s)\n", String8(filename).string(), String8(state).string());
- }
+ virtual void onObbResult(const android::String16& filename, const android::String16& state);
};
struct AStorageManager : public RefBase {
protected:
- void* mObbCallback;
+ AStorageManager_obbCallbackFunc mObbCallback;
+ void* mObbCallbackData;
sp<ObbActionListener> mObbActionListener;
sp<IMountService> mMountService;
public:
- AStorageManager() :
- mObbCallback(NULL)
+ AStorageManager()
+ : mObbCallback(NULL)
+ , mObbCallbackData(NULL)
{
}
@@ -73,8 +73,15 @@ public:
return true;
}
- void setObbCallback(void* cb) {
+ void setObbCallback(AStorageManager_obbCallbackFunc cb, void* data) {
mObbCallback = cb;
+ mObbCallbackData = data;
+ }
+
+ void fireCallback(const char* filename, const char* state) {
+ if (mObbCallback != NULL) {
+ mObbCallback(filename, state, mObbCallbackData);
+ }
}
void mountObb(const char* filename, const char* key) {
@@ -85,7 +92,7 @@ public:
void unmountObb(const char* filename, const bool force) {
String16 filename16(filename);
- mMountService->unmountObb(filename16, force);
+ mMountService->unmountObb(filename16, force, mObbActionListener);
}
int isObbMounted(const char* filename) {
@@ -104,6 +111,10 @@ public:
}
};
+void ObbActionListener::onObbResult(const android::String16& filename, const android::String16& state) {
+ mStorageManager->fireCallback(String8(filename).string(), String8(state).string());
+}
+
AStorageManager* AStorageManager_new() {
sp<AStorageManager> mgr = new AStorageManager();
@@ -120,8 +131,8 @@ void AStorageManager_delete(AStorageManager* mgr) {
}
}
-void AStorageManager_setObbCallback(AStorageManager* mgr, void* cb) {
- mgr->setObbCallback(cb);
+void AStorageManager_setObbCallback(AStorageManager* mgr, AStorageManager_obbCallbackFunc cb, void* data) {
+ mgr->setObbCallback(cb, data);
}
void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key) {
diff --git a/native/include/android/obb.h b/native/include/android/obb.h
new file mode 100644
index 0000000..65e9b2a
--- /dev/null
+++ b/native/include/android/obb.h
@@ -0,0 +1,63 @@
+/*
+ * 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.
+ */
+
+
+#ifndef ANDROID_OBB_H
+#define ANDROID_OBB_H
+
+#include <sys/types.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct AObbInfo;
+typedef struct AObbInfo AObbInfo;
+
+enum {
+ AOBBINFO_OVERLAY = 0x0001,
+};
+
+/**
+ * Scan an OBB and get information about it.
+ */
+AObbInfo* AObbScanner_getObbInfo(const char* filename);
+
+/**
+ * Destroy the AObbInfo object. You must call this when finished with the object.
+ */
+void AObbInfo_delete(AObbInfo* obbInfo);
+
+/**
+ * Get the package name for the OBB.
+ */
+const char* AObbInfo_getPackageName(AObbInfo* obbInfo);
+
+/**
+ * Get the version of an OBB file.
+ */
+int32_t AObbInfo_getVersion(AObbInfo* obbInfo);
+
+/**
+ * Get the flags of an OBB file.
+ */
+int32_t AObbInfo_getFlags(AObbInfo* obbInfo);
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif // ANDROID_OBB_H
diff --git a/native/include/android/storage_manager.h b/native/include/android/storage_manager.h
index bbed8a4..6f925c1 100644
--- a/native/include/android/storage_manager.h
+++ b/native/include/android/storage_manager.h
@@ -37,17 +37,22 @@ AStorageManager* AStorageManager_new();
void AStorageManager_delete(AStorageManager* mgr);
/**
- * Callback to call when requested OBB is complete.
+ * Callback function for asynchronous calls made on OBB files.
*/
-void AStorageManager_setObbCallback(AStorageManager* mgr, void* cb);
+typedef void (*AStorageManager_obbCallbackFunc)(const char* filename, const char* state, void* data);
/**
- * Attempts to mount an OBB file.
+ * Callback to call when requested asynchronous OBB operation is complete.
+ */
+void AStorageManager_setObbCallback(AStorageManager* mgr, AStorageManager_obbCallbackFunc cb, void* data);
+
+/**
+ * Attempts to mount an OBB file. This is an asynchronous operation.
*/
void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key);
/**
- * Attempts to unmount an OBB file.
+ * Attempts to unmount an OBB file. This is an asynchronous operation.
*/
void AStorageManager_unmountObb(AStorageManager* mgr, const char* filename, const int force);
@@ -66,4 +71,4 @@ const char* AStorageManager_getMountedObbPath(AStorageManager* mgr, const char*
};
#endif
-#endif // ANDROID_PACKAGE_MANAGER_H
+#endif // ANDROID_STORAGE_MANAGER_H
diff --git a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
index f08bd3c..eb86277 100644
--- a/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
+++ b/packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java
@@ -156,7 +156,12 @@ public class DefaultContainerService extends IntentService {
}
public ObbInfo getObbInfo(String filename) {
- return ObbScanner.getObbInfo(filename);
+ try {
+ return ObbScanner.getObbInfo(filename);
+ } catch (IOException e) {
+ Log.d(TAG, "Couldn't get OBB info", e);
+ return null;
+ }
}
};
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index cfba07a..f3625a8 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -46,6 +46,7 @@ import android.os.storage.IObbActionListener;
import android.os.storage.StorageResultCode;
import android.util.Slog;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
@@ -148,7 +149,7 @@ class MountService extends IMountService.Stub
* Mounted OBB tracking information. Used to track the current state of all
* OBBs.
*/
- final private Map<IObbActionListener, ObbState> mObbMounts = new HashMap<IObbActionListener, ObbState>();
+ final private Map<IObbActionListener, List<ObbState>> mObbMounts = new HashMap<IObbActionListener, List<ObbState>>();
final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
class ObbState implements IBinder.DeathRecipient {
@@ -160,13 +161,13 @@ class MountService extends IMountService.Stub
}
// OBB source filename
- String filename;
+ final String filename;
// Token of remote Binder caller
- IObbActionListener token;
+ final IObbActionListener token;
// Binder.callingUid()
- public int callerUid;
+ final public int callerUid;
// Whether this is mounted currently.
boolean mounted;
@@ -225,9 +226,9 @@ class MountService extends IMountService.Stub
private static final int MAX_UNMOUNT_RETRIES = 4;
class UnmountCallBack {
- String path;
+ final String path;
+ final boolean force;
int retries;
- boolean force;
UnmountCallBack(String path, boolean force) {
retries = 0;
@@ -242,7 +243,7 @@ class MountService extends IMountService.Stub
}
class UmsEnableCallBack extends UnmountCallBack {
- String method;
+ final String method;
UmsEnableCallBack(String path, String method, boolean force) {
super(path, force);
@@ -1513,10 +1514,6 @@ class MountService extends IMountService.Stub
throw new IllegalArgumentException("OBB file is already mounted");
}
- if (mObbMounts.containsKey(token)) {
- throw new IllegalArgumentException("You may only have one OBB mounted at a time");
- }
-
final int callerUid = Binder.getCallingUid();
obbState = new ObbState(filename, token, callerUid);
addObbState(obbState);
@@ -1554,14 +1551,25 @@ class MountService extends IMountService.Stub
private void addObbState(ObbState obbState) {
synchronized (mObbMounts) {
- mObbMounts.put(obbState.token, obbState);
+ List<ObbState> obbStates = mObbMounts.get(obbState.token);
+ if (obbStates == null) {
+ obbStates = new ArrayList<ObbState>();
+ mObbMounts.put(obbState.token, obbStates);
+ }
+ obbStates.add(obbState);
mObbPathToStateMap.put(obbState.filename, obbState);
}
}
private void removeObbState(ObbState obbState) {
synchronized (mObbMounts) {
- mObbMounts.remove(obbState.token);
+ final List<ObbState> obbStates = mObbMounts.get(obbState.token);
+ if (obbStates != null) {
+ obbStates.remove(obbState);
+ }
+ if (obbStates == null || obbStates.isEmpty()) {
+ mObbMounts.remove(obbState.token);
+ }
mObbPathToStateMap.remove(obbState.filename);
}
}
@@ -1737,7 +1745,7 @@ class MountService extends IMountService.Stub
}
}
- abstract void handleExecute() throws RemoteException;
+ abstract void handleExecute() throws RemoteException, IOException;
abstract void handleError();
}
@@ -1749,8 +1757,12 @@ class MountService extends IMountService.Stub
mKey = key;
}
- public void handleExecute() throws RemoteException {
+ public void handleExecute() throws RemoteException, IOException {
ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename);
+ if (obbInfo == null) {
+ throw new IOException("Couldn't read OBB file");
+ }
+
if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mObbState.callerUid)) {
throw new IllegalArgumentException("Caller package does not match OBB file");
}
@@ -1773,15 +1785,17 @@ class MountService extends IMountService.Stub
if (rc == StorageResultCode.OperationSucceeded) {
try {
- mObbState.token.onObbResult(mObbState.filename, "mounted");
+ mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_MOUNTED);
} catch (RemoteException e) {
Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
}
} else {
- Slog.e(TAG, "Couldn't mount OBB file");
+ Slog.e(TAG, "Couldn't mount OBB file: " + rc);
// We didn't succeed, so remove this from the mount-set.
removeObbState(mObbState);
+
+ mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
}
}
@@ -1789,7 +1803,7 @@ class MountService extends IMountService.Stub
removeObbState(mObbState);
try {
- mObbState.token.onObbResult(mObbState.filename, "error");
+ mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
} catch (RemoteException e) {
Slog.e(TAG, "Couldn't send back OBB mount error for " + mObbState.filename);
}
@@ -1818,8 +1832,11 @@ class MountService extends IMountService.Stub
mForceUnmount = force;
}
- public void handleExecute() throws RemoteException {
+ public void handleExecute() throws RemoteException, IOException {
ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename);
+ if (obbInfo == null) {
+ throw new IOException("Couldn't read OBB file");
+ }
if (!isCallerOwnerOfPackageOrSystem(obbInfo.packageName)) {
throw new IllegalArgumentException("Caller package does not match OBB file");
@@ -1843,13 +1860,13 @@ class MountService extends IMountService.Stub
removeObbState(mObbState);
try {
- mObbState.token.onObbResult(mObbState.filename, "unmounted");
+ mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_UNMOUNTED);
} catch (RemoteException e) {
Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
}
} else {
try {
- mObbState.token.onObbResult(mObbState.filename, "error");
+ mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
} catch (RemoteException e) {
Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
}
@@ -1860,7 +1877,7 @@ class MountService extends IMountService.Stub
removeObbState(mObbState);
try {
- mObbState.token.onObbResult(mObbState.filename, "error");
+ mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
} catch (RemoteException e) {
Slog.e(TAG, "Couldn't send back OBB unmount error for " + mObbState.filename);
}