Merge "Move OBB file reading to DefaultContainerService" into gingerbread
This commit is contained in:
@ -121,6 +121,7 @@ LOCAL_SRC_FILES += \
|
||||
core/java/android/os/storage/IMountService.aidl \
|
||||
core/java/android/os/storage/IMountServiceListener.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/INetStatService.aidl \
|
||||
core/java/android/os/IPermissionController.aidl \
|
||||
|
@ -129531,6 +129531,8 @@
|
||||
>
|
||||
<parameter name="filename" type="java.lang.String">
|
||||
</parameter>
|
||||
<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
|
||||
</exception>
|
||||
</method>
|
||||
<method name="mountObb"
|
||||
return="boolean"
|
||||
@ -129561,6 +129563,8 @@
|
||||
</parameter>
|
||||
<parameter name="force" type="boolean">
|
||||
</parameter>
|
||||
<exception name="IllegalArgumentException" type="java.lang.IllegalArgumentException">
|
||||
</exception>
|
||||
</method>
|
||||
</class>
|
||||
</package>
|
||||
|
@ -19,6 +19,7 @@ package android.os.storage;
|
||||
|
||||
import android.os.storage.IMountServiceListener;
|
||||
import android.os.storage.IMountShutdownObserver;
|
||||
import android.os.storage.IObbActionListener;
|
||||
|
||||
/** WARNING! Update IMountService.h and IMountService.cpp if you change this file.
|
||||
* 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
|
||||
* 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
|
||||
* 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.
|
||||
|
34
core/java/android/os/storage/IObbActionListener.aidl
Normal file
34
core/java/android/os/storage/IObbActionListener.aidl
Normal 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);
|
||||
}
|
@ -16,28 +16,14 @@
|
||||
|
||||
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.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.storage.IMountService;
|
||||
import android.os.storage.IMountServiceListener;
|
||||
import android.util.Log;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.IOException;
|
||||
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.
|
||||
@ -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
|
||||
* 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) {
|
||||
try {
|
||||
return mMountService.mountObb(filename, key)
|
||||
== StorageResultCode.OperationSucceeded;
|
||||
mMountService.mountObb(filename, key, mObbActionListener);
|
||||
return true;
|
||||
} catch (RemoteException 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 {
|
||||
return mMountService.unmountObb(filename, force)
|
||||
== StorageResultCode.OperationSucceeded;
|
||||
mMountService.unmountObb(filename, force, mObbActionListener);
|
||||
return true;
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG, "Failed to mount OBB", e);
|
||||
}
|
||||
@ -326,7 +346,13 @@ public class StorageManager
|
||||
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 {
|
||||
return mMountService.isObbMounted(filename);
|
||||
} 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) {
|
||||
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;
|
||||
|
@ -19,6 +19,7 @@ package com.android.internal.app;
|
||||
import android.net.Uri;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
import android.content.pm.PackageInfoLite;
|
||||
import android.content.res.ObbInfo;
|
||||
|
||||
interface IMediaContainerService {
|
||||
String copyResourceToContainer(in Uri packageURI,
|
||||
@ -28,4 +29,5 @@ interface IMediaContainerService {
|
||||
in ParcelFileDescriptor outStream);
|
||||
PackageInfoLite getMinimalPackageInfo(in Uri fileUri, int flags);
|
||||
boolean checkFreeStorage(boolean external, in Uri fileUri);
|
||||
ObbInfo getObbInfo(String filename);
|
||||
}
|
||||
|
@ -24,6 +24,8 @@ import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageInfoLite;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageParser;
|
||||
import android.content.res.ObbInfo;
|
||||
import android.content.res.ObbScanner;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.os.IBinder;
|
||||
@ -142,6 +144,10 @@ public class DefaultContainerService extends IntentService {
|
||||
public boolean checkFreeStorage(boolean external, Uri fileUri) {
|
||||
return checkFreeStorageInner(external, fileUri);
|
||||
}
|
||||
|
||||
public ObbInfo getObbInfo(String filename) {
|
||||
return ObbScanner.getObbInfo(filename);
|
||||
}
|
||||
};
|
||||
|
||||
public DefaultContainerService() {
|
||||
|
@ -16,34 +16,42 @@
|
||||
|
||||
package com.android.server;
|
||||
|
||||
import com.android.internal.app.IMediaContainerService;
|
||||
import com.android.server.am.ActivityManagerService;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.ServiceConnection;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.ObbInfo;
|
||||
import android.content.res.ObbScanner;
|
||||
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.Environment;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.IBinder;
|
||||
import android.os.Looper;
|
||||
import android.os.Message;
|
||||
import android.os.RemoteException;
|
||||
import android.os.IBinder;
|
||||
import android.os.Environment;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.SystemClock;
|
||||
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 java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 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>();
|
||||
|
||||
/**
|
||||
* 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
|
||||
private static final int H_UNMOUNT_PM_UPDATE = 1;
|
||||
@ -359,7 +435,7 @@ class MountService extends IMountService.Stub
|
||||
|
||||
public void binderDied() {
|
||||
if (LOCAL_LOGD) Slog.d(TAG, "An IMountServiceListener has died!");
|
||||
synchronized(mListeners) {
|
||||
synchronized (mListeners) {
|
||||
mListeners.remove(this);
|
||||
mListener.asBinder().unlinkToDeath(this, 0);
|
||||
}
|
||||
@ -904,6 +980,9 @@ class MountService extends IMountService.Stub
|
||||
mHandlerThread.start();
|
||||
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
|
||||
* ran and did its thing.
|
||||
@ -1338,12 +1417,16 @@ class MountService extends IMountService.Stub
|
||||
mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
|
||||
}
|
||||
|
||||
private boolean isCallerOwnerOfPackage(String packageName) {
|
||||
private boolean isCallerOwnerOfPackageOrSystem(String packageName) {
|
||||
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) {
|
||||
return false;
|
||||
}
|
||||
@ -1362,12 +1445,6 @@ class MountService extends IMountService.Stub
|
||||
waitForReady();
|
||||
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 {
|
||||
ArrayList<String> rsp = mConnector.doCommand(String.format("obb path %s", filename));
|
||||
String []tok = rsp.get(0).split(" ");
|
||||
@ -1379,7 +1456,7 @@ class MountService extends IMountService.Stub
|
||||
} catch (NativeDaemonConnectorException e) {
|
||||
int code = e.getCode();
|
||||
if (code == VoldResponseCode.OpFailedStorageNotFound) {
|
||||
throw new IllegalArgumentException(String.format("OBB '%s' not found", filename));
|
||||
return null;
|
||||
} else {
|
||||
throw new IllegalStateException(String.format("Unexpected response code %d", code));
|
||||
}
|
||||
@ -1387,95 +1464,390 @@ class MountService extends IMountService.Stub
|
||||
}
|
||||
|
||||
public boolean isObbMounted(String filename) {
|
||||
waitForReady();
|
||||
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");
|
||||
}
|
||||
|
||||
synchronized (mObbMountSet) {
|
||||
return mObbMountSet.contains(filename);
|
||||
synchronized (mObbMounts) {
|
||||
return mObbPathToStateMap.containsKey(filename);
|
||||
}
|
||||
}
|
||||
|
||||
public int mountObb(String filename, String key) {
|
||||
public void mountObb(String filename, String key, IObbActionListener token) {
|
||||
waitForReady();
|
||||
warnOnNotMounted();
|
||||
|
||||
synchronized (mObbMountSet) {
|
||||
if (mObbMountSet.contains(filename)) {
|
||||
return StorageResultCode.OperationFailedStorageMounted;
|
||||
final ObbState obbState;
|
||||
|
||||
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 {
|
||||
mConnector.doCommand(cmd);
|
||||
} catch (NativeDaemonConnectorException e) {
|
||||
int code = e.getCode();
|
||||
if (code != VoldResponseCode.OpFailedStorageBusy) {
|
||||
rc = StorageResultCode.OperationFailedInternalError;
|
||||
}
|
||||
token.asBinder().linkToDeath(obbState, 0);
|
||||
} catch (RemoteException rex) {
|
||||
Slog.e(TAG, "Failed to link to listener death");
|
||||
}
|
||||
|
||||
if (rc == StorageResultCode.OperationSucceeded) {
|
||||
synchronized (mObbMountSet) {
|
||||
mObbMountSet.add(filename);
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
MountObbAction 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 int unmountObb(String filename, boolean force) {
|
||||
waitForReady();
|
||||
warnOnNotMounted();
|
||||
public void unmountObb(String filename, boolean force, IObbActionListener token) {
|
||||
final ObbState obbState;
|
||||
|
||||
ObbInfo obbInfo = ObbScanner.getObbInfo(filename);
|
||||
if (!isCallerOwnerOfPackage(obbInfo.packageName)) {
|
||||
throw new IllegalArgumentException("Caller package does not match OBB file");
|
||||
synchronized (mObbMounts) {
|
||||
if (!isObbMounted(filename)) {
|
||||
throw new IllegalArgumentException("OBB is not mounted");
|
||||
}
|
||||
obbState = mObbPathToStateMap.get(filename);
|
||||
}
|
||||
|
||||
synchronized (mObbMountSet) {
|
||||
if (!mObbMountSet.contains(filename)) {
|
||||
return StorageResultCode.OperationFailedStorageNotMounted;
|
||||
}
|
||||
}
|
||||
UnmountObbAction action = new UnmountObbAction(obbState, force);
|
||||
mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
|
||||
|
||||
int rc = StorageResultCode.OperationSucceeded;
|
||||
String cmd = String.format("obb unmount %s%s", filename, (force ? " force" : ""));
|
||||
try {
|
||||
mConnector.doCommand(cmd);
|
||||
} catch (NativeDaemonConnectorException e) {
|
||||
int code = e.getCode();
|
||||
if (code == VoldResponseCode.OpFailedStorageBusy) {
|
||||
rc = StorageResultCode.OperationFailedStorageBusy;
|
||||
if (DEBUG_OBB)
|
||||
Slog.i(TAG, "Send to OBB handler: " + action.toString());
|
||||
}
|
||||
|
||||
private void addObbState(ObbState obbState) {
|
||||
synchronized (mObbMounts) {
|
||||
mObbMounts.put(obbState.token, obbState);
|
||||
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 {
|
||||
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) {
|
||||
synchronized (mObbMountSet) {
|
||||
mObbMountSet.remove(filename);
|
||||
public void handleError() {
|
||||
removeObbState(mObbState);
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user