summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
authorKenny Root <kroot@google.com>2010-08-05 16:14:17 -0700
committerKenny Root <kroot@google.com>2010-08-11 11:20:53 -0700
commita02b8b05dd1e8b8cf169e1f89542ef835b11fc13 (patch)
treeaa1b5fb6b900c064d9914f51c9c3817a2c46f9df /services
parent9902196f9885d369e37cf44084e3f04513511ab0 (diff)
downloadframeworks_base-a02b8b05dd1e8b8cf169e1f89542ef835b11fc13.zip
frameworks_base-a02b8b05dd1e8b8cf169e1f89542ef835b11fc13.tar.gz
frameworks_base-a02b8b05dd1e8b8cf169e1f89542ef835b11fc13.tar.bz2
Move OBB file reading to DefaultContainerService
The system_server shouldn't touch files on the SD card. This change moves the things that touch the SD card out to the DefaultContainerService so that it will get killed if the SD card goes away instead of the system_server. Change-Id: I0aefa085be4b194768527195532ee6dddc801cfc
Diffstat (limited to 'services')
-rw-r--r--services/java/com/android/server/MountService.java520
1 files changed, 446 insertions, 74 deletions
diff --git a/services/java/com/android/server/MountService.java b/services/java/com/android/server/MountService.java
index d7b92ec..344bfc1 100644
--- a/services/java/com/android/server/MountService.java
+++ b/services/java/com/android/server/MountService.java
@@ -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 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
*/
- final private HashSet<String> mObbMountSet = new HashSet<String>();
+ 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) {
+ synchronized (mObbMounts) {
+ return mObbPathToStateMap.containsKey(filename);
+ }
+ }
+
+ public void mountObb(String filename, String key, IObbActionListener token) {
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");
+ 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);
}
- synchronized (mObbMountSet) {
- return mObbMountSet.contains(filename);
+ try {
+ token.asBinder().linkToDeath(obbState, 0);
+ } catch (RemoteException rex) {
+ Slog.e(TAG, "Failed to link to listener death");
}
+
+ 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 mountObb(String filename, String key) {
- waitForReady();
- warnOnNotMounted();
+ public void unmountObb(String filename, boolean force, IObbActionListener token) {
+ final ObbState obbState;
- synchronized (mObbMountSet) {
- if (mObbMountSet.contains(filename)) {
- return StorageResultCode.OperationFailedStorageMounted;
+ synchronized (mObbMounts) {
+ if (!isObbMounted(filename)) {
+ throw new IllegalArgumentException("OBB is not mounted");
}
+ obbState = mObbPathToStateMap.get(filename);
}
- final int callerUid = Binder.getCallingUid();
+ UnmountObbAction 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());
+ }
- // 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");
+ private void addObbState(ObbState obbState) {
+ synchronized (mObbMounts) {
+ mObbMounts.put(obbState.token, obbState);
+ mObbPathToStateMap.put(obbState.filename, obbState);
}
+ }
- if (key == null) {
- key = "none";
+ private void removeObbState(ObbState obbState) {
+ synchronized (mObbMounts) {
+ mObbMounts.remove(obbState.token);
+ mObbPathToStateMap.remove(obbState.filename);
}
+ }
- 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;
+ 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;
+ }
}
}
- if (rc == StorageResultCode.OperationSucceeded) {
- synchronized (mObbMountSet) {
- mObbMountSet.add(filename);
+ 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);
}
- return rc;
}
- public int unmountObb(String filename, boolean force) {
- waitForReady();
- warnOnNotMounted();
+ abstract class ObbAction {
+ private static final int MAX_RETRIES = 3;
+ private int mRetries;
- ObbInfo obbInfo = ObbScanner.getObbInfo(filename);
- if (!isCallerOwnerOfPackage(obbInfo.packageName)) {
- throw new IllegalArgumentException("Caller package does not match OBB file");
+ ObbState mObbState;
+
+ ObbAction(ObbState obbState) {
+ mObbState = obbState;
}
- synchronized (mObbMountSet) {
- if (!mObbMountSet.contains(filename)) {
- return StorageResultCode.OperationFailedStorageNotMounted;
+ 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();
}
- }
+ }
- 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;
+ 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();
+ }
}
}