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
This commit is contained in:
Kenny Root
2010-09-22 17:29:43 -07:00
parent ea2cf2f936
commit 05105f7abe
15 changed files with 576 additions and 77 deletions

View File

@ -52619,6 +52619,118 @@
> >
</field> </field>
</class> </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" <class name="Resources"
extends="java.lang.Object" extends="java.lang.Object"
abstract="false" abstract="false"
@ -129032,6 +129144,38 @@
</package> </package>
<package name="android.os.storage" <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" <class name="StorageManager"
extends="java.lang.Object" extends="java.lang.Object"
abstract="false" abstract="false"
@ -129082,6 +129226,8 @@
</parameter> </parameter>
<parameter name="key" type="java.lang.String"> <parameter name="key" type="java.lang.String">
</parameter> </parameter>
<parameter name="listener" type="android.os.storage.OnObbStateChangeListener">
</parameter>
</method> </method>
<method name="unmountObb" <method name="unmountObb"
return="boolean" return="boolean"
@ -129097,6 +129243,8 @@
</parameter> </parameter>
<parameter name="force" type="boolean"> <parameter name="force" type="boolean">
</parameter> </parameter>
<parameter name="listener" type="android.os.storage.OnObbStateChangeListener">
</parameter>
<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException"> <exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
</exception> </exception>
</method> </method>

View File

@ -20,9 +20,9 @@ import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
/** /**
* Basic information about a Opaque Binary Blob (OBB) that reflects * Basic information about a Opaque Binary Blob (OBB) that reflects the info
* the info from the footer on the OBB file. * from the footer on the OBB file. This information may be manipulated by a
* @hide * developer with the <code>obbtool</code> program in the Android SDK.
*/ */
public class ObbInfo implements Parcelable { public class ObbInfo implements Parcelable {
/** Flag noting that this OBB is an overlay patch for a base OBB. */ /** 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 int flags;
public ObbInfo() { // Only allow things in this package to instantiate.
/* package */ ObbInfo() {
} }
public String toString() { public String toString() {

View File

@ -16,25 +16,43 @@
package android.content.res; package android.content.res;
import java.io.File;
import java.io.IOException;
/** /**
* Class to scan Opaque Binary Blob (OBB) files. * Class to scan Opaque Binary Blob (OBB) files. Use this to get information
* @hide * about an OBB file for use in a program via {@link ObbInfo}.
*/ */
public class ObbScanner { public class ObbScanner {
// Don't allow others to instantiate this class // Don't allow others to instantiate this class
private ObbScanner() {} 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) { if (filePath == null) {
return null; throw new IllegalArgumentException("file path cannot be null");
} }
ObbInfo obbInfo = new ObbInfo(); final File obbFile = new File(filePath);
if (!getObbInfo_native(filePath, obbInfo)) { if (!obbFile.exists()) {
throw new IllegalArgumentException("Could not read OBB file: " + filePath); throw new IllegalArgumentException("OBB file does nto exist: " + filePath);
} }
final String canonicalFilePath = obbFile.getCanonicalPath();
ObbInfo obbInfo = new ObbInfo();
getObbInfo_native(canonicalFilePath, obbInfo);
return 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;
} }

View File

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

View File

@ -23,13 +23,28 @@ import android.os.RemoteException;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.util.Log; import android.util.Log;
import java.lang.ref.WeakReference;
import java.util.ArrayList; 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 * Get an instance of this class by calling
* {@link android.content.Context#getSystemService(java.lang.String)} with an argument * {@link android.content.Context#getSystemService(java.lang.String)} with an
* of {@link android.content.Context#STORAGE_SERVICE}. * argument of {@link android.content.Context#STORAGE_SERVICE}.
*/ */
public class StorageManager public class StorageManager
@ -75,11 +90,113 @@ public class StorageManager
/** /**
* Binder listener for OBB action results. * Binder listener for OBB action results.
*/ */
private final ObbActionBinderListener mObbActionListener = new ObbActionBinderListener(); private final ObbActionListener mObbActionListener = new ObbActionListener();
private class ObbActionBinderListener extends IObbActionListener.Stub {
private class ObbActionListener extends IObbActionListener.Stub {
private List<WeakReference<ObbListenerDelegate>> mListeners = new LinkedList<WeakReference<ObbListenerDelegate>>();
@Override @Override
public void onObbResult(String filename, String status) throws RemoteException { 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. * and the target looper handler.
*/ */
private class StorageEvent { private class StorageEvent {
public static final int EVENT_UMS_CONNECTION_CHANGED = 1; static final int EVENT_UMS_CONNECTION_CHANGED = 1;
public static final int EVENT_STORAGE_STATE_CHANGED = 2; static final int EVENT_STORAGE_STATE_CHANGED = 2;
static final int EVENT_OBB_STATE_CHANGED = 3;
private Message mMessage; private Message mMessage;
@ -300,19 +418,27 @@ public class StorageManager
* specified, it is supplied to the mounting process to be used in any * specified, it is supplied to the mounting process to be used in any
* encryption used in the OBB. * encryption used in the OBB.
* <p> * <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 * <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. * 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. * 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 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 * @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 { try {
mObbActionListener.addListener(listener);
mMountService.mountObb(filename, key, mObbActionListener); mMountService.mountObb(filename, key, mObbActionListener);
return true; return true;
} catch (RemoteException e) { } catch (RemoteException e) {
@ -323,15 +449,20 @@ public class StorageManager
} }
/** /**
* Unmount an Opaque Binary Blob (OBB) file. If the <code>force</code> flag * Unmount an Opaque Binary Blob (OBB) file asynchronously. If the
* is true, it will kill any application needed to unmount the given OBB. * <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> * <p>
* <em>Note:</em> you can only mount OBB files for which the OBB tag on the * <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. * 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 obtain access to any other
* application's OBB that shares its UID. * application's OBB that shares its UID.
* <p> * <p>
* STOPSHIP document more; discuss lack of guarantees of security
* *
* @param filename path to the OBB file * @param filename path to the OBB file
* @param force whether to kill any programs using this in order to unmount * @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 * @return whether the unmount call was successfully queued or not
* @throws IllegalArgumentException when OBB is not already mounted * @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 { try {
mObbActionListener.addListener(listener);
mMountService.unmountObb(filename, force, mObbActionListener); mMountService.unmountObb(filename, force, mObbActionListener);
return true; return true;
} catch (RemoteException e) { } catch (RemoteException e) {

View File

@ -34,7 +34,17 @@ static struct {
jfieldID flags; jfieldID flags;
} gObbInfoClassInfo; } 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) jobject obbInfo)
{ {
const char* filePath = env->GetStringUTFChars(file, JNI_FALSE); 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(); sp<ObbFile> obb = new ObbFile();
if (!obb->readFrom(filePath)) { if (!obb->readFrom(filePath)) {
env->ReleaseStringUTFChars(file, filePath); env->ReleaseStringUTFChars(file, filePath);
return JNI_FALSE; doThrow(env, "java/io/IOException", "Could not read OBB file");
return;
} }
env->ReleaseStringUTFChars(file, filePath); env->ReleaseStringUTFChars(file, filePath);
@ -51,13 +62,13 @@ static jboolean android_content_res_ObbScanner_getObbInfo(JNIEnv* env, jobject c
jstring packageName = env->NewStringUTF(packageNameStr); jstring packageName = env->NewStringUTF(packageNameStr);
if (packageName == NULL) { if (packageName == NULL) {
return JNI_FALSE; doThrow(env, "java/io/IOException", "Could not read OBB file");
return;
} }
env->SetObjectField(obbInfo, gObbInfoClassInfo.packageName, packageName); env->SetObjectField(obbInfo, gObbInfoClassInfo.packageName, packageName);
env->SetIntField(obbInfo, gObbInfoClassInfo.version, obb->getVersion()); env->SetIntField(obbInfo, gObbInfoClassInfo.version, obb->getVersion());
env->SetIntField(obbInfo, gObbInfoClassInfo.flags, obb->getFlags());
return JNI_TRUE;
} }
/* /*
@ -65,7 +76,7 @@ static jboolean android_content_res_ObbScanner_getObbInfo(JNIEnv* env, jobject c
*/ */
static JNINativeMethod gMethods[] = { static JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */ /* 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 }, (void*) android_content_res_ObbScanner_getObbInfo },
}; };

View File

@ -62,7 +62,8 @@ public:
virtual void finishMediaUpdate() = 0; virtual void finishMediaUpdate() = 0;
virtual void mountObb(const String16& filename, const String16& key, virtual void mountObb(const String16& filename, const String16& key,
const sp<IObbActionListener>& token) = 0; 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 isObbMounted(const String16& filename) = 0;
virtual bool getMountedObbPath(const String16& filename, String16& path) = 0; virtual bool getMountedObbPath(const String16& filename, String16& path) = 0;
}; };

View File

@ -429,8 +429,8 @@ public:
reply.readExceptionCode(); reply.readExceptionCode();
} }
void mountObb(const String16& filename, const String16& key, const sp< void mountObb(const String16& filename, const String16& key,
IObbActionListener>& token) const sp<IObbActionListener>& token)
{ {
Parcel data, reply; Parcel data, reply;
data.writeInterfaceToken(IMountService::getInterfaceDescriptor()); 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; Parcel data, reply;
data.writeInterfaceToken(IMountService::getInterfaceDescriptor()); data.writeInterfaceToken(IMountService::getInterfaceDescriptor());

View File

@ -12,6 +12,7 @@ LOCAL_SRC_FILES:= \
looper.cpp \ looper.cpp \
native_activity.cpp \ native_activity.cpp \
native_window.cpp \ native_window.cpp \
obb.cpp \
sensor.cpp \ sensor.cpp \
storage_manager.cpp storage_manager.cpp

54
native/android/obb.cpp Normal file
View File

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

View File

@ -38,20 +38,20 @@ public:
mStorageManager(mgr) mStorageManager(mgr)
{} {}
virtual void onObbResult(const android::String16& filename, const android::String16& state) { virtual void onObbResult(const android::String16& filename, const android::String16& state);
LOGD("Got obb result (%s, %s)\n", String8(filename).string(), String8(state).string());
}
}; };
struct AStorageManager : public RefBase { struct AStorageManager : public RefBase {
protected: protected:
void* mObbCallback; AStorageManager_obbCallbackFunc mObbCallback;
void* mObbCallbackData;
sp<ObbActionListener> mObbActionListener; sp<ObbActionListener> mObbActionListener;
sp<IMountService> mMountService; sp<IMountService> mMountService;
public: public:
AStorageManager() : AStorageManager()
mObbCallback(NULL) : mObbCallback(NULL)
, mObbCallbackData(NULL)
{ {
} }
@ -73,8 +73,15 @@ public:
return true; return true;
} }
void setObbCallback(void* cb) { void setObbCallback(AStorageManager_obbCallbackFunc cb, void* data) {
mObbCallback = cb; 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) { void mountObb(const char* filename, const char* key) {
@ -85,7 +92,7 @@ public:
void unmountObb(const char* filename, const bool force) { void unmountObb(const char* filename, const bool force) {
String16 filename16(filename); String16 filename16(filename);
mMountService->unmountObb(filename16, force); mMountService->unmountObb(filename16, force, mObbActionListener);
} }
int isObbMounted(const char* filename) { 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() { AStorageManager* AStorageManager_new() {
sp<AStorageManager> mgr = new AStorageManager(); sp<AStorageManager> mgr = new AStorageManager();
@ -120,8 +131,8 @@ void AStorageManager_delete(AStorageManager* mgr) {
} }
} }
void AStorageManager_setObbCallback(AStorageManager* mgr, void* cb) { void AStorageManager_setObbCallback(AStorageManager* mgr, AStorageManager_obbCallbackFunc cb, void* data) {
mgr->setObbCallback(cb); mgr->setObbCallback(cb, data);
} }
void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key) { void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key) {

View File

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

View File

@ -37,17 +37,22 @@ AStorageManager* AStorageManager_new();
void AStorageManager_delete(AStorageManager* mgr); 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); 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); 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
#endif // ANDROID_PACKAGE_MANAGER_H #endif // ANDROID_STORAGE_MANAGER_H

View File

@ -156,7 +156,12 @@ public class DefaultContainerService extends IntentService {
} }
public ObbInfo getObbInfo(String filename) { 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;
}
} }
}; };

View File

@ -46,6 +46,7 @@ import android.os.storage.IObbActionListener;
import android.os.storage.StorageResultCode; import android.os.storage.StorageResultCode;
import android.util.Slog; import android.util.Slog;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.HashSet; 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 * Mounted OBB tracking information. Used to track the current state of all
* OBBs. * 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>(); final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
class ObbState implements IBinder.DeathRecipient { class ObbState implements IBinder.DeathRecipient {
@ -160,13 +161,13 @@ class MountService extends IMountService.Stub
} }
// OBB source filename // OBB source filename
String filename; final String filename;
// Token of remote Binder caller // Token of remote Binder caller
IObbActionListener token; final IObbActionListener token;
// Binder.callingUid() // Binder.callingUid()
public int callerUid; final public int callerUid;
// Whether this is mounted currently. // Whether this is mounted currently.
boolean mounted; boolean mounted;
@ -225,9 +226,9 @@ class MountService extends IMountService.Stub
private static final int MAX_UNMOUNT_RETRIES = 4; private static final int MAX_UNMOUNT_RETRIES = 4;
class UnmountCallBack { class UnmountCallBack {
String path; final String path;
final boolean force;
int retries; int retries;
boolean force;
UnmountCallBack(String path, boolean force) { UnmountCallBack(String path, boolean force) {
retries = 0; retries = 0;
@ -242,7 +243,7 @@ class MountService extends IMountService.Stub
} }
class UmsEnableCallBack extends UnmountCallBack { class UmsEnableCallBack extends UnmountCallBack {
String method; final String method;
UmsEnableCallBack(String path, String method, boolean force) { UmsEnableCallBack(String path, String method, boolean force) {
super(path, force); super(path, force);
@ -1513,10 +1514,6 @@ class MountService extends IMountService.Stub
throw new IllegalArgumentException("OBB file is already mounted"); 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(); final int callerUid = Binder.getCallingUid();
obbState = new ObbState(filename, token, callerUid); obbState = new ObbState(filename, token, callerUid);
addObbState(obbState); addObbState(obbState);
@ -1554,14 +1551,25 @@ class MountService extends IMountService.Stub
private void addObbState(ObbState obbState) { private void addObbState(ObbState obbState) {
synchronized (mObbMounts) { 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); mObbPathToStateMap.put(obbState.filename, obbState);
} }
} }
private void removeObbState(ObbState obbState) { private void removeObbState(ObbState obbState) {
synchronized (mObbMounts) { 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); 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(); abstract void handleError();
} }
@ -1749,8 +1757,12 @@ class MountService extends IMountService.Stub
mKey = key; mKey = key;
} }
public void handleExecute() throws RemoteException { public void handleExecute() throws RemoteException, IOException {
ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename); ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename);
if (obbInfo == null) {
throw new IOException("Couldn't read OBB file");
}
if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mObbState.callerUid)) { if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mObbState.callerUid)) {
throw new IllegalArgumentException("Caller package does not match OBB file"); throw new IllegalArgumentException("Caller package does not match OBB file");
} }
@ -1773,15 +1785,17 @@ class MountService extends IMountService.Stub
if (rc == StorageResultCode.OperationSucceeded) { if (rc == StorageResultCode.OperationSucceeded) {
try { try {
mObbState.token.onObbResult(mObbState.filename, "mounted"); mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_MOUNTED);
} catch (RemoteException e) { } catch (RemoteException e) {
Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged"); Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
} }
} else { } 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. // We didn't succeed, so remove this from the mount-set.
removeObbState(mObbState); removeObbState(mObbState);
mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
} }
} }
@ -1789,7 +1803,7 @@ class MountService extends IMountService.Stub
removeObbState(mObbState); removeObbState(mObbState);
try { try {
mObbState.token.onObbResult(mObbState.filename, "error"); mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
} catch (RemoteException e) { } catch (RemoteException e) {
Slog.e(TAG, "Couldn't send back OBB mount error for " + mObbState.filename); Slog.e(TAG, "Couldn't send back OBB mount error for " + mObbState.filename);
} }
@ -1818,8 +1832,11 @@ class MountService extends IMountService.Stub
mForceUnmount = force; mForceUnmount = force;
} }
public void handleExecute() throws RemoteException { public void handleExecute() throws RemoteException, IOException {
ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename); ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename);
if (obbInfo == null) {
throw new IOException("Couldn't read OBB file");
}
if (!isCallerOwnerOfPackageOrSystem(obbInfo.packageName)) { if (!isCallerOwnerOfPackageOrSystem(obbInfo.packageName)) {
throw new IllegalArgumentException("Caller package does not match OBB file"); throw new IllegalArgumentException("Caller package does not match OBB file");
@ -1843,13 +1860,13 @@ class MountService extends IMountService.Stub
removeObbState(mObbState); removeObbState(mObbState);
try { try {
mObbState.token.onObbResult(mObbState.filename, "unmounted"); mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_UNMOUNTED);
} catch (RemoteException e) { } catch (RemoteException e) {
Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged"); Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
} }
} else { } else {
try { try {
mObbState.token.onObbResult(mObbState.filename, "error"); mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
} catch (RemoteException e) { } catch (RemoteException e) {
Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged"); Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
} }
@ -1860,7 +1877,7 @@ class MountService extends IMountService.Stub
removeObbState(mObbState); removeObbState(mObbState);
try { try {
mObbState.token.onObbResult(mObbState.filename, "error"); mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
} catch (RemoteException e) { } catch (RemoteException e) {
Slog.e(TAG, "Couldn't send back OBB unmount error for " + mObbState.filename); Slog.e(TAG, "Couldn't send back OBB unmount error for " + mObbState.filename);
} }