diff options
-rw-r--r-- | api/current.xml | 150 | ||||
-rw-r--r-- | core/java/android/content/res/ObbInfo.java | 9 | ||||
-rw-r--r-- | core/java/android/content/res/ObbScanner.java | 34 | ||||
-rw-r--r-- | core/java/android/os/storage/OnObbStateChangeListener.java | 31 | ||||
-rw-r--r-- | core/java/android/os/storage/StorageManager.java | 168 | ||||
-rw-r--r-- | core/jni/android_content_res_ObbScanner.cpp | 23 | ||||
-rw-r--r-- | include/storage/IMountService.h | 3 | ||||
-rw-r--r-- | libs/storage/IMountService.cpp | 6 | ||||
-rw-r--r-- | native/android/Android.mk | 1 | ||||
-rw-r--r-- | native/android/obb.cpp | 54 | ||||
-rw-r--r-- | native/android/storage_manager.cpp | 31 | ||||
-rw-r--r-- | native/include/android/obb.h | 63 | ||||
-rw-r--r-- | native/include/android/storage_manager.h | 15 | ||||
-rw-r--r-- | packages/DefaultContainerService/src/com/android/defcontainer/DefaultContainerService.java | 7 | ||||
-rw-r--r-- | services/java/com/android/server/MountService.java | 61 |
15 files changed, 577 insertions, 79 deletions
diff --git a/api/current.xml b/api/current.xml index d9e8247..7e7131b 100644 --- a/api/current.xml +++ b/api/current.xml @@ -60494,6 +60494,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" @@ -139670,6 +139782,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="StorageEventListener" extends="java.lang.Object" abstract="true" @@ -139811,6 +139955,8 @@ </parameter> <parameter name="key" type="java.lang.String"> </parameter> +<parameter name="listener" type="android.os.storage.OnObbStateChangeListener"> +</parameter> </method> <method name="registerListener" return="void" @@ -139839,6 +139985,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> @@ -240997,7 +241145,7 @@ deprecated="not deprecated" visibility="public" > -<parameter name="t" type="T"> +<parameter name="arg0" type="T"> </parameter> </method> </interface> 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 4a0296b..4268618 100644 --- a/core/java/android/os/storage/StorageManager.java +++ b/core/java/android/os/storage/StorageManager.java @@ -23,14 +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 @@ -76,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; } } @@ -89,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; @@ -291,19 +408,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) { @@ -314,15 +439,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 @@ -330,8 +460,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 32f5e73..e6c6953 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; @@ -150,7 +151,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 { @@ -162,13 +163,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; @@ -227,9 +228,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; @@ -244,7 +245,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); @@ -1526,10 +1527,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); @@ -1567,14 +1564,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); } } @@ -1750,7 +1758,7 @@ class MountService extends IMountService.Stub } } - abstract void handleExecute() throws RemoteException; + abstract void handleExecute() throws RemoteException, IOException; abstract void handleError(); } @@ -1762,8 +1770,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"); } @@ -1786,15 +1798,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); } } @@ -1802,7 +1816,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); } @@ -1831,8 +1845,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"); @@ -1856,13 +1873,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"); } @@ -1873,7 +1890,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); } |