Merge "Move OBB file reading to DefaultContainerService" into gingerbread

This commit is contained in:
Kenny Root
2010-08-11 14:06:39 -07:00
committed by Android (Google) Code Review
8 changed files with 572 additions and 112 deletions

View File

@ -121,6 +121,7 @@ LOCAL_SRC_FILES += \
core/java/android/os/storage/IMountService.aidl \ core/java/android/os/storage/IMountService.aidl \
core/java/android/os/storage/IMountServiceListener.aidl \ core/java/android/os/storage/IMountServiceListener.aidl \
core/java/android/os/storage/IMountShutdownObserver.aidl \ core/java/android/os/storage/IMountShutdownObserver.aidl \
core/java/android/os/storage/IObbActionListener.aidl \
core/java/android/os/INetworkManagementService.aidl \ core/java/android/os/INetworkManagementService.aidl \
core/java/android/os/INetStatService.aidl \ core/java/android/os/INetStatService.aidl \
core/java/android/os/IPermissionController.aidl \ core/java/android/os/IPermissionController.aidl \

View File

@ -129531,6 +129531,8 @@
> >
<parameter name="filename" type="java.lang.String"> <parameter name="filename" type="java.lang.String">
</parameter> </parameter>
<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
</exception>
</method> </method>
<method name="mountObb" <method name="mountObb"
return="boolean" return="boolean"
@ -129561,6 +129563,8 @@
</parameter> </parameter>
<parameter name="force" type="boolean"> <parameter name="force" type="boolean">
</parameter> </parameter>
<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
</exception>
</method> </method>
</class> </class>
</package> </package>

View File

@ -19,6 +19,7 @@ package android.os.storage;
import android.os.storage.IMountServiceListener; import android.os.storage.IMountServiceListener;
import android.os.storage.IMountShutdownObserver; import android.os.storage.IMountShutdownObserver;
import android.os.storage.IObbActionListener;
/** WARNING! Update IMountService.h and IMountService.cpp if you change this file. /** WARNING! Update IMountService.h and IMountService.cpp if you change this file.
* In particular, the ordering of the methods below must match the * In particular, the ordering of the methods below must match the
@ -156,14 +157,20 @@ interface IMountService
/** /**
* Mounts an Opaque Binary Blob (OBB) with the specified decryption key and only * Mounts an Opaque Binary Blob (OBB) with the specified decryption key and only
* allows the calling process's UID access to the contents. * allows the calling process's UID access to the contents.
*
* MountService will call back to the supplied IObbActionListener to inform
* it of the terminal state of the call.
*/ */
int mountObb(String filename, String key); void mountObb(String filename, String key, IObbActionListener token);
/** /**
* Unmounts an Opaque Binary Blob (OBB). When the force flag is specified, any * Unmounts an Opaque Binary Blob (OBB). When the force flag is specified, any
* program using it will be forcibly killed to unmount the image. * program using it will be forcibly killed to unmount the image.
*
* MountService will call back to the supplied IObbActionListener to inform
* it of the terminal state of the call.
*/ */
int unmountObb(String filename, boolean force); void unmountObb(String filename, boolean force, IObbActionListener token);
/** /**
* Checks whether the specified Opaque Binary Blob (OBB) is mounted somewhere. * Checks whether the specified Opaque Binary Blob (OBB) is mounted somewhere.

View File

@ -0,0 +1,34 @@
/*
* 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;
/**
* Callback class for receiving events from MountService about
* Opaque Binary Blobs (OBBs).
*
* @hide - Applications should use android.os.storage.StorageManager
* to interact with OBBs.
*/
interface IObbActionListener {
/**
* Return from an OBB action result.
*
* @param filename the path to the OBB the operation was performed on
* @param returnCode status of the operation
*/
void onObbResult(String filename, String status);
}

View File

@ -16,28 +16,14 @@
package android.os.storage; package android.os.storage;
import android.content.Context;
import android.os.Binder;
import android.os.Bundle;
import android.os.Looper;
import android.os.Parcelable;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.RemoteException;
import android.os.Handler; import android.os.Handler;
import android.os.Looper;
import android.os.Message; import android.os.Message;
import android.os.RemoteException;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.os.storage.IMountService;
import android.os.storage.IMountServiceListener;
import android.util.Log; import android.util.Log;
import android.util.SparseArray;
import java.io.FileDescriptor;
import java.io.IOException;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
/** /**
* StorageManager is the interface to the systems storage service. * StorageManager is the interface to the systems storage service.
@ -86,6 +72,17 @@ public class StorageManager
} }
} }
/**
* Binder listener for OBB action results.
*/
private final ObbActionBinderListener mObbActionListener = new ObbActionBinderListener();
private class ObbActionBinderListener extends IObbActionListener.Stub {
@Override
public void onObbResult(String filename, String status) throws RemoteException {
Log.i(TAG, "filename = " + filename + ", result = " + status);
}
}
/** /**
* Private base class for messages sent between the callback thread * Private base class for messages sent between the callback thread
* and the target looper handler. * and the target looper handler.
@ -299,12 +296,23 @@ public class StorageManager
} }
/** /**
* Mount an OBB file. * Mount an Opaque Binary Blob (OBB) file. If a <code>key</code> is
* specified, it is supplied to the mounting process to be used in any
* encryption used in the OBB.
* <p>
* <em>Note:</em> you can only mount OBB files for which the OBB tag on the
* file matches a package ID that is owned by the calling program's UID.
* That is, shared UID applications can obtain access to any other
* application's OBB that shares its UID.
*
* @param filename the path to the OBB file
* @param key decryption key
* @return whether the mount call was successfully queued or not
*/ */
public boolean mountObb(String filename, String key) { public boolean mountObb(String filename, String key) {
try { try {
return mMountService.mountObb(filename, key) mMountService.mountObb(filename, key, mObbActionListener);
== StorageResultCode.OperationSucceeded; return true;
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(TAG, "Failed to mount OBB", e); Log.e(TAG, "Failed to mount OBB", e);
} }
@ -313,12 +321,24 @@ public class StorageManager
} }
/** /**
* Mount an OBB file. * Unmount an Opaque Binary Blob (OBB) file. If the <code>force</code> flag
* is true, it will kill any application needed to unmount the given OBB.
* <p>
* <em>Note:</em> you can only mount OBB files for which the OBB tag on the
* file matches a package ID that is owned by the calling program's UID.
* That is, shared UID applications can obtain access to any other
* application's OBB that shares its UID.
*
* @param filename path to the OBB file
* @param force whether to kill any programs using this in order to unmount
* it
* @return whether the unmount call was successfully queued or not
* @throws IllegalArgumentException when OBB is not already mounted
*/ */
public boolean unmountObb(String filename, boolean force) { public boolean unmountObb(String filename, boolean force) throws IllegalArgumentException {
try { try {
return mMountService.unmountObb(filename, force) mMountService.unmountObb(filename, force, mObbActionListener);
== StorageResultCode.OperationSucceeded; return true;
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(TAG, "Failed to mount OBB", e); Log.e(TAG, "Failed to mount OBB", e);
} }
@ -326,7 +346,13 @@ public class StorageManager
return false; return false;
} }
public boolean isObbMounted(String filename) { /**
* Check whether an Opaque Binary Blob (OBB) is mounted or not.
*
* @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 {
try { try {
return mMountService.isObbMounted(filename); return mMountService.isObbMounted(filename);
} catch (RemoteException e) { } catch (RemoteException e) {
@ -337,13 +363,21 @@ public class StorageManager
} }
/** /**
* Check the mounted path of an OBB file. * Check the mounted path of an Opaque Binary Blob (OBB) file. This will
* give you the path to where you can obtain access to the internals of the
* OBB.
*
* @param filename path to OBB image
* @return absolute path to mounted OBB image data or <code>null</code> if
* not mounted or exception encountered trying to read status
*/ */
public String getMountedObbPath(String filename) { public String getMountedObbPath(String filename) {
try { try {
return mMountService.getMountedObbPath(filename); return mMountService.getMountedObbPath(filename);
} catch (RemoteException e) { } catch (RemoteException e) {
Log.e(TAG, "Failed to find mounted path for OBB", 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; return null;

View File

@ -19,6 +19,7 @@ package com.android.internal.app;
import android.net.Uri; import android.net.Uri;
import android.os.ParcelFileDescriptor; import android.os.ParcelFileDescriptor;
import android.content.pm.PackageInfoLite; import android.content.pm.PackageInfoLite;
import android.content.res.ObbInfo;
interface IMediaContainerService { interface IMediaContainerService {
String copyResourceToContainer(in Uri packageURI, String copyResourceToContainer(in Uri packageURI,
@ -28,4 +29,5 @@ interface IMediaContainerService {
in ParcelFileDescriptor outStream); in ParcelFileDescriptor outStream);
PackageInfoLite getMinimalPackageInfo(in Uri fileUri, int flags); PackageInfoLite getMinimalPackageInfo(in Uri fileUri, int flags);
boolean checkFreeStorage(boolean external, in Uri fileUri); boolean checkFreeStorage(boolean external, in Uri fileUri);
ObbInfo getObbInfo(String filename);
} }

View File

@ -24,6 +24,8 @@ import android.content.pm.PackageInfo;
import android.content.pm.PackageInfoLite; import android.content.pm.PackageInfoLite;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.PackageParser; import android.content.pm.PackageParser;
import android.content.res.ObbInfo;
import android.content.res.ObbScanner;
import android.net.Uri; import android.net.Uri;
import android.os.Environment; import android.os.Environment;
import android.os.IBinder; import android.os.IBinder;
@ -142,6 +144,10 @@ public class DefaultContainerService extends IntentService {
public boolean checkFreeStorage(boolean external, Uri fileUri) { public boolean checkFreeStorage(boolean external, Uri fileUri) {
return checkFreeStorageInner(external, fileUri); return checkFreeStorageInner(external, fileUri);
} }
public ObbInfo getObbInfo(String filename) {
return ObbScanner.getObbInfo(filename);
}
}; };
public DefaultContainerService() { public DefaultContainerService() {

View File

@ -16,34 +16,42 @@
package com.android.server; package com.android.server;
import com.android.internal.app.IMediaContainerService;
import com.android.server.am.ActivityManagerService; import com.android.server.am.ActivityManagerService;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.res.ObbInfo; import android.content.res.ObbInfo;
import android.content.res.ObbScanner;
import android.net.Uri; import android.net.Uri;
import android.os.storage.IMountService;
import android.os.storage.IMountServiceListener;
import android.os.storage.IMountShutdownObserver;
import android.os.storage.StorageResultCode;
import android.os.Binder; import android.os.Binder;
import android.os.Environment;
import android.os.Handler; import android.os.Handler;
import android.os.HandlerThread; import android.os.HandlerThread;
import android.os.IBinder;
import android.os.Looper; import android.os.Looper;
import android.os.Message; import android.os.Message;
import android.os.RemoteException; import android.os.RemoteException;
import android.os.IBinder;
import android.os.Environment;
import android.os.ServiceManager; import android.os.ServiceManager;
import android.os.SystemClock; import android.os.SystemClock;
import android.os.SystemProperties; import android.os.SystemProperties;
import android.os.storage.IMountService;
import android.os.storage.IMountServiceListener;
import android.os.storage.IMountShutdownObserver;
import android.os.storage.IObbActionListener;
import android.os.storage.StorageResultCode;
import android.util.Slog; import android.util.Slog;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
/** /**
* MountService implements back-end services for platform storage * MountService implements back-end services for platform storage
@ -135,9 +143,77 @@ class MountService extends IMountService.Stub
final private HashSet<String> mAsecMountSet = new HashSet<String>(); final private HashSet<String> mAsecMountSet = new HashSet<String>();
/** /**
* Private hash of currently mounted filesystem images. * Mounted OBB tracking information. Used to track the current state of all
* OBBs.
*/ */
final private HashSet<String> mObbMountSet = new HashSet<String>(); final private Map<IObbActionListener, ObbState> mObbMounts = new HashMap<IObbActionListener, ObbState>();
final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
class ObbState implements IBinder.DeathRecipient {
public ObbState(String filename, IObbActionListener token, int callerUid) {
this.filename = filename;
this.token = token;
this.callerUid = callerUid;
mounted = false;
}
// OBB source filename
String filename;
// Token of remote Binder caller
IObbActionListener token;
// Binder.callingUid()
public int callerUid;
// Whether this is mounted currently.
boolean mounted;
@Override
public void binderDied() {
ObbAction action = new UnmountObbAction(this, true);
mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
removeObbState(this);
token.asBinder().unlinkToDeath(this, 0);
}
}
// OBB Action Handler
final private ObbActionHandler mObbActionHandler;
// OBB action handler messages
private static final int OBB_RUN_ACTION = 1;
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_MCS_GIVE_UP = 5;
/*
* Default Container Service information
*/
static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
"com.android.defcontainer", "com.android.defcontainer.DefaultContainerService");
final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
class DefaultContainerConnection implements ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service) {
if (DEBUG_OBB)
Slog.i(TAG, "onServiceConnected");
IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service);
mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs));
}
public void onServiceDisconnected(ComponentName name) {
if (DEBUG_OBB)
Slog.i(TAG, "onServiceDisconnected");
}
};
// Used in the ObbActionHandler
private IMediaContainerService mContainerService = null;
// Handler messages // Handler messages
private static final int H_UNMOUNT_PM_UPDATE = 1; private static final int H_UNMOUNT_PM_UPDATE = 1;
@ -359,7 +435,7 @@ class MountService extends IMountService.Stub
public void binderDied() { public void binderDied() {
if (LOCAL_LOGD) Slog.d(TAG, "An IMountServiceListener has died!"); if (LOCAL_LOGD) Slog.d(TAG, "An IMountServiceListener has died!");
synchronized(mListeners) { synchronized (mListeners) {
mListeners.remove(this); mListeners.remove(this);
mListener.asBinder().unlinkToDeath(this, 0); mListener.asBinder().unlinkToDeath(this, 0);
} }
@ -904,6 +980,9 @@ class MountService extends IMountService.Stub
mHandlerThread.start(); mHandlerThread.start();
mHandler = new MountServiceHandler(mHandlerThread.getLooper()); mHandler = new MountServiceHandler(mHandlerThread.getLooper());
// Add OBB Action Handler to MountService thread.
mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper());
/* /*
* Vold does not run in the simulator, so pretend the connector thread * Vold does not run in the simulator, so pretend the connector thread
* ran and did its thing. * ran and did its thing.
@ -1338,12 +1417,16 @@ class MountService extends IMountService.Stub
mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE); mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
} }
private boolean isCallerOwnerOfPackage(String packageName) { private boolean isCallerOwnerOfPackageOrSystem(String packageName) {
final int callerUid = Binder.getCallingUid(); final int callerUid = Binder.getCallingUid();
return isUidOwnerOfPackage(packageName, callerUid); return isUidOwnerOfPackageOrSystem(packageName, callerUid);
} }
private boolean isUidOwnerOfPackage(String packageName, int callerUid) { private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
if (callerUid == android.os.Process.SYSTEM_UID) {
return true;
}
if (packageName == null) { if (packageName == null) {
return false; return false;
} }
@ -1362,12 +1445,6 @@ class MountService extends IMountService.Stub
waitForReady(); waitForReady();
warnOnNotMounted(); warnOnNotMounted();
// XXX replace with call to IMediaContainerService
ObbInfo obbInfo = ObbScanner.getObbInfo(filename);
if (!isCallerOwnerOfPackage(obbInfo.packageName)) {
throw new IllegalArgumentException("Caller package does not match OBB file");
}
try { try {
ArrayList<String> rsp = mConnector.doCommand(String.format("obb path %s", filename)); ArrayList<String> rsp = mConnector.doCommand(String.format("obb path %s", filename));
String []tok = rsp.get(0).split(" "); String []tok = rsp.get(0).split(" ");
@ -1379,7 +1456,7 @@ class MountService extends IMountService.Stub
} catch (NativeDaemonConnectorException e) { } catch (NativeDaemonConnectorException e) {
int code = e.getCode(); int code = e.getCode();
if (code == VoldResponseCode.OpFailedStorageNotFound) { if (code == VoldResponseCode.OpFailedStorageNotFound) {
throw new IllegalArgumentException(String.format("OBB '%s' not found", filename)); return null;
} else { } else {
throw new IllegalStateException(String.format("Unexpected response code %d", code)); throw new IllegalStateException(String.format("Unexpected response code %d", code));
} }
@ -1387,95 +1464,390 @@ class MountService extends IMountService.Stub
} }
public boolean isObbMounted(String filename) { public boolean isObbMounted(String filename) {
waitForReady(); synchronized (mObbMounts) {
warnOnNotMounted(); return mObbPathToStateMap.containsKey(filename);
// XXX replace with call to IMediaContainerService
ObbInfo obbInfo = ObbScanner.getObbInfo(filename);
if (!isCallerOwnerOfPackage(obbInfo.packageName)) {
throw new IllegalArgumentException("Caller package does not match OBB file");
}
synchronized (mObbMountSet) {
return mObbMountSet.contains(filename);
} }
} }
public int mountObb(String filename, String key) { public void mountObb(String filename, String key, IObbActionListener token) {
waitForReady(); waitForReady();
warnOnNotMounted(); warnOnNotMounted();
synchronized (mObbMountSet) { final ObbState obbState;
if (mObbMountSet.contains(filename)) {
return StorageResultCode.OperationFailedStorageMounted; synchronized (mObbMounts) {
if (isObbMounted(filename)) {
throw new IllegalArgumentException("OBB file is already mounted");
} }
if (mObbMounts.containsKey(token)) {
throw new IllegalArgumentException("You may only have one OBB mounted at a time");
}
final int callerUid = Binder.getCallingUid();
obbState = new ObbState(filename, token, callerUid);
addObbState(obbState);
} }
final int callerUid = Binder.getCallingUid();
// XXX replace with call to IMediaContainerService
ObbInfo obbInfo = ObbScanner.getObbInfo(filename);
if (!isUidOwnerOfPackage(obbInfo.packageName, callerUid)) {
throw new IllegalArgumentException("Caller package does not match OBB file");
}
if (key == null) {
key = "none";
}
int rc = StorageResultCode.OperationSucceeded;
String cmd = String.format("obb mount %s %s %d", filename, key, callerUid);
try { try {
mConnector.doCommand(cmd); token.asBinder().linkToDeath(obbState, 0);
} catch (NativeDaemonConnectorException e) { } catch (RemoteException rex) {
int code = e.getCode(); Slog.e(TAG, "Failed to link to listener death");
if (code != VoldResponseCode.OpFailedStorageBusy) {
rc = StorageResultCode.OperationFailedInternalError;
}
} }
if (rc == StorageResultCode.OperationSucceeded) { MountObbAction action = new MountObbAction(obbState, key);
synchronized (mObbMountSet) { mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
mObbMountSet.add(filename);
} if (DEBUG_OBB)
} Slog.i(TAG, "Send to OBB handler: " + action.toString());
return rc;
} }
public int unmountObb(String filename, boolean force) { public void unmountObb(String filename, boolean force, IObbActionListener token) {
waitForReady(); final ObbState obbState;
warnOnNotMounted();
ObbInfo obbInfo = ObbScanner.getObbInfo(filename); synchronized (mObbMounts) {
if (!isCallerOwnerOfPackage(obbInfo.packageName)) { if (!isObbMounted(filename)) {
throw new IllegalArgumentException("Caller package does not match OBB file"); throw new IllegalArgumentException("OBB is not mounted");
}
obbState = mObbPathToStateMap.get(filename);
} }
synchronized (mObbMountSet) { UnmountObbAction action = new UnmountObbAction(obbState, force);
if (!mObbMountSet.contains(filename)) { mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
return StorageResultCode.OperationFailedStorageNotMounted;
}
}
int rc = StorageResultCode.OperationSucceeded; if (DEBUG_OBB)
String cmd = String.format("obb unmount %s%s", filename, (force ? " force" : "")); Slog.i(TAG, "Send to OBB handler: " + action.toString());
try { }
mConnector.doCommand(cmd);
} catch (NativeDaemonConnectorException e) { private void addObbState(ObbState obbState) {
int code = e.getCode(); synchronized (mObbMounts) {
if (code == VoldResponseCode.OpFailedStorageBusy) { mObbMounts.put(obbState.token, obbState);
rc = StorageResultCode.OperationFailedStorageBusy; mObbPathToStateMap.put(obbState.filename, obbState);
}
}
private void removeObbState(ObbState obbState) {
synchronized (mObbMounts) {
mObbMounts.remove(obbState.token);
mObbPathToStateMap.remove(obbState.filename);
}
}
private class ObbActionHandler extends Handler {
private boolean mBound = false;
private List<ObbAction> mActions = new LinkedList<ObbAction>();
ObbActionHandler(Looper l) {
super(l);
}
@Override
public void handleMessage(Message msg) {
switch (msg.what) {
case OBB_RUN_ACTION: {
ObbAction action = (ObbAction) msg.obj;
if (DEBUG_OBB)
Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString());
// If a bind was already initiated we don't really
// need to do anything. The pending install
// will be processed later on.
if (!mBound) {
// If this is the only one pending we might
// have to bind to the service again.
if (!connectToService()) {
Slog.e(TAG, "Failed to bind to media container service");
action.handleError();
return;
} else {
// Once we bind to the service, the first
// pending request will be processed.
mActions.add(action);
}
} else {
// Already bound to the service. Just make
// sure we trigger off processing the first request.
if (mActions.size() == 0) {
mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
}
mActions.add(action);
}
break;
}
case OBB_MCS_BOUND: {
if (DEBUG_OBB)
Slog.i(TAG, "OBB_MCS_BOUND");
if (msg.obj != null) {
mContainerService = (IMediaContainerService) msg.obj;
}
if (mContainerService == null) {
// Something seriously wrong. Bail out
Slog.e(TAG, "Cannot bind to media container service");
for (ObbAction action : mActions) {
// Indicate service bind error
action.handleError();
}
mActions.clear();
} else if (mActions.size() > 0) {
ObbAction action = mActions.get(0);
if (action != null) {
action.execute(this);
}
} else {
// Should never happen ideally.
Slog.w(TAG, "Empty queue");
}
break;
}
case OBB_MCS_RECONNECT: {
if (DEBUG_OBB)
Slog.i(TAG, "OBB_MCS_RECONNECT");
if (mActions.size() > 0) {
if (mBound) {
disconnectService();
}
if (!connectToService()) {
Slog.e(TAG, "Failed to bind to media container service");
for (ObbAction action : mActions) {
// Indicate service bind error
action.handleError();
}
mActions.clear();
}
}
break;
}
case OBB_MCS_UNBIND: {
if (DEBUG_OBB)
Slog.i(TAG, "OBB_MCS_UNBIND");
// Delete pending install
if (mActions.size() > 0) {
mActions.remove(0);
}
if (mActions.size() == 0) {
if (mBound) {
disconnectService();
}
} else {
// There are more pending requests in queue.
// Just post MCS_BOUND message to trigger processing
// of next pending install.
mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
}
break;
}
case OBB_MCS_GIVE_UP: {
if (DEBUG_OBB)
Slog.i(TAG, "OBB_MCS_GIVE_UP");
mActions.remove(0);
break;
}
}
}
private boolean connectToService() {
if (DEBUG_OBB)
Slog.i(TAG, "Trying to bind to DefaultContainerService");
Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
if (mContext.bindService(service, mDefContainerConn, Context.BIND_AUTO_CREATE)) {
mBound = true;
return true;
}
return false;
}
private void disconnectService() {
mContainerService = null;
mBound = false;
mContext.unbindService(mDefContainerConn);
}
}
abstract class ObbAction {
private static final int MAX_RETRIES = 3;
private int mRetries;
ObbState mObbState;
ObbAction(ObbState obbState) {
mObbState = obbState;
}
public void execute(ObbActionHandler handler) {
try {
if (DEBUG_OBB)
Slog.i(TAG, "Starting to execute action: " + this.toString());
mRetries++;
if (mRetries > MAX_RETRIES) {
Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
mObbActionHandler.sendEmptyMessage(OBB_MCS_GIVE_UP);
handleError();
return;
} else {
handleExecute();
if (DEBUG_OBB)
Slog.i(TAG, "Posting install MCS_UNBIND");
mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
}
} catch (RemoteException e) {
if (DEBUG_OBB)
Slog.i(TAG, "Posting install MCS_RECONNECT");
mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT);
} catch (Exception e) {
if (DEBUG_OBB)
Slog.d(TAG, "Error handling OBB action", e);
handleError();
}
}
abstract void handleExecute() throws RemoteException;
abstract void handleError();
}
class MountObbAction extends ObbAction {
private String mKey;
MountObbAction(ObbState obbState, String key) {
super(obbState);
mKey = key;
}
public void handleExecute() throws RemoteException {
ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename);
if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mObbState.callerUid)) {
throw new IllegalArgumentException("Caller package does not match OBB file");
}
if (mKey == null) {
mKey = "none";
}
int rc = StorageResultCode.OperationSucceeded;
String cmd = String.format("obb mount %s %s %d", mObbState.filename, mKey,
mObbState.callerUid);
try {
mConnector.doCommand(cmd);
} catch (NativeDaemonConnectorException e) {
int code = e.getCode();
if (code != VoldResponseCode.OpFailedStorageBusy) {
rc = StorageResultCode.OperationFailedInternalError;
}
}
if (rc == StorageResultCode.OperationSucceeded) {
try {
mObbState.token.onObbResult(mObbState.filename, "mounted");
} catch (RemoteException e) {
Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
}
} else { } else {
rc = StorageResultCode.OperationFailedInternalError; Slog.e(TAG, "Couldn't mount OBB file");
// We didn't succeed, so remove this from the mount-set.
removeObbState(mObbState);
} }
} }
if (rc == StorageResultCode.OperationSucceeded) { public void handleError() {
synchronized (mObbMountSet) { removeObbState(mObbState);
mObbMountSet.remove(filename);
try {
mObbState.token.onObbResult(mObbState.filename, "error");
} catch (RemoteException e) {
Slog.e(TAG, "Couldn't send back OBB mount error for " + mObbState.filename);
} }
} }
return rc;
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("MountObbAction{");
sb.append("filename=");
sb.append(mObbState.filename);
sb.append(",callerUid=");
sb.append(mObbState.callerUid);
sb.append(",token=");
sb.append(mObbState.token != null ? mObbState.token.toString() : "NULL");
sb.append('}');
return sb.toString();
}
}
class UnmountObbAction extends ObbAction {
private boolean mForceUnmount;
UnmountObbAction(ObbState obbState, boolean force) {
super(obbState);
mForceUnmount = force;
}
public void handleExecute() throws RemoteException {
ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename);
if (!isCallerOwnerOfPackageOrSystem(obbInfo.packageName)) {
throw new IllegalArgumentException("Caller package does not match OBB file");
}
int rc = StorageResultCode.OperationSucceeded;
String cmd = String.format("obb unmount %s%s", mObbState.filename,
(mForceUnmount ? " force" : ""));
try {
mConnector.doCommand(cmd);
} catch (NativeDaemonConnectorException e) {
int code = e.getCode();
if (code == VoldResponseCode.OpFailedStorageBusy) {
rc = StorageResultCode.OperationFailedStorageBusy;
} else {
rc = StorageResultCode.OperationFailedInternalError;
}
}
if (rc == StorageResultCode.OperationSucceeded) {
removeObbState(mObbState);
try {
mObbState.token.onObbResult(mObbState.filename, "unmounted");
} catch (RemoteException e) {
Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
}
} else {
try {
mObbState.token.onObbResult(mObbState.filename, "error");
} catch (RemoteException e) {
Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
}
}
}
public void handleError() {
removeObbState(mObbState);
try {
mObbState.token.onObbResult(mObbState.filename, "error");
} catch (RemoteException e) {
Slog.e(TAG, "Couldn't send back OBB unmount error for " + mObbState.filename);
}
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("UnmountObbAction{");
sb.append("filename=");
sb.append(mObbState.filename != null ? mObbState.filename : "null");
sb.append(",force=");
sb.append(mForceUnmount);
sb.append(",callerUid=");
sb.append(mObbState.callerUid);
sb.append(",token=");
sb.append(mObbState.token != null ? mObbState.token.toString() : "null");
sb.append('}');
return sb.toString();
}
} }
} }