am b4de3dca
: Merge "OBB: rearrange to be entirely asynchronous" into gingerbread
Merge commit 'b4de3dca96b9ff18562062e181dcd8b83e641e45' into gingerbread-plus-aosp * commit 'b4de3dca96b9ff18562062e181dcd8b83e641e45': OBB: rearrange to be entirely asynchronous
This commit is contained in:
@ -133777,9 +133777,97 @@
|
||||
>
|
||||
<parameter name="path" type="java.lang.String">
|
||||
</parameter>
|
||||
<parameter name="state" type="java.lang.String">
|
||||
<parameter name="state" type="int">
|
||||
</parameter>
|
||||
</method>
|
||||
<field name="ERROR_ALREADY_MOUNTED"
|
||||
type="int"
|
||||
transient="false"
|
||||
volatile="false"
|
||||
value="24"
|
||||
static="true"
|
||||
final="true"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
<field name="ERROR_COULD_NOT_MOUNT"
|
||||
type="int"
|
||||
transient="false"
|
||||
volatile="false"
|
||||
value="21"
|
||||
static="true"
|
||||
final="true"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
<field name="ERROR_COULD_NOT_UNMOUNT"
|
||||
type="int"
|
||||
transient="false"
|
||||
volatile="false"
|
||||
value="22"
|
||||
static="true"
|
||||
final="true"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
<field name="ERROR_INTERNAL"
|
||||
type="int"
|
||||
transient="false"
|
||||
volatile="false"
|
||||
value="20"
|
||||
static="true"
|
||||
final="true"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
<field name="ERROR_NOT_MOUNTED"
|
||||
type="int"
|
||||
transient="false"
|
||||
volatile="false"
|
||||
value="23"
|
||||
static="true"
|
||||
final="true"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
<field name="ERROR_PERMISSION_DENIED"
|
||||
type="int"
|
||||
transient="false"
|
||||
volatile="false"
|
||||
value="25"
|
||||
static="true"
|
||||
final="true"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
<field name="MOUNTED"
|
||||
type="int"
|
||||
transient="false"
|
||||
volatile="false"
|
||||
value="1"
|
||||
static="true"
|
||||
final="true"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
<field name="UNMOUNTED"
|
||||
type="int"
|
||||
transient="false"
|
||||
volatile="false"
|
||||
value="2"
|
||||
static="true"
|
||||
final="true"
|
||||
deprecated="not deprecated"
|
||||
visibility="public"
|
||||
>
|
||||
</field>
|
||||
</class>
|
||||
<class name="StorageManager"
|
||||
extends="java.lang.Object"
|
||||
@ -133814,8 +133902,6 @@
|
||||
>
|
||||
<parameter name="filename" type="java.lang.String">
|
||||
</parameter>
|
||||
<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
|
||||
</exception>
|
||||
</method>
|
||||
<method name="mountObb"
|
||||
return="boolean"
|
||||
|
@ -484,7 +484,7 @@ public interface IMountService extends IInterface {
|
||||
* IObbActionListener to inform it of the terminal state of the
|
||||
* call.
|
||||
*/
|
||||
public void mountObb(String filename, String key, IObbActionListener token)
|
||||
public void mountObb(String filename, String key, IObbActionListener token, int nonce)
|
||||
throws RemoteException {
|
||||
Parcel _data = Parcel.obtain();
|
||||
Parcel _reply = Parcel.obtain();
|
||||
@ -493,6 +493,7 @@ public interface IMountService extends IInterface {
|
||||
_data.writeString(filename);
|
||||
_data.writeString(key);
|
||||
_data.writeStrongBinder((token != null ? token.asBinder() : null));
|
||||
_data.writeInt(nonce);
|
||||
mRemote.transact(Stub.TRANSACTION_mountObb, _data, _reply, 0);
|
||||
_reply.readException();
|
||||
} finally {
|
||||
@ -508,8 +509,8 @@ public interface IMountService extends IInterface {
|
||||
* IObbActionListener to inform it of the terminal state of the
|
||||
* call.
|
||||
*/
|
||||
public void unmountObb(String filename, boolean force, IObbActionListener token)
|
||||
throws RemoteException {
|
||||
public void unmountObb(String filename, boolean force, IObbActionListener token,
|
||||
int nonce) throws RemoteException {
|
||||
Parcel _data = Parcel.obtain();
|
||||
Parcel _reply = Parcel.obtain();
|
||||
try {
|
||||
@ -517,6 +518,7 @@ public interface IMountService extends IInterface {
|
||||
_data.writeString(filename);
|
||||
_data.writeInt((force ? 1 : 0));
|
||||
_data.writeStrongBinder((token != null ? token.asBinder() : null));
|
||||
_data.writeInt(nonce);
|
||||
mRemote.transact(Stub.TRANSACTION_unmountObb, _data, _reply, 0);
|
||||
_reply.readException();
|
||||
} finally {
|
||||
@ -855,7 +857,9 @@ public interface IMountService extends IInterface {
|
||||
key = data.readString();
|
||||
IObbActionListener observer;
|
||||
observer = IObbActionListener.Stub.asInterface(data.readStrongBinder());
|
||||
mountObb(filename, key, observer);
|
||||
int nonce;
|
||||
nonce = data.readInt();
|
||||
mountObb(filename, key, observer, nonce);
|
||||
reply.writeNoException();
|
||||
return true;
|
||||
}
|
||||
@ -867,7 +871,9 @@ public interface IMountService extends IInterface {
|
||||
force = 0 != data.readInt();
|
||||
IObbActionListener observer;
|
||||
observer = IObbActionListener.Stub.asInterface(data.readStrongBinder());
|
||||
unmountObb(filename, force, observer);
|
||||
int nonce;
|
||||
nonce = data.readInt();
|
||||
unmountObb(filename, force, observer, nonce);
|
||||
reply.writeNoException();
|
||||
return true;
|
||||
}
|
||||
@ -979,7 +985,7 @@ public interface IMountService extends IInterface {
|
||||
* MountService will call back to the supplied IObbActionListener to inform
|
||||
* it of the terminal state of the call.
|
||||
*/
|
||||
public void mountObb(String filename, String key, IObbActionListener token)
|
||||
public void mountObb(String filename, String key, IObbActionListener token, int nonce)
|
||||
throws RemoteException;
|
||||
|
||||
/*
|
||||
@ -1023,7 +1029,7 @@ public interface IMountService extends IInterface {
|
||||
* MountService will call back to the supplied IObbActionListener to inform
|
||||
* it of the terminal state of the call.
|
||||
*/
|
||||
public void unmountObb(String filename, boolean force, IObbActionListener token)
|
||||
public void unmountObb(String filename, boolean force, IObbActionListener token, int nonce)
|
||||
throws RemoteException;
|
||||
|
||||
/*
|
||||
|
@ -69,9 +69,11 @@ public interface IObbActionListener extends IInterface {
|
||||
data.enforceInterface(DESCRIPTOR);
|
||||
String filename;
|
||||
filename = data.readString();
|
||||
String status;
|
||||
status = data.readString();
|
||||
this.onObbResult(filename, status);
|
||||
int nonce;
|
||||
nonce = data.readInt();
|
||||
int status;
|
||||
status = data.readInt();
|
||||
this.onObbResult(filename, nonce, status);
|
||||
reply.writeNoException();
|
||||
return true;
|
||||
}
|
||||
@ -101,13 +103,15 @@ public interface IObbActionListener extends IInterface {
|
||||
* on
|
||||
* @param returnCode status of the operation
|
||||
*/
|
||||
public void onObbResult(String filename, String status) throws RemoteException {
|
||||
public void onObbResult(String filename, int nonce, int status)
|
||||
throws RemoteException {
|
||||
Parcel _data = Parcel.obtain();
|
||||
Parcel _reply = Parcel.obtain();
|
||||
try {
|
||||
_data.writeInterfaceToken(DESCRIPTOR);
|
||||
_data.writeString(filename);
|
||||
_data.writeString(status);
|
||||
_data.writeInt(nonce);
|
||||
_data.writeInt(status);
|
||||
mRemote.transact(Stub.TRANSACTION_onObbResult, _data, _reply, 0);
|
||||
_reply.readException();
|
||||
} finally {
|
||||
@ -124,7 +128,8 @@ public interface IObbActionListener extends IInterface {
|
||||
* Return from an OBB action result.
|
||||
*
|
||||
* @param filename the path to the OBB the operation was performed on
|
||||
* @param returnCode status of the operation
|
||||
* @param nonce identifier that is meaningful to the receiver
|
||||
* @param status status code as defined in {@link OnObbStateChangeListener}
|
||||
*/
|
||||
public void onObbResult(String filename, String status) throws RemoteException;
|
||||
public void onObbResult(String filename, int nonce, int status) throws RemoteException;
|
||||
}
|
||||
|
@ -17,15 +17,69 @@
|
||||
package android.os.storage;
|
||||
|
||||
/**
|
||||
* Used for receiving notifications from {@link StorageManager}.
|
||||
* Used for receiving notifications from {@link StorageManager} about OBB file
|
||||
* states.
|
||||
*/
|
||||
public abstract class OnObbStateChangeListener {
|
||||
|
||||
/**
|
||||
* The OBB container is now mounted and ready for use. Returned in status
|
||||
* messages from calls made via {@link StorageManager}
|
||||
*/
|
||||
public static final int MOUNTED = 1;
|
||||
|
||||
/**
|
||||
* The OBB container is now unmounted and not usable. Returned in status
|
||||
* messages from calls made via {@link StorageManager}
|
||||
*/
|
||||
public static final int UNMOUNTED = 2;
|
||||
|
||||
/**
|
||||
* There was an internal system error encountered while trying to mount the
|
||||
* OBB. Returned in status messages from calls made via
|
||||
* {@link StorageManager}
|
||||
*/
|
||||
public static final int ERROR_INTERNAL = 20;
|
||||
|
||||
/**
|
||||
* The OBB could not be mounted by the system. Returned in status messages
|
||||
* from calls made via {@link StorageManager}
|
||||
*/
|
||||
public static final int ERROR_COULD_NOT_MOUNT = 21;
|
||||
|
||||
/**
|
||||
* The OBB could not be unmounted. This most likely indicates that a file is
|
||||
* in use on the OBB. Returned in status messages from calls made via
|
||||
* {@link StorageManager}
|
||||
*/
|
||||
public static final int ERROR_COULD_NOT_UNMOUNT = 22;
|
||||
|
||||
/**
|
||||
* A call was made to unmount the OBB when it was not mounted. Returned in
|
||||
* status messages from calls made via {@link StorageManager}
|
||||
*/
|
||||
public static final int ERROR_NOT_MOUNTED = 23;
|
||||
|
||||
/**
|
||||
* The OBB has already been mounted. Returned in status messages from calls
|
||||
* made via {@link StorageManager}
|
||||
*/
|
||||
public static final int ERROR_ALREADY_MOUNTED = 24;
|
||||
|
||||
/**
|
||||
* The current application does not have permission to use this OBB because
|
||||
* the OBB indicates it's owned by a different package or the key used to
|
||||
* open it is incorrect. Returned in status messages from calls made via
|
||||
* {@link StorageManager}
|
||||
*/
|
||||
public static final int ERROR_PERMISSION_DENIED = 25;
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
public void onObbStateChange(String path, int state) {
|
||||
}
|
||||
}
|
||||
|
@ -22,12 +22,13 @@ import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.util.Log;
|
||||
import android.util.Slog;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
/**
|
||||
* StorageManager is the interface to the systems storage service. The storage
|
||||
@ -69,7 +70,12 @@ public class StorageManager
|
||||
/*
|
||||
* List of our listeners
|
||||
*/
|
||||
private ArrayList<ListenerDelegate> mListeners = new ArrayList<ListenerDelegate>();
|
||||
private List<ListenerDelegate> mListeners = new ArrayList<ListenerDelegate>();
|
||||
|
||||
/*
|
||||
* Next available nonce
|
||||
*/
|
||||
final private AtomicInteger mNextNonce = new AtomicInteger(0);
|
||||
|
||||
private class MountServiceBinderListener extends IMountServiceListener.Stub {
|
||||
public void onUsbMassStorageConnectionChanged(boolean available) {
|
||||
@ -93,55 +99,36 @@ public class StorageManager
|
||||
private final ObbActionListener mObbActionListener = new ObbActionListener();
|
||||
|
||||
private class ObbActionListener extends IObbActionListener.Stub {
|
||||
private List<WeakReference<ObbListenerDelegate>> mListeners = new LinkedList<WeakReference<ObbListenerDelegate>>();
|
||||
private SparseArray<ObbListenerDelegate> mListeners = new SparseArray<ObbListenerDelegate>();
|
||||
|
||||
@Override
|
||||
public void onObbResult(String filename, String status) throws RemoteException {
|
||||
public void onObbResult(String filename, int nonce, int status) throws RemoteException {
|
||||
final ObbListenerDelegate delegate;
|
||||
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 = mListeners.get(nonce);
|
||||
if (delegate != null) {
|
||||
mListeners.remove(nonce);
|
||||
}
|
||||
}
|
||||
|
||||
if (delegate != null) {
|
||||
delegate.sendObbStateChanged(filename, status);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void addListener(OnObbStateChangeListener listener) {
|
||||
if (listener == null) {
|
||||
return;
|
||||
}
|
||||
public int addListener(OnObbStateChangeListener listener) {
|
||||
final ObbListenerDelegate delegate = new ObbListenerDelegate(listener);
|
||||
|
||||
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;
|
||||
mListeners.put(delegate.nonce, delegate);
|
||||
}
|
||||
|
||||
/*
|
||||
* If we're already in the listeners, we don't need to be in
|
||||
* there again.
|
||||
*/
|
||||
if (listener.equals(delegate.getListener())) {
|
||||
return;
|
||||
return delegate.nonce;
|
||||
}
|
||||
}
|
||||
|
||||
final ObbListenerDelegate delegate = new ObbListenerDelegate(listener);
|
||||
mListeners.add(new WeakReference<ObbListenerDelegate>(delegate));
|
||||
}
|
||||
}
|
||||
private int getNextNonce() {
|
||||
return mNextNonce.getAndIncrement();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -151,7 +138,10 @@ public class StorageManager
|
||||
private final WeakReference<OnObbStateChangeListener> mObbEventListenerRef;
|
||||
private final Handler mHandler;
|
||||
|
||||
private final int nonce;
|
||||
|
||||
ObbListenerDelegate(OnObbStateChangeListener listener) {
|
||||
nonce = getNextNonce();
|
||||
mObbEventListenerRef = new WeakReference<OnObbStateChangeListener>(listener);
|
||||
mHandler = new Handler(mTgtLooper) {
|
||||
@Override
|
||||
@ -180,7 +170,7 @@ public class StorageManager
|
||||
return mObbEventListenerRef.get();
|
||||
}
|
||||
|
||||
void sendObbStateChanged(String path, String state) {
|
||||
void sendObbStateChanged(String path, int state) {
|
||||
ObbStateChangedStorageEvent e = new ObbStateChangedStorageEvent(path, state);
|
||||
mHandler.sendMessage(e.getMessage());
|
||||
}
|
||||
@ -191,9 +181,10 @@ public class StorageManager
|
||||
*/
|
||||
private class ObbStateChangedStorageEvent extends StorageEvent {
|
||||
public final String path;
|
||||
public final String state;
|
||||
|
||||
public ObbStateChangedStorageEvent(String path, String state) {
|
||||
public final int state;
|
||||
|
||||
public ObbStateChangedStorageEvent(String path, int state) {
|
||||
super(EVENT_OBB_STATE_CHANGED);
|
||||
this.path = path;
|
||||
this.state = state;
|
||||
@ -420,10 +411,8 @@ public class StorageManager
|
||||
* <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>.
|
||||
* in use will be unmounted. The {@link OnObbStateChangeListener} registered
|
||||
* with this call will receive the success or failure of this operation.
|
||||
* <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.
|
||||
@ -433,12 +422,21 @@ public class StorageManager
|
||||
* @param filename the path to the OBB file
|
||||
* @param key secret used to encrypt the OBB; may be <code>null</code> if no
|
||||
* encryption was used on the OBB.
|
||||
* @param listener will receive the success or failure of the operation
|
||||
* @return whether the mount call was successfully queued or not
|
||||
*/
|
||||
public boolean mountObb(String filename, String key, OnObbStateChangeListener listener) {
|
||||
if (filename == null) {
|
||||
throw new IllegalArgumentException("filename cannot be null");
|
||||
}
|
||||
|
||||
if (listener == null) {
|
||||
throw new IllegalArgumentException("listener cannot be null");
|
||||
}
|
||||
|
||||
try {
|
||||
mObbActionListener.addListener(listener);
|
||||
mMountService.mountObb(filename, key, mObbActionListener);
|
||||
final int nonce = mObbActionListener.addListener(listener);
|
||||
mMountService.mountObb(filename, key, mObbActionListener, nonce);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to mount OBB", e);
|
||||
@ -452,10 +450,8 @@ public class StorageManager
|
||||
* <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>.
|
||||
* The {@link OnObbStateChangeListener} registered with this call will
|
||||
* receive the success or failure of this operation.
|
||||
* <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.
|
||||
@ -466,12 +462,21 @@ public class StorageManager
|
||||
* @param filename path to the OBB file
|
||||
* @param force whether to kill any programs using this in order to unmount
|
||||
* it
|
||||
* @param listener will receive the success or failure of the operation
|
||||
* @return whether the unmount call was successfully queued or not
|
||||
*/
|
||||
public boolean unmountObb(String filename, boolean force, OnObbStateChangeListener listener) {
|
||||
if (filename == null) {
|
||||
throw new IllegalArgumentException("filename cannot be null");
|
||||
}
|
||||
|
||||
if (listener == null) {
|
||||
throw new IllegalArgumentException("listener cannot be null");
|
||||
}
|
||||
|
||||
try {
|
||||
mObbActionListener.addListener(listener);
|
||||
mMountService.unmountObb(filename, force, mObbActionListener);
|
||||
final int nonce = mObbActionListener.addListener(listener);
|
||||
mMountService.unmountObb(filename, force, mObbActionListener, nonce);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to mount OBB", e);
|
||||
@ -486,7 +491,11 @@ public class StorageManager
|
||||
* @param filename path to OBB image
|
||||
* @return true if OBB is mounted; false if not mounted or on error
|
||||
*/
|
||||
public boolean isObbMounted(String filename) throws IllegalArgumentException {
|
||||
public boolean isObbMounted(String filename) {
|
||||
if (filename == null) {
|
||||
throw new IllegalArgumentException("filename cannot be null");
|
||||
}
|
||||
|
||||
try {
|
||||
return mMountService.isObbMounted(filename);
|
||||
} catch (RemoteException e) {
|
||||
@ -506,12 +515,14 @@ public class StorageManager
|
||||
* not mounted or exception encountered trying to read status
|
||||
*/
|
||||
public String getMountedObbPath(String filename) {
|
||||
if (filename == null) {
|
||||
throw new IllegalArgumentException("filename cannot be null");
|
||||
}
|
||||
|
||||
try {
|
||||
return mMountService.getMountedObbPath(filename);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to find mounted path for OBB", e);
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.d(TAG, "Couldn't read OBB file", e);
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -21,7 +21,6 @@ import com.android.frameworks.coretests.R;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.content.res.Resources.NotFoundException;
|
||||
import android.os.Environment;
|
||||
import android.os.FileUtils;
|
||||
import android.os.storage.OnObbStateChangeListener;
|
||||
import android.os.storage.StorageManager;
|
||||
@ -57,17 +56,15 @@ public class MountServiceTests extends AndroidTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
private interface CompletableTask {
|
||||
public boolean isDone();
|
||||
}
|
||||
private static class ObbObserver extends OnObbStateChangeListener {
|
||||
private String path;
|
||||
|
||||
private static class ObbObserver extends OnObbStateChangeListener implements CompletableTask {
|
||||
public String path;
|
||||
public String state;
|
||||
public int state = -1;
|
||||
boolean done = false;
|
||||
|
||||
@Override
|
||||
public void onObbStateChange(String path, String state) {
|
||||
public void onObbStateChange(String path, int state) {
|
||||
Log.d(TAG, "Received message. path=" + path + ", state=" + state);
|
||||
synchronized (this) {
|
||||
this.path = path;
|
||||
this.state = state;
|
||||
@ -76,23 +73,32 @@ public class MountServiceTests extends AndroidTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
public String getPath() {
|
||||
assertTrue("Expected ObbObserver to have received a state change.", done);
|
||||
return path;
|
||||
}
|
||||
|
||||
public int getState() {
|
||||
assertTrue("Expected ObbObserver to have received a state change.", done);
|
||||
return state;
|
||||
}
|
||||
|
||||
public void reset() {
|
||||
this.path = null;
|
||||
this.state = null;
|
||||
this.state = -1;
|
||||
done = false;
|
||||
}
|
||||
|
||||
public boolean isDone() {
|
||||
return done;
|
||||
}
|
||||
}
|
||||
|
||||
private boolean waitForCompletion(CompletableTask task) {
|
||||
public boolean waitForCompletion() {
|
||||
long waitTime = 0;
|
||||
synchronized (task) {
|
||||
while (!task.isDone() && waitTime < MAX_WAIT_TIME) {
|
||||
synchronized (this) {
|
||||
while (!isDone() && waitTime < MAX_WAIT_TIME) {
|
||||
try {
|
||||
task.wait(WAIT_TIME_INCR);
|
||||
wait(WAIT_TIME_INCR);
|
||||
waitTime += WAIT_TIME_INCR;
|
||||
} catch (InterruptedException e) {
|
||||
Log.i(TAG, "Interrupted during sleep", e);
|
||||
@ -100,8 +106,10 @@ public class MountServiceTests extends AndroidTestCase {
|
||||
}
|
||||
}
|
||||
|
||||
return task.isDone();
|
||||
return isDone();
|
||||
}
|
||||
}
|
||||
|
||||
private File getFilePath(String name) {
|
||||
final File filesDir = mContext.getFilesDir();
|
||||
final File outFile = new File(filesDir, name);
|
||||
@ -128,23 +136,52 @@ public class MountServiceTests extends AndroidTestCase {
|
||||
}
|
||||
|
||||
private void mountObb(StorageManager sm, final int resource, final File file,
|
||||
String expectedState) {
|
||||
int expectedState) {
|
||||
copyRawToFile(resource, file);
|
||||
|
||||
ObbObserver observer = new ObbObserver();
|
||||
final ObbObserver observer = new ObbObserver();
|
||||
assertTrue("mountObb call on " + file.getPath() + " should succeed",
|
||||
sm.mountObb(file.getPath(), null, observer));
|
||||
|
||||
assertTrue("Mount should have completed",
|
||||
waitForCompletion(observer));
|
||||
observer.waitForCompletion());
|
||||
|
||||
assertEquals("Actual file and resolved file should be the same",
|
||||
file.getPath(), observer.path);
|
||||
|
||||
assertEquals(expectedState, observer.state);
|
||||
if (expectedState == OnObbStateChangeListener.MOUNTED) {
|
||||
assertTrue("OBB should be mounted", sm.isObbMounted(file.getPath()));
|
||||
}
|
||||
|
||||
private String checkMountedPath(StorageManager sm, File file) {
|
||||
assertEquals("Actual file and resolved file should be the same",
|
||||
file.getPath(), observer.getPath());
|
||||
|
||||
assertEquals(expectedState, observer.getState());
|
||||
}
|
||||
|
||||
private ObbObserver mountObbWithoutWait(final StorageManager sm, final int resource,
|
||||
final File file) {
|
||||
copyRawToFile(resource, file);
|
||||
|
||||
final ObbObserver observer = new ObbObserver();
|
||||
assertTrue("mountObb call on " + file.getPath() + " should succeed", sm.mountObb(file
|
||||
.getPath(), null, observer));
|
||||
|
||||
return observer;
|
||||
}
|
||||
|
||||
private void waitForObbActionCompletion(final StorageManager sm, final File file,
|
||||
final ObbObserver observer, int expectedState, boolean checkPath) {
|
||||
assertTrue("Mount should have completed", observer.waitForCompletion());
|
||||
|
||||
assertTrue("OBB should be mounted", sm.isObbMounted(file.getPath()));
|
||||
|
||||
if (checkPath) {
|
||||
assertEquals("Actual file and resolved file should be the same", file.getPath(),
|
||||
observer.getPath());
|
||||
}
|
||||
|
||||
assertEquals(expectedState, observer.getState());
|
||||
}
|
||||
|
||||
private String checkMountedPath(final StorageManager sm, final File file) {
|
||||
final String mountPath = sm.getMountedObbPath(file.getPath());
|
||||
assertStartsWith("Path should be in " + OBB_MOUNT_PREFIX,
|
||||
OBB_MOUNT_PREFIX,
|
||||
@ -152,13 +189,21 @@ public class MountServiceTests extends AndroidTestCase {
|
||||
return mountPath;
|
||||
}
|
||||
|
||||
private void unmountObb(StorageManager sm, final File outFile) {
|
||||
ObbObserver observer = new ObbObserver();
|
||||
private void unmountObb(final StorageManager sm, final File file, int expectedState) {
|
||||
final ObbObserver observer = new ObbObserver();
|
||||
|
||||
assertTrue("unmountObb call on test1.obb should succeed",
|
||||
sm.unmountObb(outFile.getPath(), false, observer));
|
||||
sm.unmountObb(file.getPath(),
|
||||
false, observer));
|
||||
|
||||
assertTrue("Unmount should have completed",
|
||||
waitForCompletion(observer));
|
||||
observer.waitForCompletion());
|
||||
|
||||
assertEquals(expectedState, observer.getState());
|
||||
|
||||
if (expectedState == OnObbStateChangeListener.UNMOUNTED) {
|
||||
assertFalse("OBB should not be mounted", sm.isObbMounted(file.getPath()));
|
||||
}
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@ -167,7 +212,9 @@ public class MountServiceTests extends AndroidTestCase {
|
||||
|
||||
final File outFile = getFilePath("test1.obb");
|
||||
|
||||
mountObb(sm, R.raw.test1, outFile, Environment.MEDIA_MOUNTED);
|
||||
mountObb(sm, R.raw.test1, outFile, OnObbStateChangeListener.MOUNTED);
|
||||
|
||||
mountObb(sm, R.raw.test1, outFile, OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
|
||||
|
||||
final String mountPath = checkMountedPath(sm, outFile);
|
||||
final File mountDir = new File(mountPath);
|
||||
@ -175,7 +222,7 @@ public class MountServiceTests extends AndroidTestCase {
|
||||
assertTrue("OBB mounted path should be a directory",
|
||||
mountDir.isDirectory());
|
||||
|
||||
unmountObb(sm, outFile);
|
||||
unmountObb(sm, outFile, OnObbStateChangeListener.UNMOUNTED);
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
@ -184,7 +231,7 @@ public class MountServiceTests extends AndroidTestCase {
|
||||
|
||||
final File outFile = getFilePath("test1_nosig.obb");
|
||||
|
||||
mountObb(sm, R.raw.test1_nosig, outFile, Environment.MEDIA_BAD_REMOVAL);
|
||||
mountObb(sm, R.raw.test1_nosig, outFile, OnObbStateChangeListener.ERROR_INTERNAL);
|
||||
|
||||
assertFalse("OBB should not be mounted",
|
||||
sm.isObbMounted(outFile.getPath()));
|
||||
@ -199,7 +246,8 @@ public class MountServiceTests extends AndroidTestCase {
|
||||
|
||||
final File outFile = getFilePath("test1_wrongpackage.obb");
|
||||
|
||||
mountObb(sm, R.raw.test1_wrongpackage, outFile, Environment.MEDIA_BAD_REMOVAL);
|
||||
mountObb(sm, R.raw.test1_wrongpackage, outFile,
|
||||
OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
|
||||
|
||||
assertFalse("OBB should not be mounted",
|
||||
sm.isObbMounted(outFile.getPath()));
|
||||
@ -207,4 +255,31 @@ public class MountServiceTests extends AndroidTestCase {
|
||||
assertNull("OBB's mounted path should be null",
|
||||
sm.getMountedObbPath(outFile.getPath()));
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
public void testMountAndUnmountTwoObbs() {
|
||||
StorageManager sm = getStorageManager();
|
||||
|
||||
final File file1 = getFilePath("test1.obb");
|
||||
final File file2 = getFilePath("test2.obb");
|
||||
|
||||
ObbObserver oo1 = mountObbWithoutWait(sm, R.raw.test1, file1);
|
||||
ObbObserver oo2 = mountObbWithoutWait(sm, R.raw.test1, file2);
|
||||
|
||||
Log.d(TAG, "Waiting for OBB #1 to complete mount");
|
||||
waitForObbActionCompletion(sm, file1, oo1, OnObbStateChangeListener.MOUNTED, false);
|
||||
Log.d(TAG, "Waiting for OBB #2 to complete mount");
|
||||
waitForObbActionCompletion(sm, file2, oo2, OnObbStateChangeListener.MOUNTED, false);
|
||||
|
||||
final String mountPath1 = checkMountedPath(sm, file1);
|
||||
final File mountDir1 = new File(mountPath1);
|
||||
assertTrue("OBB mounted path should be a directory", mountDir1.isDirectory());
|
||||
|
||||
final String mountPath2 = checkMountedPath(sm, file2);
|
||||
final File mountDir2 = new File(mountPath2);
|
||||
assertTrue("OBB mounted path should be a directory", mountDir2.isDirectory());
|
||||
|
||||
unmountObb(sm, file1, OnObbStateChangeListener.UNMOUNTED);
|
||||
unmountObb(sm, file2, OnObbStateChangeListener.UNMOUNTED);
|
||||
}
|
||||
}
|
||||
|
@ -61,9 +61,9 @@ public:
|
||||
virtual void shutdown(const sp<IMountShutdownObserver>& observer) = 0;
|
||||
virtual void finishMediaUpdate() = 0;
|
||||
virtual void mountObb(const String16& filename, const String16& key,
|
||||
const sp<IObbActionListener>& token) = 0;
|
||||
const sp<IObbActionListener>& token, const int32_t nonce) = 0;
|
||||
virtual void unmountObb(const String16& filename, const bool force,
|
||||
const sp<IObbActionListener>& token) = 0;
|
||||
const sp<IObbActionListener>& token, const int32_t nonce) = 0;
|
||||
virtual bool isObbMounted(const String16& filename) = 0;
|
||||
virtual bool getMountedObbPath(const String16& filename, String16& path) = 0;
|
||||
};
|
||||
|
@ -29,7 +29,7 @@ class IObbActionListener: public IInterface
|
||||
public:
|
||||
DECLARE_META_INTERFACE(ObbActionListener);
|
||||
|
||||
virtual void onObbResult(const String16& filename, const String16& status) = 0;
|
||||
virtual void onObbResult(const String16& filename, const int32_t nonce, const int32_t state) = 0;
|
||||
};
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
|
@ -430,13 +430,14 @@ public:
|
||||
}
|
||||
|
||||
void mountObb(const String16& filename, const String16& key,
|
||||
const sp<IObbActionListener>& token)
|
||||
const sp<IObbActionListener>& token, int32_t nonce)
|
||||
{
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
||||
data.writeString16(filename);
|
||||
data.writeString16(key);
|
||||
data.writeStrongBinder(token->asBinder());
|
||||
data.writeInt32(nonce);
|
||||
if (remote()->transact(TRANSACTION_mountObb, data, &reply) != NO_ERROR) {
|
||||
LOGD("mountObb could not contact remote\n");
|
||||
return;
|
||||
@ -448,7 +449,7 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void unmountObb(const String16& filename, const bool force, const sp<IObbActionListener>& token)
|
||||
void unmountObb(const String16& filename, const bool force)
|
||||
{
|
||||
Parcel data, reply;
|
||||
data.writeInterfaceToken(IMountService::getInterfaceDescriptor());
|
||||
|
@ -30,7 +30,7 @@ public:
|
||||
: BpInterface<IObbActionListener>(impl)
|
||||
{ }
|
||||
|
||||
virtual void onObbResult(const String16& filename, const String16& status) { }
|
||||
virtual void onObbResult(const String16& filename, const int32_t nonce, const int32_t state) { }
|
||||
};
|
||||
|
||||
IMPLEMENT_META_INTERFACE(ObbActionListener, "IObbActionListener");
|
||||
@ -44,8 +44,9 @@ status_t BnObbActionListener::onTransact(
|
||||
case TRANSACTION_onObbResult: {
|
||||
CHECK_INTERFACE(IObbActionListener, data, reply);
|
||||
String16 filename = data.readString16();
|
||||
String16 state = data.readString16();
|
||||
onObbResult(filename, state);
|
||||
int32_t nonce = data.readInt32();
|
||||
int32_t state = data.readInt32();
|
||||
onObbResult(filename, nonce, state);
|
||||
reply->writeNoException();
|
||||
return NO_ERROR;
|
||||
} break;
|
||||
|
@ -21,10 +21,13 @@
|
||||
|
||||
#include <binder/Binder.h>
|
||||
#include <binder/IServiceManager.h>
|
||||
#include <utils/Atomic.h>
|
||||
#include <utils/Log.h>
|
||||
#include <utils/RefBase.h>
|
||||
#include <utils/String8.h>
|
||||
#include <utils/String16.h>
|
||||
#include <utils/Vector.h>
|
||||
#include <utils/threads.h>
|
||||
|
||||
|
||||
using namespace android;
|
||||
@ -38,20 +41,46 @@ public:
|
||||
mStorageManager(mgr)
|
||||
{}
|
||||
|
||||
virtual void onObbResult(const android::String16& filename, const android::String16& state);
|
||||
virtual void onObbResult(const android::String16& filename, const int32_t nonce,
|
||||
const int32_t state);
|
||||
};
|
||||
|
||||
class ObbCallback {
|
||||
public:
|
||||
ObbCallback(int32_t _nonce, AStorageManager_obbCallbackFunc _cb, void* _data)
|
||||
: nonce(_nonce)
|
||||
, cb(_cb)
|
||||
, data(_data)
|
||||
{}
|
||||
|
||||
int32_t nonce;
|
||||
AStorageManager_obbCallbackFunc cb;
|
||||
void* data;
|
||||
};
|
||||
|
||||
struct AStorageManager : public RefBase {
|
||||
protected:
|
||||
AStorageManager_obbCallbackFunc mObbCallback;
|
||||
void* mObbCallbackData;
|
||||
Mutex mCallbackLock;
|
||||
Vector<ObbCallback*> mCallbacks;
|
||||
volatile int32_t mNextNonce;
|
||||
sp<ObbActionListener> mObbActionListener;
|
||||
sp<IMountService> mMountService;
|
||||
|
||||
int32_t getNextNonce() {
|
||||
return android_atomic_inc(&mNextNonce);
|
||||
}
|
||||
|
||||
ObbCallback* registerObbCallback(AStorageManager_obbCallbackFunc func, void* data) {
|
||||
ObbCallback* cb = new ObbCallback(getNextNonce(), func, data);
|
||||
{
|
||||
AutoMutex _l(mCallbackLock);
|
||||
mCallbacks.push(cb);
|
||||
}
|
||||
return cb;
|
||||
}
|
||||
|
||||
public:
|
||||
AStorageManager()
|
||||
: mObbCallback(NULL)
|
||||
, mObbCallbackData(NULL)
|
||||
{
|
||||
}
|
||||
|
||||
@ -73,26 +102,40 @@ public:
|
||||
return true;
|
||||
}
|
||||
|
||||
void setObbCallback(AStorageManager_obbCallbackFunc cb, void* data) {
|
||||
mObbCallback = cb;
|
||||
mObbCallbackData = data;
|
||||
void fireCallback(const char* filename, const int32_t nonce, const int32_t state) {
|
||||
ObbCallback* target = NULL;
|
||||
{
|
||||
AutoMutex _l(mCallbackLock);
|
||||
int N = mCallbacks.size();
|
||||
for (int i = 0; i < N; i++) {
|
||||
ObbCallback* cb = mCallbacks.editItemAt(i);
|
||||
if (cb->nonce == nonce) {
|
||||
target = cb;
|
||||
mCallbacks.removeAt(i);
|
||||
break;
|
||||
}
|
||||
|
||||
void fireCallback(const char* filename, const char* state) {
|
||||
if (mObbCallback != NULL) {
|
||||
mObbCallback(filename, state, mObbCallbackData);
|
||||
}
|
||||
}
|
||||
|
||||
void mountObb(const char* filename, const char* key) {
|
||||
if (target != NULL) {
|
||||
target->cb(filename, state, target->data);
|
||||
delete target;
|
||||
} else {
|
||||
LOGI("Didn't find the callback handler for: %s\n", filename);
|
||||
}
|
||||
}
|
||||
|
||||
void mountObb(const char* filename, const char* key, AStorageManager_obbCallbackFunc func, void* data) {
|
||||
ObbCallback* cb = registerObbCallback(func, data);
|
||||
String16 filename16(filename);
|
||||
String16 key16(key);
|
||||
mMountService->mountObb(filename16, key16, mObbActionListener);
|
||||
mMountService->mountObb(filename16, key16, mObbActionListener, cb->nonce);
|
||||
}
|
||||
|
||||
void unmountObb(const char* filename, const bool force) {
|
||||
void unmountObb(const char* filename, const bool force, AStorageManager_obbCallbackFunc func, void* data) {
|
||||
ObbCallback* cb = registerObbCallback(func, data);
|
||||
String16 filename16(filename);
|
||||
mMountService->unmountObb(filename16, force, mObbActionListener);
|
||||
mMountService->unmountObb(filename16, force, mObbActionListener, cb->nonce);
|
||||
}
|
||||
|
||||
int isObbMounted(const char* filename) {
|
||||
@ -111,8 +154,8 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
void ObbActionListener::onObbResult(const android::String16& filename, const android::String16& state) {
|
||||
mStorageManager->fireCallback(String8(filename).string(), String8(state).string());
|
||||
void ObbActionListener::onObbResult(const android::String16& filename, const int32_t nonce, const int32_t state) {
|
||||
mStorageManager->fireCallback(String8(filename).string(), nonce, state);
|
||||
}
|
||||
|
||||
|
||||
@ -131,16 +174,14 @@ void AStorageManager_delete(AStorageManager* mgr) {
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
AStorageManager_obbCallbackFunc cb, void* data) {
|
||||
mgr->mountObb(filename, key, cb, data);
|
||||
}
|
||||
|
||||
void AStorageManager_mountObb(AStorageManager* mgr, const char* filename, const char* key) {
|
||||
mgr->mountObb(filename, key);
|
||||
}
|
||||
|
||||
void AStorageManager_unmountObb(AStorageManager* mgr, const char* filename, const int force) {
|
||||
mgr->unmountObb(filename, force != 0);
|
||||
void AStorageManager_unmountObb(AStorageManager* mgr, const char* filename, const int force,
|
||||
AStorageManager_obbCallbackFunc cb, void* data) {
|
||||
mgr->unmountObb(filename, force != 0, cb, data);
|
||||
}
|
||||
|
||||
int AStorageManager_isObbMounted(AStorageManager* mgr, const char* filename) {
|
||||
|
@ -18,6 +18,8 @@
|
||||
#ifndef ANDROID_STORAGE_MANAGER_H
|
||||
#define ANDROID_STORAGE_MANAGER_H
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@ -25,6 +27,60 @@ extern "C" {
|
||||
struct AStorageManager;
|
||||
typedef struct AStorageManager AStorageManager;
|
||||
|
||||
enum {
|
||||
/*
|
||||
* The OBB container is now mounted and ready for use. Can be returned
|
||||
* as the status for callbacks made during asynchronous OBB actions.
|
||||
*/
|
||||
AOBB_STATE_MOUNTED = 1,
|
||||
|
||||
/*
|
||||
* The OBB container is now unmounted and not usable. Can be returned
|
||||
* as the status for callbacks made during asynchronous OBB actions.
|
||||
*/
|
||||
AOBB_STATE_UNMOUNTED = 2,
|
||||
|
||||
/*
|
||||
* There was an internal system error encountered while trying to
|
||||
* mount the OBB. Can be returned as the status for callbacks made
|
||||
* during asynchronous OBB actions.
|
||||
*/
|
||||
AOBB_STATE_ERROR_INTERNAL = 20,
|
||||
|
||||
/*
|
||||
* The OBB could not be mounted by the system. Can be returned as the
|
||||
* status for callbacks made during asynchronous OBB actions.
|
||||
*/
|
||||
AOBB_STATE_ERROR_COULD_NOT_MOUNT = 21,
|
||||
|
||||
/*
|
||||
* The OBB could not be unmounted. This most likely indicates that a
|
||||
* file is in use on the OBB. Can be returned as the status for
|
||||
* callbacks made during asynchronous OBB actions.
|
||||
*/
|
||||
AOBB_STATE_ERROR_COULD_NOT_UNMOUNT = 22,
|
||||
|
||||
/*
|
||||
* A call was made to unmount the OBB when it was not mounted. Can be
|
||||
* returned as the status for callbacks made during asynchronous OBB
|
||||
* actions.
|
||||
*/
|
||||
AOBB_STATE_ERROR_NOT_MOUNTED = 23,
|
||||
|
||||
/*
|
||||
* The OBB has already been mounted. Can be returned as the status for
|
||||
* callbacks made during asynchronous OBB actions.
|
||||
*/
|
||||
AOBB_STATE_ERROR_ALREADY_MOUNTED = 24,
|
||||
|
||||
/*
|
||||
* The current application does not have permission to use this OBB
|
||||
* because the OBB indicates it's owned by a different package or the
|
||||
* key used to open it is incorrect. Can be returned as the status for
|
||||
* callbacks made during asynchronous OBB actions.
|
||||
*/
|
||||
AOBB_STATE_ERROR_PERMISSION_DENIED = 25,
|
||||
};
|
||||
|
||||
/**
|
||||
* Obtains a new instance of AStorageManager.
|
||||
@ -39,22 +95,19 @@ void AStorageManager_delete(AStorageManager* mgr);
|
||||
/**
|
||||
* Callback function for asynchronous calls made on OBB files.
|
||||
*/
|
||||
typedef void (*AStorageManager_obbCallbackFunc)(const char* filename, const char* state, void* data);
|
||||
|
||||
/**
|
||||
* Callback to call when requested asynchronous OBB operation is complete.
|
||||
*/
|
||||
void AStorageManager_setObbCallback(AStorageManager* mgr, AStorageManager_obbCallbackFunc cb, void* data);
|
||||
typedef void (*AStorageManager_obbCallbackFunc)(const char* filename, const int32_t state, 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,
|
||||
AStorageManager_obbCallbackFunc cb, void* data);
|
||||
|
||||
/**
|
||||
* 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,
|
||||
AStorageManager_obbCallbackFunc cb, void* data);
|
||||
|
||||
/**
|
||||
* Check whether an OBB is mounted.
|
||||
|
@ -44,6 +44,7 @@ import android.os.storage.IMountService;
|
||||
import android.os.storage.IMountServiceListener;
|
||||
import android.os.storage.IMountShutdownObserver;
|
||||
import android.os.storage.IObbActionListener;
|
||||
import android.os.storage.OnObbStateChangeListener;
|
||||
import android.os.storage.StorageResultCode;
|
||||
import android.security.MessageDigest;
|
||||
import android.util.Slog;
|
||||
@ -53,7 +54,6 @@ import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
@ -161,25 +161,25 @@ class MountService extends IMountService.Stub
|
||||
final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
|
||||
|
||||
class ObbState implements IBinder.DeathRecipient {
|
||||
public ObbState(String filename, IObbActionListener token, int callerUid)
|
||||
public ObbState(String filename, int callerUid, IObbActionListener token, int nonce)
|
||||
throws RemoteException {
|
||||
this.filename = filename;
|
||||
this.token = token;
|
||||
this.callerUid = callerUid;
|
||||
mounted = false;
|
||||
this.token = token;
|
||||
this.nonce = nonce;
|
||||
}
|
||||
|
||||
// OBB source filename
|
||||
final String filename;
|
||||
|
||||
// Token of remote Binder caller
|
||||
final IObbActionListener token;
|
||||
String filename;
|
||||
|
||||
// Binder.callingUid()
|
||||
final public int callerUid;
|
||||
|
||||
// Whether this is mounted currently.
|
||||
boolean mounted;
|
||||
// Token of remote Binder caller
|
||||
final IObbActionListener token;
|
||||
|
||||
// Identifier to pass back to the token
|
||||
final int nonce;
|
||||
|
||||
public IBinder getBinder() {
|
||||
return token.asBinder();
|
||||
@ -208,8 +208,6 @@ class MountService extends IMountService.Stub
|
||||
sb.append(token.toString());
|
||||
sb.append(",callerUid=");
|
||||
sb.append(callerUid);
|
||||
sb.append(",mounted=");
|
||||
sb.append(mounted);
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
@ -223,6 +221,7 @@ class MountService extends IMountService.Stub
|
||||
private static final int OBB_MCS_BOUND = 2;
|
||||
private static final int OBB_MCS_UNBIND = 3;
|
||||
private static final int OBB_MCS_RECONNECT = 4;
|
||||
private static final int OBB_FLUSH_MOUNT_STATE = 5;
|
||||
|
||||
/*
|
||||
* Default Container Service information
|
||||
@ -500,40 +499,23 @@ class MountService extends IMountService.Stub
|
||||
Slog.w(TAG, String.format("Duplicate state transition (%s -> %s)", mLegacyState, state));
|
||||
return;
|
||||
}
|
||||
// Update state on PackageManager
|
||||
|
||||
if (Environment.MEDIA_UNMOUNTED.equals(state)) {
|
||||
// Tell the package manager the media is gone.
|
||||
mPms.updateExternalMediaStatus(false, false);
|
||||
|
||||
/*
|
||||
* Some OBBs might have been unmounted when this volume was
|
||||
* unmounted, so send a message to the handler to let it know to
|
||||
* remove those from the list of mounted OBBS.
|
||||
*/
|
||||
mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_FLUSH_MOUNT_STATE,
|
||||
path));
|
||||
} else if (Environment.MEDIA_MOUNTED.equals(state)) {
|
||||
// Tell the package manager the media is available for use.
|
||||
mPms.updateExternalMediaStatus(true, false);
|
||||
}
|
||||
|
||||
// Remove all OBB mappings and listeners from this path
|
||||
synchronized (mObbMounts) {
|
||||
final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
|
||||
|
||||
final Iterator<Entry<String, ObbState>> i = mObbPathToStateMap.entrySet().iterator();
|
||||
while (i.hasNext()) {
|
||||
final Entry<String, ObbState> obbEntry = i.next();
|
||||
|
||||
// If this entry's source file is in the volume path that got
|
||||
// unmounted, remove it because it's no longer valid.
|
||||
if (obbEntry.getKey().startsWith(path)) {
|
||||
obbStatesToRemove.add(obbEntry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
for (final ObbState obbState : obbStatesToRemove) {
|
||||
removeObbState(obbState);
|
||||
|
||||
try {
|
||||
obbState.token.onObbResult(obbState.filename, Environment.MEDIA_UNMOUNTED);
|
||||
} catch (RemoteException e) {
|
||||
Slog.i(TAG, "Couldn't send unmount notification for OBB: "
|
||||
+ obbState.filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
String oldState = mLegacyState;
|
||||
mLegacyState = state;
|
||||
|
||||
@ -1530,6 +1512,10 @@ class MountService extends IMountService.Stub
|
||||
}
|
||||
|
||||
public String getMountedObbPath(String filename) {
|
||||
if (filename == null) {
|
||||
throw new IllegalArgumentException("filename cannot be null");
|
||||
}
|
||||
|
||||
waitForReady();
|
||||
warnOnNotMounted();
|
||||
|
||||
@ -1552,127 +1538,72 @@ class MountService extends IMountService.Stub
|
||||
}
|
||||
|
||||
public boolean isObbMounted(String filename) {
|
||||
synchronized (mObbMounts) {
|
||||
final ObbState obbState = mObbPathToStateMap.get(filename);
|
||||
if (obbState != null) {
|
||||
synchronized (obbState) {
|
||||
return obbState.mounted;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void mountObb(String filename, String key, IObbActionListener token)
|
||||
throws RemoteException {
|
||||
waitForReady();
|
||||
warnOnNotMounted();
|
||||
|
||||
if (filename == null) {
|
||||
throw new IllegalArgumentException("filename cannot be null");
|
||||
} else if (token == null) {
|
||||
throw new IllegalArgumentException("token cannot be null");
|
||||
}
|
||||
|
||||
final ObbState obbState;
|
||||
|
||||
synchronized (mObbMounts) {
|
||||
if (isObbMounted(filename)) {
|
||||
try {
|
||||
token.onObbResult(filename, Environment.MEDIA_MOUNTED);
|
||||
} catch (RemoteException e) {
|
||||
Slog.d(TAG, "Could not send unmount notification for: " + filename);
|
||||
return mObbPathToStateMap.containsKey(filename);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
public void mountObb(String filename, String key, IObbActionListener token, int nonce)
|
||||
throws RemoteException {
|
||||
if (filename == null) {
|
||||
throw new IllegalArgumentException("filename cannot be null");
|
||||
}
|
||||
|
||||
if (token == null) {
|
||||
throw new IllegalArgumentException("token cannot be null");
|
||||
}
|
||||
|
||||
final int callerUid = Binder.getCallingUid();
|
||||
obbState = new ObbState(filename, token, callerUid);
|
||||
addObbState(obbState);
|
||||
}
|
||||
|
||||
String hashedKey = null;
|
||||
if (key != null) {
|
||||
final MessageDigest md;
|
||||
try {
|
||||
md = MessageDigest.getInstance("MD5");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
Slog.e(TAG, "Could not load MD5 algorithm", e);
|
||||
try {
|
||||
token.onObbResult(filename, Environment.MEDIA_UNMOUNTED);
|
||||
} catch (RemoteException e1) {
|
||||
Slog.d(TAG, "Could not send unmount notification for: " + filename);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
hashedKey = HexDump.toHexString(md.digest(key.getBytes()));
|
||||
}
|
||||
|
||||
ObbAction action = new MountObbAction(obbState, hashedKey);
|
||||
final ObbState obbState = new ObbState(filename, callerUid, token, nonce);
|
||||
final ObbAction action = new MountObbAction(obbState, key);
|
||||
mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
|
||||
|
||||
if (DEBUG_OBB)
|
||||
Slog.i(TAG, "Send to OBB handler: " + action.toString());
|
||||
}
|
||||
|
||||
public void unmountObb(String filename, boolean force, IObbActionListener token) {
|
||||
public void unmountObb(String filename, boolean force, IObbActionListener token, int nonce)
|
||||
throws RemoteException {
|
||||
if (filename == null) {
|
||||
throw new IllegalArgumentException("filename cannot be null");
|
||||
} else if (token == null) {
|
||||
throw new IllegalArgumentException("token cannot be null");
|
||||
}
|
||||
|
||||
final ObbState obbState;
|
||||
|
||||
synchronized (mObbMounts) {
|
||||
if (!isObbMounted(filename)) {
|
||||
try {
|
||||
token.onObbResult(filename, Environment.MEDIA_UNMOUNTED);
|
||||
} catch (RemoteException e) {
|
||||
Slog.d(TAG, "Could not send unmount notification for: " + filename);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
obbState = mObbPathToStateMap.get(filename);
|
||||
|
||||
if (Binder.getCallingUid() != obbState.callerUid) {
|
||||
throw new SecurityException("caller UID does not match original mount caller UID");
|
||||
} else if (!token.asBinder().equals(obbState.getBinder())) {
|
||||
throw new SecurityException("caller does not match original mount caller");
|
||||
}
|
||||
}
|
||||
|
||||
ObbAction action = new UnmountObbAction(obbState, force);
|
||||
final int callerUid = Binder.getCallingUid();
|
||||
final ObbState obbState = new ObbState(filename, callerUid, token, nonce);
|
||||
final ObbAction action = new UnmountObbAction(obbState, force);
|
||||
mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
|
||||
|
||||
if (DEBUG_OBB)
|
||||
Slog.i(TAG, "Send to OBB handler: " + action.toString());
|
||||
}
|
||||
|
||||
private void addObbState(ObbState obbState) throws RemoteException {
|
||||
synchronized (mObbMounts) {
|
||||
private void addObbStateLocked(ObbState obbState) throws RemoteException {
|
||||
final IBinder binder = obbState.getBinder();
|
||||
List<ObbState> obbStates = mObbMounts.get(binder);
|
||||
final boolean unique;
|
||||
|
||||
if (obbStates == null) {
|
||||
obbStates = new ArrayList<ObbState>();
|
||||
mObbMounts.put(binder, obbStates);
|
||||
unique = true;
|
||||
} else {
|
||||
unique = obbStates.contains(obbState);
|
||||
for (final ObbState o : obbStates) {
|
||||
if (o.filename.equals(obbState.filename)) {
|
||||
throw new IllegalStateException("Attempt to add ObbState twice. "
|
||||
+ "This indicates an error in the MountService logic.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (unique) {
|
||||
obbStates.add(obbState);
|
||||
try {
|
||||
obbState.link();
|
||||
} catch (RemoteException e) {
|
||||
/*
|
||||
* The binder died before we could link it, so clean up our
|
||||
* state and return failure.
|
||||
* The binder died before we could link it, so clean up our state
|
||||
* and return failure.
|
||||
*/
|
||||
obbStates.remove(obbState);
|
||||
if (obbStates.isEmpty()) {
|
||||
@ -1682,14 +1613,11 @@ class MountService extends IMountService.Stub
|
||||
// Rethrow the error so mountObb can get it
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
mObbPathToStateMap.put(obbState.filename, obbState);
|
||||
}
|
||||
}
|
||||
|
||||
private void removeObbState(ObbState obbState) {
|
||||
synchronized (mObbMounts) {
|
||||
private void removeObbStateLocked(ObbState obbState) {
|
||||
final IBinder binder = obbState.getBinder();
|
||||
final List<ObbState> obbStates = mObbMounts.get(binder);
|
||||
if (obbStates != null) {
|
||||
@ -1703,14 +1631,6 @@ class MountService extends IMountService.Stub
|
||||
|
||||
mObbPathToStateMap.remove(obbState.filename);
|
||||
}
|
||||
}
|
||||
|
||||
private void replaceObbState(ObbState oldObbState, ObbState newObbState) throws RemoteException {
|
||||
synchronized (mObbMounts) {
|
||||
removeObbState(oldObbState);
|
||||
addObbState(newObbState);
|
||||
}
|
||||
}
|
||||
|
||||
private class ObbActionHandler extends Handler {
|
||||
private boolean mBound = false;
|
||||
@ -1808,6 +1728,47 @@ class MountService extends IMountService.Stub
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OBB_FLUSH_MOUNT_STATE: {
|
||||
final String path = (String) msg.obj;
|
||||
|
||||
if (DEBUG_OBB)
|
||||
Slog.i(TAG, "Flushing all OBB state for path " + path);
|
||||
|
||||
synchronized (mObbMounts) {
|
||||
final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>();
|
||||
|
||||
final Iterator<Entry<String, ObbState>> i =
|
||||
mObbPathToStateMap.entrySet().iterator();
|
||||
while (i.hasNext()) {
|
||||
final Entry<String, ObbState> obbEntry = i.next();
|
||||
|
||||
/*
|
||||
* If this entry's source file is in the volume path
|
||||
* that got unmounted, remove it because it's no
|
||||
* longer valid.
|
||||
*/
|
||||
if (obbEntry.getKey().startsWith(path)) {
|
||||
obbStatesToRemove.add(obbEntry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
for (final ObbState obbState : obbStatesToRemove) {
|
||||
if (DEBUG_OBB)
|
||||
Slog.i(TAG, "Removing state for " + obbState.filename);
|
||||
|
||||
removeObbStateLocked(obbState);
|
||||
|
||||
try {
|
||||
obbState.token.onObbResult(obbState.filename, obbState.nonce,
|
||||
OnObbStateChangeListener.UNMOUNTED);
|
||||
} catch (RemoteException e) {
|
||||
Slog.i(TAG, "Couldn't send unmount notification for OBB: "
|
||||
+ obbState.filename);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1886,9 +1847,13 @@ class MountService extends IMountService.Stub
|
||||
return obbInfo;
|
||||
}
|
||||
|
||||
protected void sendNewStatusOrIgnore(String filename, String status) {
|
||||
protected void sendNewStatusOrIgnore(int status) {
|
||||
if (mObbState == null || mObbState.token == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
mObbState.token.onObbResult(filename, status);
|
||||
mObbState.token.onObbResult(mObbState.filename, mObbState.nonce, status);
|
||||
} catch (RemoteException e) {
|
||||
Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
|
||||
}
|
||||
@ -1904,58 +1869,52 @@ class MountService extends IMountService.Stub
|
||||
}
|
||||
|
||||
public void handleExecute() throws IOException, RemoteException {
|
||||
waitForReady();
|
||||
warnOnNotMounted();
|
||||
|
||||
final ObbInfo obbInfo = getObbInfo();
|
||||
|
||||
/*
|
||||
* If someone tried to trick us with some weird characters, rectify
|
||||
* it here.
|
||||
*/
|
||||
if (!mObbState.filename.equals(obbInfo.filename)) {
|
||||
if (DEBUG_OBB)
|
||||
Slog.i(TAG, "OBB filename " + mObbState.filename + " is actually "
|
||||
+ obbInfo.filename);
|
||||
|
||||
synchronized (mObbMounts) {
|
||||
/*
|
||||
* If the real filename is already mounted, discard this
|
||||
* state and notify the caller that the OBB is already
|
||||
* mounted.
|
||||
*/
|
||||
if (isObbMounted(obbInfo.filename)) {
|
||||
if (DEBUG_OBB)
|
||||
Slog.i(TAG, "OBB already mounted as " + obbInfo.filename);
|
||||
|
||||
removeObbState(mObbState);
|
||||
sendNewStatusOrIgnore(obbInfo.filename, Environment.MEDIA_MOUNTED);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* It's not already mounted, so we have to replace the state
|
||||
* with the state containing the actual filename.
|
||||
*/
|
||||
ObbState newObbState = new ObbState(obbInfo.filename, mObbState.token,
|
||||
mObbState.callerUid);
|
||||
replaceObbState(mObbState, newObbState);
|
||||
mObbState = newObbState;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mObbState.callerUid)) {
|
||||
throw new IllegalArgumentException("Caller package does not match OBB file");
|
||||
}
|
||||
|
||||
boolean mounted = false;
|
||||
int rc;
|
||||
synchronized (mObbState) {
|
||||
if (mObbState.mounted) {
|
||||
sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_MOUNTED);
|
||||
Slog.w(TAG, "Denied attempt to mount OBB " + obbInfo.filename
|
||||
+ " which is owned by " + obbInfo.packageName);
|
||||
sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
|
||||
return;
|
||||
}
|
||||
|
||||
rc = StorageResultCode.OperationSucceeded;
|
||||
String cmd = String.format("obb mount %s %s %d", mObbState.filename,
|
||||
mKey != null ? mKey : "none",
|
||||
final boolean isMounted;
|
||||
synchronized (mObbMounts) {
|
||||
isMounted = mObbPathToStateMap.containsKey(obbInfo.filename);
|
||||
}
|
||||
if (isMounted) {
|
||||
Slog.w(TAG, "Attempt to mount OBB which is already mounted: " + obbInfo.filename);
|
||||
sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_ALREADY_MOUNTED);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* The filename passed in might not be the canonical name, so just
|
||||
* set the filename to the canonicalized version.
|
||||
*/
|
||||
mObbState.filename = obbInfo.filename;
|
||||
|
||||
final String hashedKey;
|
||||
if (mKey == null) {
|
||||
hashedKey = "none";
|
||||
} else {
|
||||
final MessageDigest md;
|
||||
try {
|
||||
md = MessageDigest.getInstance("MD5");
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
Slog.e(TAG, "Could not load MD5 algorithm", e);
|
||||
sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
|
||||
return;
|
||||
}
|
||||
|
||||
hashedKey = HexDump.toHexString(md.digest(mKey.getBytes()));
|
||||
}
|
||||
|
||||
int rc = StorageResultCode.OperationSucceeded;
|
||||
String cmd = String.format("obb mount %s %s %d", mObbState.filename, hashedKey,
|
||||
mObbState.callerUid);
|
||||
try {
|
||||
mConnector.doCommand(cmd);
|
||||
@ -1967,26 +1926,23 @@ class MountService extends IMountService.Stub
|
||||
}
|
||||
|
||||
if (rc == StorageResultCode.OperationSucceeded) {
|
||||
mObbState.mounted = mounted = true;
|
||||
}
|
||||
if (DEBUG_OBB)
|
||||
Slog.d(TAG, "Successfully mounted OBB " + mObbState.filename);
|
||||
|
||||
synchronized (mObbMounts) {
|
||||
addObbStateLocked(mObbState);
|
||||
}
|
||||
|
||||
if (mounted) {
|
||||
sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_MOUNTED);
|
||||
sendNewStatusOrIgnore(OnObbStateChangeListener.MOUNTED);
|
||||
} else {
|
||||
Slog.e(TAG, "Couldn't mount OBB file: " + rc);
|
||||
|
||||
// We didn't succeed, so remove this from the mount-set.
|
||||
removeObbState(mObbState);
|
||||
|
||||
sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_UNMOUNTED);
|
||||
sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleError() {
|
||||
removeObbState(mObbState);
|
||||
|
||||
sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
|
||||
sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -1999,6 +1955,8 @@ class MountService extends IMountService.Stub
|
||||
sb.append(mObbState.callerUid);
|
||||
sb.append(",token=");
|
||||
sb.append(mObbState.token != null ? mObbState.token.toString() : "NULL");
|
||||
sb.append(",binder=");
|
||||
sb.append(mObbState.token != null ? mObbState.getBinder().toString() : "null");
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
@ -2013,32 +1971,30 @@ class MountService extends IMountService.Stub
|
||||
}
|
||||
|
||||
public void handleExecute() throws IOException {
|
||||
waitForReady();
|
||||
warnOnNotMounted();
|
||||
|
||||
final ObbInfo obbInfo = getObbInfo();
|
||||
|
||||
/*
|
||||
* If someone tried to trick us with some weird characters, rectify
|
||||
* it here.
|
||||
*/
|
||||
final ObbState obbState;
|
||||
synchronized (mObbMounts) {
|
||||
if (!isObbMounted(obbInfo.filename)) {
|
||||
removeObbState(mObbState);
|
||||
sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_UNMOUNTED);
|
||||
obbState = mObbPathToStateMap.get(obbInfo.filename);
|
||||
}
|
||||
|
||||
if (obbState == null) {
|
||||
sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_NOT_MOUNTED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mObbState.filename.equals(obbInfo.filename)) {
|
||||
removeObbState(mObbState);
|
||||
mObbState = mObbPathToStateMap.get(obbInfo.filename);
|
||||
}
|
||||
}
|
||||
|
||||
boolean unmounted = false;
|
||||
synchronized (mObbState) {
|
||||
if (!mObbState.mounted) {
|
||||
sendNewStatusOrIgnore(obbInfo.filename, Environment.MEDIA_UNMOUNTED);
|
||||
if (obbState.callerUid != mObbState.callerUid) {
|
||||
Slog.w(TAG, "Permission denied attempting to unmount OBB " + obbInfo.filename
|
||||
+ " (owned by " + obbInfo.packageName + ")");
|
||||
sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED);
|
||||
return;
|
||||
}
|
||||
|
||||
mObbState.filename = obbInfo.filename;
|
||||
|
||||
int rc = StorageResultCode.OperationSucceeded;
|
||||
String cmd = String.format("obb unmount %s%s", mObbState.filename,
|
||||
(mForceUnmount ? " force" : ""));
|
||||
@ -2057,24 +2013,19 @@ class MountService extends IMountService.Stub
|
||||
}
|
||||
|
||||
if (rc == StorageResultCode.OperationSucceeded) {
|
||||
mObbState.mounted = false;
|
||||
unmounted = true;
|
||||
}
|
||||
synchronized (mObbMounts) {
|
||||
removeObbStateLocked(obbState);
|
||||
}
|
||||
|
||||
if (unmounted) {
|
||||
removeObbState(mObbState);
|
||||
|
||||
sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_UNMOUNTED);
|
||||
sendNewStatusOrIgnore(OnObbStateChangeListener.UNMOUNTED);
|
||||
} else {
|
||||
sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_MOUNTED);
|
||||
Slog.w(TAG, "Could not mount OBB: " + mObbState.filename);
|
||||
sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT);
|
||||
}
|
||||
}
|
||||
|
||||
public void handleError() {
|
||||
removeObbState(mObbState);
|
||||
|
||||
sendNewStatusOrIgnore(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
|
||||
sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -2090,7 +2041,7 @@ class MountService extends IMountService.Stub
|
||||
sb.append(",token=");
|
||||
sb.append(mObbState.token != null ? mObbState.token.toString() : "null");
|
||||
sb.append(",binder=");
|
||||
sb.append(mObbState.getBinder().toString());
|
||||
sb.append(mObbState.token != null ? mObbState.getBinder().toString() : "null");
|
||||
sb.append('}');
|
||||
return sb.toString();
|
||||
}
|
||||
@ -2105,16 +2056,27 @@ class MountService extends IMountService.Stub
|
||||
return;
|
||||
}
|
||||
|
||||
synchronized (mObbMounts) {
|
||||
pw.println(" mObbMounts:");
|
||||
|
||||
synchronized (mObbMounts) {
|
||||
final Collection<List<ObbState>> obbStateLists = mObbMounts.values();
|
||||
|
||||
for (final List<ObbState> obbStates : obbStateLists) {
|
||||
final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet().iterator();
|
||||
while (binders.hasNext()) {
|
||||
Entry<IBinder, List<ObbState>> e = binders.next();
|
||||
pw.print(" Key="); pw.println(e.getKey().toString());
|
||||
final List<ObbState> obbStates = e.getValue();
|
||||
for (final ObbState obbState : obbStates) {
|
||||
pw.print(" "); pw.println(obbState.toString());
|
||||
}
|
||||
}
|
||||
|
||||
pw.println("");
|
||||
pw.println(" mObbPathToStateMap:");
|
||||
final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator();
|
||||
while (maps.hasNext()) {
|
||||
final Entry<String, ObbState> e = maps.next();
|
||||
pw.print(" "); pw.print(e.getKey());
|
||||
pw.print(" -> "); pw.println(e.getValue().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user