blob: ca8fc527d48af85d366dc4105babda4d3f57be9a [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server;
18
Kenny Roota02b8b02010-08-05 16:14:17 -070019import com.android.internal.app.IMediaContainerService;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080020import com.android.server.am.ActivityManagerService;
21
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.content.BroadcastReceiver;
Kenny Roota02b8b02010-08-05 16:14:17 -070023import android.content.ComponentName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.content.Context;
25import android.content.Intent;
26import android.content.IntentFilter;
Kenny Roota02b8b02010-08-05 16:14:17 -070027import android.content.ServiceConnection;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.content.pm.PackageManager;
Kenny Root02c87302010-07-01 08:10:18 -070029import android.content.res.ObbInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.net.Uri;
Kenny Root02c87302010-07-01 08:10:18 -070031import android.os.Binder;
Kenny Roota02b8b02010-08-05 16:14:17 -070032import android.os.Environment;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080033import android.os.Handler;
Daniel Sandler5f27ef42010-03-16 15:42:02 -040034import android.os.HandlerThread;
Kenny Roota02b8b02010-08-05 16:14:17 -070035import android.os.IBinder;
Daniel Sandler5f27ef42010-03-16 15:42:02 -040036import android.os.Looper;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080037import android.os.Message;
San Mehat4270e1e2010-01-29 05:32:19 -080038import android.os.RemoteException;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -080039import android.os.ServiceManager;
San Mehat207e5382010-02-04 20:46:54 -080040import android.os.SystemClock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.os.SystemProperties;
Kenny Roota02b8b02010-08-05 16:14:17 -070042import android.os.storage.IMountService;
43import android.os.storage.IMountServiceListener;
44import android.os.storage.IMountShutdownObserver;
45import android.os.storage.IObbActionListener;
46import android.os.storage.StorageResultCode;
San Mehata5078592010-03-25 09:36:54 -070047import android.util.Slog;
Kenny Roota02b8b02010-08-05 16:14:17 -070048
Kenny Root05105f72010-09-22 17:29:43 -070049import java.io.IOException;
San Mehat22dd86e2010-01-12 12:21:18 -080050import java.util.ArrayList;
Kenny Roota02b8b02010-08-05 16:14:17 -070051import java.util.HashMap;
San Mehat6cdd9c02010-02-09 14:45:20 -080052import java.util.HashSet;
Kenny Roota02b8b02010-08-05 16:14:17 -070053import java.util.LinkedList;
54import java.util.List;
55import java.util.Map;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057/**
San Mehatb1043402010-02-05 08:26:50 -080058 * MountService implements back-end services for platform storage
59 * management.
60 * @hide - Applications should use android.os.storage.StorageManager
61 * to access the MountService.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062 */
San Mehat22dd86e2010-01-12 12:21:18 -080063class MountService extends IMountService.Stub
64 implements INativeDaemonConnectorCallbacks {
San Mehatb1043402010-02-05 08:26:50 -080065 private static final boolean LOCAL_LOGD = false;
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -080066 private static final boolean DEBUG_UNMOUNT = false;
67 private static final boolean DEBUG_EVENTS = false;
Kenny Root02c87302010-07-01 08:10:18 -070068 private static final boolean DEBUG_OBB = true;
69
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 private static final String TAG = "MountService";
71
Kenny Root305bcbf2010-09-03 07:56:38 -070072 private static final String VOLD_TAG = "VoldConnector";
73
San Mehat4270e1e2010-01-29 05:32:19 -080074 /*
75 * Internal vold volume state constants
76 */
San Mehat7fd0fee2009-12-17 07:12:23 -080077 class VolumeState {
78 public static final int Init = -1;
79 public static final int NoMedia = 0;
80 public static final int Idle = 1;
81 public static final int Pending = 2;
82 public static final int Checking = 3;
83 public static final int Mounted = 4;
84 public static final int Unmounting = 5;
85 public static final int Formatting = 6;
86 public static final int Shared = 7;
87 public static final int SharedMnt = 8;
88 }
89
San Mehat4270e1e2010-01-29 05:32:19 -080090 /*
91 * Internal vold response code constants
92 */
San Mehat22dd86e2010-01-12 12:21:18 -080093 class VoldResponseCode {
San Mehat4270e1e2010-01-29 05:32:19 -080094 /*
95 * 100 series - Requestion action was initiated; expect another reply
96 * before proceeding with a new command.
97 */
San Mehat22dd86e2010-01-12 12:21:18 -080098 public static final int VolumeListResult = 110;
99 public static final int AsecListResult = 111;
San Mehatc1b4ce92010-02-16 17:13:03 -0800100 public static final int StorageUsersListResult = 112;
San Mehat22dd86e2010-01-12 12:21:18 -0800101
San Mehat4270e1e2010-01-29 05:32:19 -0800102 /*
103 * 200 series - Requestion action has been successfully completed.
104 */
105 public static final int ShareStatusResult = 210;
San Mehat22dd86e2010-01-12 12:21:18 -0800106 public static final int AsecPathResult = 211;
San Mehat4270e1e2010-01-29 05:32:19 -0800107 public static final int ShareEnabledResult = 212;
San Mehat22dd86e2010-01-12 12:21:18 -0800108
San Mehat4270e1e2010-01-29 05:32:19 -0800109 /*
110 * 400 series - Command was accepted, but the requested action
111 * did not take place.
112 */
113 public static final int OpFailedNoMedia = 401;
114 public static final int OpFailedMediaBlank = 402;
115 public static final int OpFailedMediaCorrupt = 403;
116 public static final int OpFailedVolNotMounted = 404;
San Mehatd9709982010-02-18 11:43:03 -0800117 public static final int OpFailedStorageBusy = 405;
San Mehat2d66cef2010-03-23 11:12:52 -0700118 public static final int OpFailedStorageNotFound = 406;
San Mehat4270e1e2010-01-29 05:32:19 -0800119
120 /*
121 * 600 series - Unsolicited broadcasts.
122 */
San Mehat22dd86e2010-01-12 12:21:18 -0800123 public static final int VolumeStateChange = 605;
San Mehat22dd86e2010-01-12 12:21:18 -0800124 public static final int ShareAvailabilityChange = 620;
125 public static final int VolumeDiskInserted = 630;
126 public static final int VolumeDiskRemoved = 631;
127 public static final int VolumeBadRemoval = 632;
128 }
129
San Mehat4270e1e2010-01-29 05:32:19 -0800130 private Context mContext;
131 private NativeDaemonConnector mConnector;
132 private String mLegacyState = Environment.MEDIA_REMOVED;
133 private PackageManagerService mPms;
134 private boolean mUmsEnabling;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800135 // Used as a lock for methods that register/unregister listeners.
136 final private ArrayList<MountServiceBinderListener> mListeners =
137 new ArrayList<MountServiceBinderListener>();
San Mehat6a965af22010-02-24 17:47:30 -0800138 private boolean mBooted = false;
139 private boolean mReady = false;
140 private boolean mSendUmsConnectedOnBoot = false;
Mike Lockwood03559752010-07-19 18:25:03 -0400141 // true if we should fake MEDIA_MOUNTED state for external storage
142 private boolean mEmulateExternalStorage = false;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -0800143
San Mehat6cdd9c02010-02-09 14:45:20 -0800144 /**
145 * Private hash of currently mounted secure containers.
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800146 * Used as a lock in methods to manipulate secure containers.
San Mehat6cdd9c02010-02-09 14:45:20 -0800147 */
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800148 final private HashSet<String> mAsecMountSet = new HashSet<String>();
San Mehat6cdd9c02010-02-09 14:45:20 -0800149
Kenny Root02c87302010-07-01 08:10:18 -0700150 /**
Kenny Roota02b8b02010-08-05 16:14:17 -0700151 * Mounted OBB tracking information. Used to track the current state of all
152 * OBBs.
Kenny Root02c87302010-07-01 08:10:18 -0700153 */
Kenny Root05105f72010-09-22 17:29:43 -0700154 final private Map<IObbActionListener, List<ObbState>> mObbMounts = new HashMap<IObbActionListener, List<ObbState>>();
Kenny Roota02b8b02010-08-05 16:14:17 -0700155 final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>();
156
157 class ObbState implements IBinder.DeathRecipient {
158 public ObbState(String filename, IObbActionListener token, int callerUid) {
159 this.filename = filename;
160 this.token = token;
161 this.callerUid = callerUid;
162 mounted = false;
163 }
164
165 // OBB source filename
Kenny Root05105f72010-09-22 17:29:43 -0700166 final String filename;
Kenny Roota02b8b02010-08-05 16:14:17 -0700167
168 // Token of remote Binder caller
Kenny Root05105f72010-09-22 17:29:43 -0700169 final IObbActionListener token;
Kenny Roota02b8b02010-08-05 16:14:17 -0700170
171 // Binder.callingUid()
Kenny Root05105f72010-09-22 17:29:43 -0700172 final public int callerUid;
Kenny Roota02b8b02010-08-05 16:14:17 -0700173
174 // Whether this is mounted currently.
175 boolean mounted;
176
177 @Override
178 public void binderDied() {
179 ObbAction action = new UnmountObbAction(this, true);
180 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
181
182 removeObbState(this);
183
184 token.asBinder().unlinkToDeath(this, 0);
185 }
186 }
187
188 // OBB Action Handler
189 final private ObbActionHandler mObbActionHandler;
190
191 // OBB action handler messages
192 private static final int OBB_RUN_ACTION = 1;
193 private static final int OBB_MCS_BOUND = 2;
194 private static final int OBB_MCS_UNBIND = 3;
195 private static final int OBB_MCS_RECONNECT = 4;
196 private static final int OBB_MCS_GIVE_UP = 5;
197
198 /*
199 * Default Container Service information
200 */
201 static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName(
202 "com.android.defcontainer", "com.android.defcontainer.DefaultContainerService");
203
204 final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection();
205
206 class DefaultContainerConnection implements ServiceConnection {
207 public void onServiceConnected(ComponentName name, IBinder service) {
208 if (DEBUG_OBB)
209 Slog.i(TAG, "onServiceConnected");
210 IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service);
211 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs));
212 }
213
214 public void onServiceDisconnected(ComponentName name) {
215 if (DEBUG_OBB)
216 Slog.i(TAG, "onServiceDisconnected");
217 }
218 };
219
220 // Used in the ObbActionHandler
221 private IMediaContainerService mContainerService = null;
Kenny Root02c87302010-07-01 08:10:18 -0700222
223 // Handler messages
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800224 private static final int H_UNMOUNT_PM_UPDATE = 1;
225 private static final int H_UNMOUNT_PM_DONE = 2;
226 private static final int H_UNMOUNT_MS = 3;
227 private static final int RETRY_UNMOUNT_DELAY = 30; // in ms
228 private static final int MAX_UNMOUNT_RETRIES = 4;
229
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800230 class UnmountCallBack {
Kenny Root05105f72010-09-22 17:29:43 -0700231 final String path;
232 final boolean force;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800233 int retries;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800234
235 UnmountCallBack(String path, boolean force) {
236 retries = 0;
237 this.path = path;
238 this.force = force;
239 }
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800240
241 void handleFinished() {
San Mehata5078592010-03-25 09:36:54 -0700242 if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path);
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800243 doUnmountVolume(path, true);
244 }
245 }
246
247 class UmsEnableCallBack extends UnmountCallBack {
Kenny Root05105f72010-09-22 17:29:43 -0700248 final String method;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800249
250 UmsEnableCallBack(String path, String method, boolean force) {
251 super(path, force);
252 this.method = method;
253 }
254
255 @Override
256 void handleFinished() {
257 super.handleFinished();
258 doShareUnshareVolume(path, method, true);
259 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800260 }
261
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800262 class ShutdownCallBack extends UnmountCallBack {
263 IMountShutdownObserver observer;
264 ShutdownCallBack(String path, IMountShutdownObserver observer) {
265 super(path, true);
266 this.observer = observer;
267 }
268
269 @Override
270 void handleFinished() {
271 int ret = doUnmountVolume(path, true);
272 if (observer != null) {
273 try {
274 observer.onShutDownComplete(ret);
275 } catch (RemoteException e) {
San Mehata5078592010-03-25 09:36:54 -0700276 Slog.w(TAG, "RemoteException when shutting down");
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800277 }
278 }
279 }
280 }
281
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400282 class MountServiceHandler extends Handler {
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800283 ArrayList<UnmountCallBack> mForceUnmounts = new ArrayList<UnmountCallBack>();
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700284 boolean mUpdatingStatus = false;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800285
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400286 MountServiceHandler(Looper l) {
287 super(l);
288 }
289
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800290 public void handleMessage(Message msg) {
291 switch (msg.what) {
292 case H_UNMOUNT_PM_UPDATE: {
San Mehata5078592010-03-25 09:36:54 -0700293 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_UPDATE");
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800294 UnmountCallBack ucb = (UnmountCallBack) msg.obj;
295 mForceUnmounts.add(ucb);
San Mehata5078592010-03-25 09:36:54 -0700296 if (DEBUG_UNMOUNT) Slog.i(TAG, " registered = " + mUpdatingStatus);
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800297 // Register only if needed.
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700298 if (!mUpdatingStatus) {
San Mehata5078592010-03-25 09:36:54 -0700299 if (DEBUG_UNMOUNT) Slog.i(TAG, "Updating external media status on PackageManager");
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700300 mUpdatingStatus = true;
301 mPms.updateExternalMediaStatus(false, true);
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800302 }
303 break;
304 }
305 case H_UNMOUNT_PM_DONE: {
San Mehata5078592010-03-25 09:36:54 -0700306 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_DONE");
San Mehata5078592010-03-25 09:36:54 -0700307 if (DEBUG_UNMOUNT) Slog.i(TAG, "Updated status. Processing requests");
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700308 mUpdatingStatus = false;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800309 int size = mForceUnmounts.size();
310 int sizeArr[] = new int[size];
311 int sizeArrN = 0;
Suchi Amalapurapu7af074a2010-04-05 16:46:32 -0700312 // Kill processes holding references first
313 ActivityManagerService ams = (ActivityManagerService)
314 ServiceManager.getService("activity");
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800315 for (int i = 0; i < size; i++) {
316 UnmountCallBack ucb = mForceUnmounts.get(i);
317 String path = ucb.path;
318 boolean done = false;
319 if (!ucb.force) {
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800320 done = true;
321 } else {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800322 int pids[] = getStorageUsers(path);
323 if (pids == null || pids.length == 0) {
324 done = true;
325 } else {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800326 // Eliminate system process here?
Suchi Amalapurapu7af074a2010-04-05 16:46:32 -0700327 ams.killPids(pids, "unmount media");
328 // Confirm if file references have been freed.
329 pids = getStorageUsers(path);
330 if (pids == null || pids.length == 0) {
331 done = true;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800332 }
333 }
334 }
Suchi Amalapurapu7af074a2010-04-05 16:46:32 -0700335 if (!done && (ucb.retries < MAX_UNMOUNT_RETRIES)) {
336 // Retry again
337 Slog.i(TAG, "Retrying to kill storage users again");
338 mHandler.sendMessageDelayed(
339 mHandler.obtainMessage(H_UNMOUNT_PM_DONE,
340 ucb.retries++),
341 RETRY_UNMOUNT_DELAY);
342 } else {
343 if (ucb.retries >= MAX_UNMOUNT_RETRIES) {
344 Slog.i(TAG, "Failed to unmount media inspite of " +
345 MAX_UNMOUNT_RETRIES + " retries. Forcibly killing processes now");
346 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800347 sizeArr[sizeArrN++] = i;
348 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS,
349 ucb));
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800350 }
351 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800352 // Remove already processed elements from list.
353 for (int i = (sizeArrN-1); i >= 0; i--) {
354 mForceUnmounts.remove(sizeArr[i]);
355 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800356 break;
357 }
358 case H_UNMOUNT_MS : {
San Mehata5078592010-03-25 09:36:54 -0700359 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_MS");
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800360 UnmountCallBack ucb = (UnmountCallBack) msg.obj;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800361 ucb.handleFinished();
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800362 break;
363 }
364 }
365 }
366 };
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400367 final private HandlerThread mHandlerThread;
368 final private Handler mHandler;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800369
San Mehat207e5382010-02-04 20:46:54 -0800370 private void waitForReady() {
371 while (mReady == false) {
372 for (int retries = 5; retries > 0; retries--) {
373 if (mReady) {
374 return;
375 }
376 SystemClock.sleep(1000);
377 }
San Mehata5078592010-03-25 09:36:54 -0700378 Slog.w(TAG, "Waiting too long for mReady!");
San Mehat207e5382010-02-04 20:46:54 -0800379 }
San Mehat1f6301e2010-01-07 22:40:27 -0800380 }
Kenny Root02c87302010-07-01 08:10:18 -0700381
San Mehat207e5382010-02-04 20:46:54 -0800382 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800383 public void onReceive(Context context, Intent intent) {
San Mehat91c77612010-01-07 10:39:41 -0800384 String action = intent.getAction();
385
386 if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
San Mehat207e5382010-02-04 20:46:54 -0800387 mBooted = true;
San Mehat22dd86e2010-01-12 12:21:18 -0800388
Marco Nelissenc34ebce2010-02-18 13:39:41 -0800389 /*
390 * In the simulator, we need to broadcast a volume mounted event
391 * to make the media scanner run.
392 */
393 if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
394 notifyVolumeStateChange(null, "/sdcard", VolumeState.NoMedia, VolumeState.Mounted);
395 return;
396 }
San Mehatfafb0412010-02-18 19:40:04 -0800397 new Thread() {
398 public void run() {
399 try {
400 String path = Environment.getExternalStorageDirectory().getPath();
San Mehat6a254402010-03-22 10:21:00 -0700401 String state = getVolumeState(path);
402
Mike Lockwood03559752010-07-19 18:25:03 -0400403 if (mEmulateExternalStorage) {
404 notifyVolumeStateChange(null, path, VolumeState.NoMedia, VolumeState.Mounted);
405 } else if (state.equals(Environment.MEDIA_UNMOUNTED)) {
San Mehatfafb0412010-02-18 19:40:04 -0800406 int rc = doMountVolume(path);
407 if (rc != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -0700408 Slog.e(TAG, String.format("Boot-time mount failed (%d)", rc));
San Mehatfafb0412010-02-18 19:40:04 -0800409 }
San Mehat6a254402010-03-22 10:21:00 -0700410 } else if (state.equals(Environment.MEDIA_SHARED)) {
411 /*
412 * Bootstrap UMS enabled state since vold indicates
413 * the volume is shared (runtime restart while ums enabled)
414 */
415 notifyVolumeStateChange(null, path, VolumeState.NoMedia, VolumeState.Shared);
San Mehatfafb0412010-02-18 19:40:04 -0800416 }
San Mehat6a254402010-03-22 10:21:00 -0700417
San Mehat6a965af22010-02-24 17:47:30 -0800418 /*
San Mehat6a254402010-03-22 10:21:00 -0700419 * If UMS was connected on boot, send the connected event
San Mehat6a965af22010-02-24 17:47:30 -0800420 * now that we're up.
421 */
422 if (mSendUmsConnectedOnBoot) {
423 sendUmsIntent(true);
424 mSendUmsConnectedOnBoot = false;
425 }
San Mehatfafb0412010-02-18 19:40:04 -0800426 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700427 Slog.e(TAG, "Boot-time mount exception", ex);
San Mehatfafb0412010-02-18 19:40:04 -0800428 }
San Mehat207e5382010-02-04 20:46:54 -0800429 }
San Mehatfafb0412010-02-18 19:40:04 -0800430 }.start();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800431 }
432 }
433 };
434
San Mehat4270e1e2010-01-29 05:32:19 -0800435 private final class MountServiceBinderListener implements IBinder.DeathRecipient {
436 final IMountServiceListener mListener;
437
438 MountServiceBinderListener(IMountServiceListener listener) {
439 mListener = listener;
Kenny Root02c87302010-07-01 08:10:18 -0700440
San Mehat91c77612010-01-07 10:39:41 -0800441 }
442
San Mehat4270e1e2010-01-29 05:32:19 -0800443 public void binderDied() {
San Mehata5078592010-03-25 09:36:54 -0700444 if (LOCAL_LOGD) Slog.d(TAG, "An IMountServiceListener has died!");
Kenny Roota02b8b02010-08-05 16:14:17 -0700445 synchronized (mListeners) {
San Mehat4270e1e2010-01-29 05:32:19 -0800446 mListeners.remove(this);
447 mListener.asBinder().unlinkToDeath(this, 0);
448 }
449 }
450 }
451
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800452 private void doShareUnshareVolume(String path, String method, boolean enable) {
San Mehat4270e1e2010-01-29 05:32:19 -0800453 // TODO: Add support for multiple share methods
454 if (!method.equals("ums")) {
455 throw new IllegalArgumentException(String.format("Method %s not supported", method));
456 }
457
San Mehat4270e1e2010-01-29 05:32:19 -0800458 try {
459 mConnector.doCommand(String.format(
460 "volume %sshare %s %s", (enable ? "" : "un"), path, method));
461 } catch (NativeDaemonConnectorException e) {
San Mehata5078592010-03-25 09:36:54 -0700462 Slog.e(TAG, "Failed to share/unshare", e);
San Mehat4270e1e2010-01-29 05:32:19 -0800463 }
San Mehat4270e1e2010-01-29 05:32:19 -0800464 }
465
San Mehat207e5382010-02-04 20:46:54 -0800466 private void updatePublicVolumeState(String path, String state) {
San Mehat4270e1e2010-01-29 05:32:19 -0800467 if (!path.equals(Environment.getExternalStorageDirectory().getPath())) {
San Mehata5078592010-03-25 09:36:54 -0700468 Slog.w(TAG, "Multiple volumes not currently supported");
San Mehat4270e1e2010-01-29 05:32:19 -0800469 return;
470 }
San Mehatb1043402010-02-05 08:26:50 -0800471
472 if (mLegacyState.equals(state)) {
San Mehata5078592010-03-25 09:36:54 -0700473 Slog.w(TAG, String.format("Duplicate state transition (%s -> %s)", mLegacyState, state));
San Mehatb1043402010-02-05 08:26:50 -0800474 return;
475 }
Mike Lockwood03559752010-07-19 18:25:03 -0400476 // Update state on PackageManager, but only of real events
477 if (!mEmulateExternalStorage) {
478 if (Environment.MEDIA_UNMOUNTED.equals(state)) {
479 mPms.updateExternalMediaStatus(false, false);
480 } else if (Environment.MEDIA_MOUNTED.equals(state)) {
481 mPms.updateExternalMediaStatus(true, false);
482 }
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800483 }
San Mehat4270e1e2010-01-29 05:32:19 -0800484 String oldState = mLegacyState;
485 mLegacyState = state;
486
487 synchronized (mListeners) {
488 for (int i = mListeners.size() -1; i >= 0; i--) {
489 MountServiceBinderListener bl = mListeners.get(i);
490 try {
San Mehatb1043402010-02-05 08:26:50 -0800491 bl.mListener.onStorageStateChanged(path, oldState, state);
San Mehat4270e1e2010-01-29 05:32:19 -0800492 } catch (RemoteException rex) {
San Mehata5078592010-03-25 09:36:54 -0700493 Slog.e(TAG, "Listener dead");
San Mehat4270e1e2010-01-29 05:32:19 -0800494 mListeners.remove(i);
495 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700496 Slog.e(TAG, "Listener failed", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800497 }
498 }
499 }
500 }
501
502 /**
503 *
504 * Callback from NativeDaemonConnector
505 */
506 public void onDaemonConnected() {
507 /*
508 * Since we'll be calling back into the NativeDaemonConnector,
509 * we need to do our work in a new thread.
510 */
511 new Thread() {
512 public void run() {
513 /**
514 * Determine media state and UMS detection status
515 */
516 String path = Environment.getExternalStorageDirectory().getPath();
517 String state = Environment.MEDIA_REMOVED;
518
519 try {
520 String[] vols = mConnector.doListCommand(
521 "volume list", VoldResponseCode.VolumeListResult);
522 for (String volstr : vols) {
523 String[] tok = volstr.split(" ");
524 // FMT: <label> <mountpoint> <state>
525 if (!tok[1].equals(path)) {
San Mehata5078592010-03-25 09:36:54 -0700526 Slog.w(TAG, String.format(
San Mehat4270e1e2010-01-29 05:32:19 -0800527 "Skipping unknown volume '%s'",tok[1]));
528 continue;
529 }
530 int st = Integer.parseInt(tok[2]);
531 if (st == VolumeState.NoMedia) {
532 state = Environment.MEDIA_REMOVED;
533 } else if (st == VolumeState.Idle) {
San Mehat207e5382010-02-04 20:46:54 -0800534 state = Environment.MEDIA_UNMOUNTED;
San Mehat4270e1e2010-01-29 05:32:19 -0800535 } else if (st == VolumeState.Mounted) {
536 state = Environment.MEDIA_MOUNTED;
San Mehata5078592010-03-25 09:36:54 -0700537 Slog.i(TAG, "Media already mounted on daemon connection");
San Mehat4270e1e2010-01-29 05:32:19 -0800538 } else if (st == VolumeState.Shared) {
539 state = Environment.MEDIA_SHARED;
San Mehata5078592010-03-25 09:36:54 -0700540 Slog.i(TAG, "Media shared on daemon connection");
San Mehat4270e1e2010-01-29 05:32:19 -0800541 } else {
542 throw new Exception(String.format("Unexpected state %d", st));
543 }
544 }
545 if (state != null) {
San Mehata5078592010-03-25 09:36:54 -0700546 if (DEBUG_EVENTS) Slog.i(TAG, "Updating valid state " + state);
San Mehat4270e1e2010-01-29 05:32:19 -0800547 updatePublicVolumeState(path, state);
548 }
549 } catch (Exception e) {
San Mehata5078592010-03-25 09:36:54 -0700550 Slog.e(TAG, "Error processing initial volume state", e);
San Mehat4270e1e2010-01-29 05:32:19 -0800551 updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
552 }
553
554 try {
San Mehat207e5382010-02-04 20:46:54 -0800555 boolean avail = doGetShareMethodAvailable("ums");
San Mehat4270e1e2010-01-29 05:32:19 -0800556 notifyShareAvailabilityChange("ums", avail);
557 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700558 Slog.w(TAG, "Failed to get share availability");
San Mehat4270e1e2010-01-29 05:32:19 -0800559 }
San Mehat207e5382010-02-04 20:46:54 -0800560 /*
561 * Now that we've done our initialization, release
562 * the hounds!
563 */
564 mReady = true;
San Mehat4270e1e2010-01-29 05:32:19 -0800565 }
566 }.start();
567 }
568
569 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800570 * Callback from NativeDaemonConnector
571 */
572 public boolean onEvent(int code, String raw, String[] cooked) {
573 Intent in = null;
574
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800575 if (DEBUG_EVENTS) {
576 StringBuilder builder = new StringBuilder();
577 builder.append("onEvent::");
578 builder.append(" raw= " + raw);
579 if (cooked != null) {
580 builder.append(" cooked = " );
581 for (String str : cooked) {
582 builder.append(" " + str);
583 }
584 }
San Mehata5078592010-03-25 09:36:54 -0700585 Slog.i(TAG, builder.toString());
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -0800586 }
San Mehat4270e1e2010-01-29 05:32:19 -0800587 if (code == VoldResponseCode.VolumeStateChange) {
588 /*
589 * One of the volumes we're managing has changed state.
590 * Format: "NNN Volume <label> <path> state changed
591 * from <old_#> (<old_str>) to <new_#> (<new_str>)"
592 */
593 notifyVolumeStateChange(
594 cooked[2], cooked[3], Integer.parseInt(cooked[7]),
595 Integer.parseInt(cooked[10]));
596 } else if (code == VoldResponseCode.ShareAvailabilityChange) {
597 // FMT: NNN Share method <method> now <available|unavailable>
598 boolean avail = false;
599 if (cooked[5].equals("available")) {
600 avail = true;
601 }
602 notifyShareAvailabilityChange(cooked[3], avail);
603 } else if ((code == VoldResponseCode.VolumeDiskInserted) ||
604 (code == VoldResponseCode.VolumeDiskRemoved) ||
605 (code == VoldResponseCode.VolumeBadRemoval)) {
606 // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
607 // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
608 // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
609 final String label = cooked[2];
610 final String path = cooked[3];
611 int major = -1;
612 int minor = -1;
613
614 try {
615 String devComp = cooked[6].substring(1, cooked[6].length() -1);
616 String[] devTok = devComp.split(":");
617 major = Integer.parseInt(devTok[0]);
618 minor = Integer.parseInt(devTok[1]);
619 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700620 Slog.e(TAG, "Failed to parse major/minor", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800621 }
622
San Mehat4270e1e2010-01-29 05:32:19 -0800623 if (code == VoldResponseCode.VolumeDiskInserted) {
624 new Thread() {
625 public void run() {
626 try {
627 int rc;
San Mehatb1043402010-02-05 08:26:50 -0800628 if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -0700629 Slog.w(TAG, String.format("Insertion mount failed (%d)", rc));
San Mehat4270e1e2010-01-29 05:32:19 -0800630 }
631 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700632 Slog.w(TAG, "Failed to mount media on insertion", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800633 }
634 }
635 }.start();
636 } else if (code == VoldResponseCode.VolumeDiskRemoved) {
637 /*
638 * This event gets trumped if we're already in BAD_REMOVAL state
639 */
640 if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
641 return true;
642 }
643 /* Send the media unmounted event first */
San Mehata5078592010-03-25 09:36:54 -0700644 if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
San Mehat4270e1e2010-01-29 05:32:19 -0800645 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
646 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
647 mContext.sendBroadcast(in);
648
San Mehata5078592010-03-25 09:36:54 -0700649 if (DEBUG_EVENTS) Slog.i(TAG, "Sending media removed");
San Mehat4270e1e2010-01-29 05:32:19 -0800650 updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
651 in = new Intent(Intent.ACTION_MEDIA_REMOVED, Uri.parse("file://" + path));
652 } else if (code == VoldResponseCode.VolumeBadRemoval) {
San Mehata5078592010-03-25 09:36:54 -0700653 if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first");
San Mehat4270e1e2010-01-29 05:32:19 -0800654 /* Send the media unmounted event first */
655 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
656 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
657 mContext.sendBroadcast(in);
658
San Mehata5078592010-03-25 09:36:54 -0700659 if (DEBUG_EVENTS) Slog.i(TAG, "Sending media bad removal");
San Mehat4270e1e2010-01-29 05:32:19 -0800660 updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL);
661 in = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL, Uri.parse("file://" + path));
662 } else {
San Mehata5078592010-03-25 09:36:54 -0700663 Slog.e(TAG, String.format("Unknown code {%d}", code));
San Mehat4270e1e2010-01-29 05:32:19 -0800664 }
665 } else {
666 return false;
667 }
668
669 if (in != null) {
670 mContext.sendBroadcast(in);
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400671 }
672 return true;
San Mehat4270e1e2010-01-29 05:32:19 -0800673 }
674
San Mehat207e5382010-02-04 20:46:54 -0800675 private void notifyVolumeStateChange(String label, String path, int oldState, int newState) {
San Mehat4270e1e2010-01-29 05:32:19 -0800676 String vs = getVolumeState(path);
San Mehata5078592010-03-25 09:36:54 -0700677 if (DEBUG_EVENTS) Slog.i(TAG, "notifyVolumeStateChanged::" + vs);
San Mehat4270e1e2010-01-29 05:32:19 -0800678
679 Intent in = null;
680
Mike Lockwoodbf2dd442010-03-03 06:16:52 -0500681 if (oldState == VolumeState.Shared && newState != oldState) {
San Mehata5078592010-03-25 09:36:54 -0700682 if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_UNSHARED intent");
Mike Lockwoodbf2dd442010-03-03 06:16:52 -0500683 mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_UNSHARED,
684 Uri.parse("file://" + path)));
685 }
686
San Mehat4270e1e2010-01-29 05:32:19 -0800687 if (newState == VolumeState.Init) {
688 } else if (newState == VolumeState.NoMedia) {
689 // NoMedia is handled via Disk Remove events
690 } else if (newState == VolumeState.Idle) {
691 /*
692 * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
693 * if we're in the process of enabling UMS
694 */
695 if (!vs.equals(
696 Environment.MEDIA_BAD_REMOVAL) && !vs.equals(
697 Environment.MEDIA_NOFS) && !vs.equals(
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800698 Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) {
San Mehata5078592010-03-25 09:36:54 -0700699 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state for media bad removal nofs and unmountable");
San Mehat4270e1e2010-01-29 05:32:19 -0800700 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
701 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
702 }
703 } else if (newState == VolumeState.Pending) {
704 } else if (newState == VolumeState.Checking) {
San Mehata5078592010-03-25 09:36:54 -0700705 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state checking");
San Mehat4270e1e2010-01-29 05:32:19 -0800706 updatePublicVolumeState(path, Environment.MEDIA_CHECKING);
707 in = new Intent(Intent.ACTION_MEDIA_CHECKING, Uri.parse("file://" + path));
708 } else if (newState == VolumeState.Mounted) {
San Mehata5078592010-03-25 09:36:54 -0700709 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state mounted");
San Mehat4270e1e2010-01-29 05:32:19 -0800710 updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
San Mehat4270e1e2010-01-29 05:32:19 -0800711 in = new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + path));
712 in.putExtra("read-only", false);
713 } else if (newState == VolumeState.Unmounting) {
San Mehat4270e1e2010-01-29 05:32:19 -0800714 in = new Intent(Intent.ACTION_MEDIA_EJECT, Uri.parse("file://" + path));
715 } else if (newState == VolumeState.Formatting) {
716 } else if (newState == VolumeState.Shared) {
San Mehata5078592010-03-25 09:36:54 -0700717 if (DEBUG_EVENTS) Slog.i(TAG, "Updating volume state media mounted");
San Mehat4270e1e2010-01-29 05:32:19 -0800718 /* Send the media unmounted event first */
719 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
720 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
721 mContext.sendBroadcast(in);
722
San Mehata5078592010-03-25 09:36:54 -0700723 if (DEBUG_EVENTS) Slog.i(TAG, "Updating media shared");
San Mehat4270e1e2010-01-29 05:32:19 -0800724 updatePublicVolumeState(path, Environment.MEDIA_SHARED);
725 in = new Intent(Intent.ACTION_MEDIA_SHARED, Uri.parse("file://" + path));
San Mehata5078592010-03-25 09:36:54 -0700726 if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_SHARED intent");
San Mehat4270e1e2010-01-29 05:32:19 -0800727 } else if (newState == VolumeState.SharedMnt) {
San Mehata5078592010-03-25 09:36:54 -0700728 Slog.e(TAG, "Live shared mounts not supported yet!");
San Mehat4270e1e2010-01-29 05:32:19 -0800729 return;
730 } else {
San Mehata5078592010-03-25 09:36:54 -0700731 Slog.e(TAG, "Unhandled VolumeState {" + newState + "}");
San Mehat4270e1e2010-01-29 05:32:19 -0800732 }
733
734 if (in != null) {
735 mContext.sendBroadcast(in);
736 }
737 }
738
San Mehat207e5382010-02-04 20:46:54 -0800739 private boolean doGetShareMethodAvailable(String method) {
Kenny Root85fb2062010-06-01 20:50:21 -0700740 ArrayList<String> rsp;
Kenny Roota80ce062010-06-01 13:23:53 -0700741 try {
Kenny Root85fb2062010-06-01 20:50:21 -0700742 rsp = mConnector.doCommand("share status " + method);
Kenny Roota80ce062010-06-01 13:23:53 -0700743 } catch (NativeDaemonConnectorException ex) {
744 Slog.e(TAG, "Failed to determine whether share method " + method + " is available.");
745 return false;
746 }
San Mehat207e5382010-02-04 20:46:54 -0800747
748 for (String line : rsp) {
Kenny Roota80ce062010-06-01 13:23:53 -0700749 String[] tok = line.split(" ");
750 if (tok.length < 3) {
751 Slog.e(TAG, "Malformed response to share status " + method);
752 return false;
753 }
754
San Mehat207e5382010-02-04 20:46:54 -0800755 int code;
756 try {
757 code = Integer.parseInt(tok[0]);
758 } catch (NumberFormatException nfe) {
San Mehata5078592010-03-25 09:36:54 -0700759 Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
San Mehat207e5382010-02-04 20:46:54 -0800760 return false;
761 }
762 if (code == VoldResponseCode.ShareStatusResult) {
763 if (tok[2].equals("available"))
764 return true;
765 return false;
766 } else {
San Mehata5078592010-03-25 09:36:54 -0700767 Slog.e(TAG, String.format("Unexpected response code %d", code));
San Mehat207e5382010-02-04 20:46:54 -0800768 return false;
769 }
770 }
San Mehata5078592010-03-25 09:36:54 -0700771 Slog.e(TAG, "Got an empty response");
San Mehat207e5382010-02-04 20:46:54 -0800772 return false;
773 }
774
775 private int doMountVolume(String path) {
San Mehatb1043402010-02-05 08:26:50 -0800776 int rc = StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -0800777
San Mehata5078592010-03-25 09:36:54 -0700778 if (DEBUG_EVENTS) Slog.i(TAG, "doMountVolume: Mouting " + path);
San Mehat207e5382010-02-04 20:46:54 -0800779 try {
780 mConnector.doCommand(String.format("volume mount %s", path));
781 } catch (NativeDaemonConnectorException e) {
782 /*
783 * Mount failed for some reason
784 */
785 Intent in = null;
786 int code = e.getCode();
787 if (code == VoldResponseCode.OpFailedNoMedia) {
788 /*
789 * Attempt to mount but no media inserted
790 */
San Mehatb1043402010-02-05 08:26:50 -0800791 rc = StorageResultCode.OperationFailedNoMedia;
San Mehat207e5382010-02-04 20:46:54 -0800792 } else if (code == VoldResponseCode.OpFailedMediaBlank) {
San Mehata5078592010-03-25 09:36:54 -0700793 if (DEBUG_EVENTS) Slog.i(TAG, " updating volume state :: media nofs");
San Mehat207e5382010-02-04 20:46:54 -0800794 /*
795 * Media is blank or does not contain a supported filesystem
796 */
797 updatePublicVolumeState(path, Environment.MEDIA_NOFS);
798 in = new Intent(Intent.ACTION_MEDIA_NOFS, Uri.parse("file://" + path));
San Mehatb1043402010-02-05 08:26:50 -0800799 rc = StorageResultCode.OperationFailedMediaBlank;
San Mehat207e5382010-02-04 20:46:54 -0800800 } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
San Mehata5078592010-03-25 09:36:54 -0700801 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state media corrupt");
San Mehat207e5382010-02-04 20:46:54 -0800802 /*
803 * Volume consistency check failed
804 */
805 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE);
806 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, Uri.parse("file://" + path));
San Mehatb1043402010-02-05 08:26:50 -0800807 rc = StorageResultCode.OperationFailedMediaCorrupt;
San Mehat207e5382010-02-04 20:46:54 -0800808 } else {
San Mehatb1043402010-02-05 08:26:50 -0800809 rc = StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -0800810 }
811
812 /*
813 * Send broadcast intent (if required for the failure)
814 */
815 if (in != null) {
816 mContext.sendBroadcast(in);
817 }
818 }
819
820 return rc;
821 }
822
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800823 /*
824 * If force is not set, we do not unmount if there are
825 * processes holding references to the volume about to be unmounted.
826 * If force is set, all the processes holding references need to be
827 * killed via the ActivityManager before actually unmounting the volume.
828 * This might even take a while and might be retried after timed delays
829 * to make sure we dont end up in an instable state and kill some core
830 * processes.
831 */
San Mehatd9709982010-02-18 11:43:03 -0800832 private int doUnmountVolume(String path, boolean force) {
San Mehat59443a62010-02-09 13:28:45 -0800833 if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) {
San Mehat207e5382010-02-04 20:46:54 -0800834 return VoldResponseCode.OpFailedVolNotMounted;
835 }
Kenny Rootaa485402010-09-14 14:49:41 -0700836
837 /*
838 * Force a GC to make sure AssetManagers in other threads of the
839 * system_server are cleaned up. We have to do this since AssetManager
840 * instances are kept as a WeakReference and it's possible we have files
841 * open on the external storage.
842 */
843 Runtime.getRuntime().gc();
844
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800845 // Redundant probably. But no harm in updating state again.
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700846 mPms.updateExternalMediaStatus(false, false);
San Mehat207e5382010-02-04 20:46:54 -0800847 try {
San Mehatd9709982010-02-18 11:43:03 -0800848 mConnector.doCommand(String.format(
849 "volume unmount %s%s", path, (force ? " force" : "")));
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -0700850 // We unmounted the volume. None of the asec containers are available now.
851 synchronized (mAsecMountSet) {
852 mAsecMountSet.clear();
853 }
San Mehatb1043402010-02-05 08:26:50 -0800854 return StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -0800855 } catch (NativeDaemonConnectorException e) {
856 // Don't worry about mismatch in PackageManager since the
857 // call back will handle the status changes any way.
858 int code = e.getCode();
859 if (code == VoldResponseCode.OpFailedVolNotMounted) {
San Mehata181b212010-02-11 06:50:20 -0800860 return StorageResultCode.OperationFailedStorageNotMounted;
San Mehatd9709982010-02-18 11:43:03 -0800861 } else if (code == VoldResponseCode.OpFailedStorageBusy) {
862 return StorageResultCode.OperationFailedStorageBusy;
San Mehat207e5382010-02-04 20:46:54 -0800863 } else {
San Mehatb1043402010-02-05 08:26:50 -0800864 return StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -0800865 }
866 }
867 }
868
869 private int doFormatVolume(String path) {
870 try {
871 String cmd = String.format("volume format %s", path);
872 mConnector.doCommand(cmd);
San Mehatb1043402010-02-05 08:26:50 -0800873 return StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -0800874 } catch (NativeDaemonConnectorException e) {
875 int code = e.getCode();
876 if (code == VoldResponseCode.OpFailedNoMedia) {
San Mehatb1043402010-02-05 08:26:50 -0800877 return StorageResultCode.OperationFailedNoMedia;
San Mehat207e5382010-02-04 20:46:54 -0800878 } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
San Mehatb1043402010-02-05 08:26:50 -0800879 return StorageResultCode.OperationFailedMediaCorrupt;
San Mehat207e5382010-02-04 20:46:54 -0800880 } else {
San Mehatb1043402010-02-05 08:26:50 -0800881 return StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -0800882 }
883 }
884 }
885
San Mehatb1043402010-02-05 08:26:50 -0800886 private boolean doGetVolumeShared(String path, String method) {
887 String cmd = String.format("volume shared %s %s", path, method);
Kenny Roota80ce062010-06-01 13:23:53 -0700888 ArrayList<String> rsp;
889
890 try {
891 rsp = mConnector.doCommand(cmd);
892 } catch (NativeDaemonConnectorException ex) {
893 Slog.e(TAG, "Failed to read response to volume shared " + path + " " + method);
894 return false;
895 }
San Mehatb1043402010-02-05 08:26:50 -0800896
897 for (String line : rsp) {
Kenny Roota80ce062010-06-01 13:23:53 -0700898 String[] tok = line.split(" ");
899 if (tok.length < 3) {
900 Slog.e(TAG, "Malformed response to volume shared " + path + " " + method + " command");
901 return false;
902 }
903
San Mehatb1043402010-02-05 08:26:50 -0800904 int code;
905 try {
906 code = Integer.parseInt(tok[0]);
907 } catch (NumberFormatException nfe) {
San Mehata5078592010-03-25 09:36:54 -0700908 Slog.e(TAG, String.format("Error parsing code %s", tok[0]));
San Mehatb1043402010-02-05 08:26:50 -0800909 return false;
910 }
911 if (code == VoldResponseCode.ShareEnabledResult) {
Kenny Roota80ce062010-06-01 13:23:53 -0700912 return "enabled".equals(tok[2]);
San Mehatb1043402010-02-05 08:26:50 -0800913 } else {
San Mehata5078592010-03-25 09:36:54 -0700914 Slog.e(TAG, String.format("Unexpected response code %d", code));
San Mehatb1043402010-02-05 08:26:50 -0800915 return false;
916 }
917 }
San Mehata5078592010-03-25 09:36:54 -0700918 Slog.e(TAG, "Got an empty response");
San Mehatb1043402010-02-05 08:26:50 -0800919 return false;
920 }
921
San Mehat207e5382010-02-04 20:46:54 -0800922 private void notifyShareAvailabilityChange(String method, final boolean avail) {
San Mehat4270e1e2010-01-29 05:32:19 -0800923 if (!method.equals("ums")) {
San Mehata5078592010-03-25 09:36:54 -0700924 Slog.w(TAG, "Ignoring unsupported share method {" + method + "}");
San Mehat4270e1e2010-01-29 05:32:19 -0800925 return;
926 }
927
928 synchronized (mListeners) {
929 for (int i = mListeners.size() -1; i >= 0; i--) {
930 MountServiceBinderListener bl = mListeners.get(i);
931 try {
San Mehatb1043402010-02-05 08:26:50 -0800932 bl.mListener.onUsbMassStorageConnectionChanged(avail);
San Mehat4270e1e2010-01-29 05:32:19 -0800933 } catch (RemoteException rex) {
San Mehata5078592010-03-25 09:36:54 -0700934 Slog.e(TAG, "Listener dead");
San Mehat4270e1e2010-01-29 05:32:19 -0800935 mListeners.remove(i);
936 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700937 Slog.e(TAG, "Listener failed", ex);
San Mehat4270e1e2010-01-29 05:32:19 -0800938 }
939 }
940 }
941
San Mehat207e5382010-02-04 20:46:54 -0800942 if (mBooted == true) {
San Mehat6a965af22010-02-24 17:47:30 -0800943 sendUmsIntent(avail);
944 } else {
945 mSendUmsConnectedOnBoot = avail;
San Mehat4270e1e2010-01-29 05:32:19 -0800946 }
San Mehat2fe718a2010-03-11 12:01:49 -0800947
948 final String path = Environment.getExternalStorageDirectory().getPath();
949 if (avail == false && getVolumeState(path).equals(Environment.MEDIA_SHARED)) {
950 /*
951 * USB mass storage disconnected while enabled
952 */
953 new Thread() {
954 public void run() {
955 try {
956 int rc;
San Mehata5078592010-03-25 09:36:54 -0700957 Slog.w(TAG, "Disabling UMS after cable disconnect");
San Mehat2fe718a2010-03-11 12:01:49 -0800958 doShareUnshareVolume(path, "ums", false);
959 if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -0700960 Slog.e(TAG, String.format(
San Mehat2fe718a2010-03-11 12:01:49 -0800961 "Failed to remount {%s} on UMS enabled-disconnect (%d)",
962 path, rc));
963 }
964 } catch (Exception ex) {
San Mehata5078592010-03-25 09:36:54 -0700965 Slog.w(TAG, "Failed to mount media on UMS enabled-disconnect", ex);
San Mehat2fe718a2010-03-11 12:01:49 -0800966 }
967 }
968 }.start();
969 }
San Mehat4270e1e2010-01-29 05:32:19 -0800970 }
971
San Mehat6a965af22010-02-24 17:47:30 -0800972 private void sendUmsIntent(boolean c) {
973 mContext.sendBroadcast(
974 new Intent((c ? Intent.ACTION_UMS_CONNECTED : Intent.ACTION_UMS_DISCONNECTED)));
975 }
976
San Mehat207e5382010-02-04 20:46:54 -0800977 private void validatePermission(String perm) {
San Mehat4270e1e2010-01-29 05:32:19 -0800978 if (mContext.checkCallingOrSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) {
979 throw new SecurityException(String.format("Requires %s permission", perm));
980 }
981 }
982
983 /**
San Mehat207e5382010-02-04 20:46:54 -0800984 * Constructs a new MountService instance
985 *
986 * @param context Binder context for this service
987 */
988 public MountService(Context context) {
989 mContext = context;
990
Mike Lockwood03559752010-07-19 18:25:03 -0400991 mEmulateExternalStorage = context.getResources().getBoolean(
992 com.android.internal.R.bool.config_emulateExternalStorage);
993 if (mEmulateExternalStorage) {
994 Slog.d(TAG, "using emulated external storage");
995 mLegacyState = Environment.MEDIA_MOUNTED;
996 }
997
San Mehat207e5382010-02-04 20:46:54 -0800998 // XXX: This will go away soon in favor of IMountServiceObserver
999 mPms = (PackageManagerService) ServiceManager.getService("package");
1000
1001 mContext.registerReceiver(mBroadcastReceiver,
1002 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
1003
Daniel Sandler5f27ef42010-03-16 15:42:02 -04001004 mHandlerThread = new HandlerThread("MountService");
1005 mHandlerThread.start();
1006 mHandler = new MountServiceHandler(mHandlerThread.getLooper());
1007
Kenny Roota02b8b02010-08-05 16:14:17 -07001008 // Add OBB Action Handler to MountService thread.
1009 mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper());
1010
Marco Nelissenc34ebce2010-02-18 13:39:41 -08001011 /*
1012 * Vold does not run in the simulator, so pretend the connector thread
1013 * ran and did its thing.
1014 */
1015 if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
1016 mReady = true;
1017 mUmsEnabling = true;
1018 return;
1019 }
1020
Kenny Root305bcbf2010-09-03 07:56:38 -07001021 /*
1022 * Create the connection to vold with a maximum queue of twice the
1023 * amount of containers we'd ever expect to have. This keeps an
1024 * "asec list" from blocking a thread repeatedly.
1025 */
1026 mConnector = new NativeDaemonConnector(this, "vold",
1027 PackageManagerService.MAX_CONTAINERS * 2, VOLD_TAG);
San Mehat207e5382010-02-04 20:46:54 -08001028 mReady = false;
Kenny Root305bcbf2010-09-03 07:56:38 -07001029 Thread thread = new Thread(mConnector, VOLD_TAG);
San Mehat207e5382010-02-04 20:46:54 -08001030 thread.start();
1031 }
1032
1033 /**
San Mehat4270e1e2010-01-29 05:32:19 -08001034 * Exposed API calls below here
1035 */
1036
1037 public void registerListener(IMountServiceListener listener) {
1038 synchronized (mListeners) {
1039 MountServiceBinderListener bl = new MountServiceBinderListener(listener);
1040 try {
1041 listener.asBinder().linkToDeath(bl, 0);
1042 mListeners.add(bl);
1043 } catch (RemoteException rex) {
San Mehata5078592010-03-25 09:36:54 -07001044 Slog.e(TAG, "Failed to link to listener death");
San Mehat4270e1e2010-01-29 05:32:19 -08001045 }
1046 }
1047 }
1048
1049 public void unregisterListener(IMountServiceListener listener) {
1050 synchronized (mListeners) {
1051 for(MountServiceBinderListener bl : mListeners) {
1052 if (bl.mListener == listener) {
1053 mListeners.remove(mListeners.indexOf(bl));
1054 return;
1055 }
1056 }
1057 }
1058 }
1059
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -08001060 public void shutdown(final IMountShutdownObserver observer) {
San Mehat4270e1e2010-01-29 05:32:19 -08001061 validatePermission(android.Manifest.permission.SHUTDOWN);
1062
San Mehata5078592010-03-25 09:36:54 -07001063 Slog.i(TAG, "Shutting down");
San Mehat4270e1e2010-01-29 05:32:19 -08001064
1065 String path = Environment.getExternalStorageDirectory().getPath();
1066 String state = getVolumeState(path);
San Mehat91c77612010-01-07 10:39:41 -08001067
1068 if (state.equals(Environment.MEDIA_SHARED)) {
1069 /*
1070 * If the media is currently shared, unshare it.
1071 * XXX: This is still dangerous!. We should not
1072 * be rebooting at *all* if UMS is enabled, since
1073 * the UMS host could have dirty FAT cache entries
1074 * yet to flush.
1075 */
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001076 setUsbMassStorageEnabled(false);
San Mehat91c77612010-01-07 10:39:41 -08001077 } else if (state.equals(Environment.MEDIA_CHECKING)) {
1078 /*
1079 * If the media is being checked, then we need to wait for
1080 * it to complete before being able to proceed.
1081 */
1082 // XXX: @hackbod - Should we disable the ANR timer here?
1083 int retries = 30;
1084 while (state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) {
1085 try {
1086 Thread.sleep(1000);
1087 } catch (InterruptedException iex) {
San Mehata5078592010-03-25 09:36:54 -07001088 Slog.e(TAG, "Interrupted while waiting for media", iex);
San Mehat91c77612010-01-07 10:39:41 -08001089 break;
1090 }
1091 state = Environment.getExternalStorageState();
1092 }
1093 if (retries == 0) {
San Mehata5078592010-03-25 09:36:54 -07001094 Slog.e(TAG, "Timed out waiting for media to check");
San Mehat91c77612010-01-07 10:39:41 -08001095 }
1096 }
1097
1098 if (state.equals(Environment.MEDIA_MOUNTED)) {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -08001099 // Post a unmount message.
1100 ShutdownCallBack ucb = new ShutdownCallBack(path, observer);
1101 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
San Mehat4270e1e2010-01-29 05:32:19 -08001102 }
1103 }
1104
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001105 private boolean getUmsEnabling() {
1106 synchronized (mListeners) {
1107 return mUmsEnabling;
1108 }
1109 }
1110
1111 private void setUmsEnabling(boolean enable) {
1112 synchronized (mListeners) {
Tony Wufc711252010-08-09 16:49:19 +08001113 mUmsEnabling = enable;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001114 }
1115 }
1116
San Mehatb1043402010-02-05 08:26:50 -08001117 public boolean isUsbMassStorageConnected() {
San Mehat207e5382010-02-04 20:46:54 -08001118 waitForReady();
San Mehat91c77612010-01-07 10:39:41 -08001119
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001120 if (getUmsEnabling()) {
San Mehatb1043402010-02-05 08:26:50 -08001121 return true;
San Mehat7fd0fee2009-12-17 07:12:23 -08001122 }
San Mehatb1043402010-02-05 08:26:50 -08001123 return doGetShareMethodAvailable("ums");
1124 }
1125
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001126 public void setUsbMassStorageEnabled(boolean enable) {
San Mehatb1043402010-02-05 08:26:50 -08001127 waitForReady();
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001128 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehatb1043402010-02-05 08:26:50 -08001129
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001130 // TODO: Add support for multiple share methods
1131
1132 /*
1133 * If the volume is mounted and we're enabling then unmount it
1134 */
1135 String path = Environment.getExternalStorageDirectory().getPath();
1136 String vs = getVolumeState(path);
1137 String method = "ums";
1138 if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
1139 // Override for isUsbMassStorageEnabled()
1140 setUmsEnabling(enable);
1141 UmsEnableCallBack umscb = new UmsEnableCallBack(path, method, true);
1142 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, umscb));
1143 // Clear override
1144 setUmsEnabling(false);
1145 }
1146 /*
1147 * If we disabled UMS then mount the volume
1148 */
1149 if (!enable) {
1150 doShareUnshareVolume(path, method, enable);
1151 if (doMountVolume(path) != StorageResultCode.OperationSucceeded) {
San Mehata5078592010-03-25 09:36:54 -07001152 Slog.e(TAG, "Failed to remount " + path +
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -08001153 " after disabling share method " + method);
1154 /*
1155 * Even though the mount failed, the unshare didn't so don't indicate an error.
1156 * The mountVolume() call will have set the storage state and sent the necessary
1157 * broadcasts.
1158 */
1159 }
1160 }
San Mehatb1043402010-02-05 08:26:50 -08001161 }
1162
1163 public boolean isUsbMassStorageEnabled() {
1164 waitForReady();
1165 return doGetVolumeShared(Environment.getExternalStorageDirectory().getPath(), "ums");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001166 }
San Mehat4270e1e2010-01-29 05:32:19 -08001167
San Mehat7fd0fee2009-12-17 07:12:23 -08001168 /**
1169 * @return state of the volume at the specified mount point
1170 */
San Mehat4270e1e2010-01-29 05:32:19 -08001171 public String getVolumeState(String mountPoint) {
San Mehat7fd0fee2009-12-17 07:12:23 -08001172 /*
1173 * XXX: Until we have multiple volume discovery, just hardwire
1174 * this to /sdcard
1175 */
1176 if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) {
San Mehata5078592010-03-25 09:36:54 -07001177 Slog.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
San Mehat7fd0fee2009-12-17 07:12:23 -08001178 throw new IllegalArgumentException();
1179 }
1180
1181 return mLegacyState;
1182 }
1183
San Mehat4270e1e2010-01-29 05:32:19 -08001184 public int mountVolume(String path) {
1185 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehat4270e1e2010-01-29 05:32:19 -08001186
San Mehat207e5382010-02-04 20:46:54 -08001187 waitForReady();
1188 return doMountVolume(path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001189 }
1190
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -08001191 public void unmountVolume(String path, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -08001192 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -08001193 waitForReady();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001194
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -08001195 String volState = getVolumeState(path);
San Mehata5078592010-03-25 09:36:54 -07001196 if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path + " force = " + force);
Suchi Amalapurapu8a9ab242010-03-11 16:49:16 -08001197 if (Environment.MEDIA_UNMOUNTED.equals(volState) ||
1198 Environment.MEDIA_REMOVED.equals(volState) ||
1199 Environment.MEDIA_SHARED.equals(volState) ||
1200 Environment.MEDIA_UNMOUNTABLE.equals(volState)) {
1201 // Media already unmounted or cannot be unmounted.
1202 // TODO return valid return code when adding observer call back.
1203 return;
1204 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -08001205 UnmountCallBack ucb = new UnmountCallBack(path, force);
1206 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001207 }
1208
San Mehat4270e1e2010-01-29 05:32:19 -08001209 public int formatVolume(String path) {
1210 validatePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -08001211 waitForReady();
San Mehat5b77dab2010-01-26 13:28:50 -08001212
San Mehat207e5382010-02-04 20:46:54 -08001213 return doFormatVolume(path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001214 }
1215
San Mehatc1b4ce92010-02-16 17:13:03 -08001216 public int []getStorageUsers(String path) {
1217 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1218 waitForReady();
1219 try {
1220 String[] r = mConnector.doListCommand(
1221 String.format("storage users %s", path),
1222 VoldResponseCode.StorageUsersListResult);
1223 // FMT: <pid> <process name>
1224 int[] data = new int[r.length];
1225 for (int i = 0; i < r.length; i++) {
1226 String []tok = r[i].split(" ");
1227 try {
1228 data[i] = Integer.parseInt(tok[0]);
1229 } catch (NumberFormatException nfe) {
San Mehata5078592010-03-25 09:36:54 -07001230 Slog.e(TAG, String.format("Error parsing pid %s", tok[0]));
San Mehatc1b4ce92010-02-16 17:13:03 -08001231 return new int[0];
1232 }
1233 }
1234 return data;
1235 } catch (NativeDaemonConnectorException e) {
San Mehata5078592010-03-25 09:36:54 -07001236 Slog.e(TAG, "Failed to retrieve storage users list", e);
San Mehatc1b4ce92010-02-16 17:13:03 -08001237 return new int[0];
1238 }
1239 }
1240
San Mehatb1043402010-02-05 08:26:50 -08001241 private void warnOnNotMounted() {
1242 if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
San Mehata5078592010-03-25 09:36:54 -07001243 Slog.w(TAG, "getSecureContainerList() called when storage not mounted");
San Mehatb1043402010-02-05 08:26:50 -08001244 }
1245 }
1246
San Mehat4270e1e2010-01-29 05:32:19 -08001247 public String[] getSecureContainerList() {
1248 validatePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001249 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001250 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001251
San Mehat4270e1e2010-01-29 05:32:19 -08001252 try {
1253 return mConnector.doListCommand("asec list", VoldResponseCode.AsecListResult);
1254 } catch (NativeDaemonConnectorException e) {
1255 return new String[0];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001256 }
1257 }
San Mehat36972292010-01-06 11:06:32 -08001258
San Mehat4270e1e2010-01-29 05:32:19 -08001259 public int createSecureContainer(String id, int sizeMb, String fstype,
1260 String key, int ownerUid) {
1261 validatePermission(android.Manifest.permission.ASEC_CREATE);
San Mehat207e5382010-02-04 20:46:54 -08001262 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001263 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001264
San Mehatb1043402010-02-05 08:26:50 -08001265 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001266 String cmd = String.format("asec create %s %d %s %s %d", id, sizeMb, fstype, key, ownerUid);
1267 try {
1268 mConnector.doCommand(cmd);
1269 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001270 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001271 }
San Mehata181b212010-02-11 06:50:20 -08001272
1273 if (rc == StorageResultCode.OperationSucceeded) {
1274 synchronized (mAsecMountSet) {
1275 mAsecMountSet.add(id);
1276 }
1277 }
San Mehat4270e1e2010-01-29 05:32:19 -08001278 return rc;
San Mehat36972292010-01-06 11:06:32 -08001279 }
1280
San Mehat4270e1e2010-01-29 05:32:19 -08001281 public int finalizeSecureContainer(String id) {
1282 validatePermission(android.Manifest.permission.ASEC_CREATE);
San Mehatb1043402010-02-05 08:26:50 -08001283 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001284
San Mehatb1043402010-02-05 08:26:50 -08001285 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001286 try {
1287 mConnector.doCommand(String.format("asec finalize %s", id));
San Mehata181b212010-02-11 06:50:20 -08001288 /*
1289 * Finalization does a remount, so no need
1290 * to update mAsecMountSet
1291 */
San Mehat4270e1e2010-01-29 05:32:19 -08001292 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001293 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001294 }
San Mehat4270e1e2010-01-29 05:32:19 -08001295 return rc;
San Mehat36972292010-01-06 11:06:32 -08001296 }
1297
San Mehatd9709982010-02-18 11:43:03 -08001298 public int destroySecureContainer(String id, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -08001299 validatePermission(android.Manifest.permission.ASEC_DESTROY);
San Mehat207e5382010-02-04 20:46:54 -08001300 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001301 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001302
Kenny Rootaa485402010-09-14 14:49:41 -07001303 /*
1304 * Force a GC to make sure AssetManagers in other threads of the
1305 * system_server are cleaned up. We have to do this since AssetManager
1306 * instances are kept as a WeakReference and it's possible we have files
1307 * open on the external storage.
1308 */
1309 Runtime.getRuntime().gc();
1310
San Mehatb1043402010-02-05 08:26:50 -08001311 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001312 try {
San Mehatd9709982010-02-18 11:43:03 -08001313 mConnector.doCommand(String.format("asec destroy %s%s", id, (force ? " force" : "")));
San Mehat4270e1e2010-01-29 05:32:19 -08001314 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08001315 int code = e.getCode();
1316 if (code == VoldResponseCode.OpFailedStorageBusy) {
1317 rc = StorageResultCode.OperationFailedStorageBusy;
1318 } else {
1319 rc = StorageResultCode.OperationFailedInternalError;
1320 }
San Mehat02735bc2010-01-26 15:18:08 -08001321 }
San Mehata181b212010-02-11 06:50:20 -08001322
1323 if (rc == StorageResultCode.OperationSucceeded) {
1324 synchronized (mAsecMountSet) {
1325 if (mAsecMountSet.contains(id)) {
1326 mAsecMountSet.remove(id);
1327 }
1328 }
1329 }
1330
San Mehat4270e1e2010-01-29 05:32:19 -08001331 return rc;
San Mehat36972292010-01-06 11:06:32 -08001332 }
1333
San Mehat4270e1e2010-01-29 05:32:19 -08001334 public int mountSecureContainer(String id, String key, int ownerUid) {
1335 validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08001336 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001337 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001338
San Mehata181b212010-02-11 06:50:20 -08001339 synchronized (mAsecMountSet) {
1340 if (mAsecMountSet.contains(id)) {
1341 return StorageResultCode.OperationFailedStorageMounted;
1342 }
1343 }
1344
San Mehatb1043402010-02-05 08:26:50 -08001345 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001346 String cmd = String.format("asec mount %s %s %d", id, key, ownerUid);
1347 try {
1348 mConnector.doCommand(cmd);
1349 } catch (NativeDaemonConnectorException e) {
Kenny Rootf0304622010-03-19 19:20:42 -07001350 int code = e.getCode();
1351 if (code != VoldResponseCode.OpFailedStorageBusy) {
1352 rc = StorageResultCode.OperationFailedInternalError;
1353 }
San Mehat02735bc2010-01-26 15:18:08 -08001354 }
San Mehat6cdd9c02010-02-09 14:45:20 -08001355
1356 if (rc == StorageResultCode.OperationSucceeded) {
1357 synchronized (mAsecMountSet) {
1358 mAsecMountSet.add(id);
1359 }
1360 }
San Mehat4270e1e2010-01-29 05:32:19 -08001361 return rc;
San Mehat36972292010-01-06 11:06:32 -08001362 }
1363
San Mehatd9709982010-02-18 11:43:03 -08001364 public int unmountSecureContainer(String id, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -08001365 validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08001366 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001367 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001368
San Mehat6cdd9c02010-02-09 14:45:20 -08001369 synchronized (mAsecMountSet) {
1370 if (!mAsecMountSet.contains(id)) {
San Mehata181b212010-02-11 06:50:20 -08001371 return StorageResultCode.OperationFailedStorageNotMounted;
San Mehat6cdd9c02010-02-09 14:45:20 -08001372 }
1373 }
1374
Kenny Rootaa485402010-09-14 14:49:41 -07001375 /*
1376 * Force a GC to make sure AssetManagers in other threads of the
1377 * system_server are cleaned up. We have to do this since AssetManager
1378 * instances are kept as a WeakReference and it's possible we have files
1379 * open on the external storage.
1380 */
1381 Runtime.getRuntime().gc();
1382
San Mehatb1043402010-02-05 08:26:50 -08001383 int rc = StorageResultCode.OperationSucceeded;
San Mehatd9709982010-02-18 11:43:03 -08001384 String cmd = String.format("asec unmount %s%s", id, (force ? " force" : ""));
San Mehat4270e1e2010-01-29 05:32:19 -08001385 try {
1386 mConnector.doCommand(cmd);
1387 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08001388 int code = e.getCode();
1389 if (code == VoldResponseCode.OpFailedStorageBusy) {
1390 rc = StorageResultCode.OperationFailedStorageBusy;
1391 } else {
1392 rc = StorageResultCode.OperationFailedInternalError;
1393 }
San Mehat02735bc2010-01-26 15:18:08 -08001394 }
San Mehat6cdd9c02010-02-09 14:45:20 -08001395
1396 if (rc == StorageResultCode.OperationSucceeded) {
1397 synchronized (mAsecMountSet) {
1398 mAsecMountSet.remove(id);
1399 }
1400 }
San Mehat4270e1e2010-01-29 05:32:19 -08001401 return rc;
San Mehat9dba7092010-01-18 06:47:41 -08001402 }
1403
San Mehat6cdd9c02010-02-09 14:45:20 -08001404 public boolean isSecureContainerMounted(String id) {
1405 validatePermission(android.Manifest.permission.ASEC_ACCESS);
1406 waitForReady();
1407 warnOnNotMounted();
1408
1409 synchronized (mAsecMountSet) {
1410 return mAsecMountSet.contains(id);
1411 }
1412 }
1413
San Mehat4270e1e2010-01-29 05:32:19 -08001414 public int renameSecureContainer(String oldId, String newId) {
1415 validatePermission(android.Manifest.permission.ASEC_RENAME);
San Mehat207e5382010-02-04 20:46:54 -08001416 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001417 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001418
San Mehata181b212010-02-11 06:50:20 -08001419 synchronized (mAsecMountSet) {
San Mehat85451ee2010-02-24 08:54:18 -08001420 /*
1421 * Because a mounted container has active internal state which cannot be
1422 * changed while active, we must ensure both ids are not currently mounted.
1423 */
1424 if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) {
San Mehata181b212010-02-11 06:50:20 -08001425 return StorageResultCode.OperationFailedStorageMounted;
1426 }
1427 }
1428
San Mehatb1043402010-02-05 08:26:50 -08001429 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001430 String cmd = String.format("asec rename %s %s", oldId, newId);
1431 try {
1432 mConnector.doCommand(cmd);
1433 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001434 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001435 }
San Mehata181b212010-02-11 06:50:20 -08001436
San Mehat4270e1e2010-01-29 05:32:19 -08001437 return rc;
San Mehat45f61042010-01-23 08:12:43 -08001438 }
1439
San Mehat4270e1e2010-01-29 05:32:19 -08001440 public String getSecureContainerPath(String id) {
1441 validatePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001442 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001443 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001444
San Mehat2d66cef2010-03-23 11:12:52 -07001445 try {
1446 ArrayList<String> rsp = mConnector.doCommand(String.format("asec path %s", id));
1447 String []tok = rsp.get(0).split(" ");
San Mehat22dd86e2010-01-12 12:21:18 -08001448 int code = Integer.parseInt(tok[0]);
San Mehat2d66cef2010-03-23 11:12:52 -07001449 if (code != VoldResponseCode.AsecPathResult) {
1450 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1451 }
1452 return tok[1];
1453 } catch (NativeDaemonConnectorException e) {
1454 int code = e.getCode();
1455 if (code == VoldResponseCode.OpFailedStorageNotFound) {
1456 throw new IllegalArgumentException(String.format("Container '%s' not found", id));
San Mehat22dd86e2010-01-12 12:21:18 -08001457 } else {
San Mehat2d66cef2010-03-23 11:12:52 -07001458 throw new IllegalStateException(String.format("Unexpected response code %d", code));
San Mehat22dd86e2010-01-12 12:21:18 -08001459 }
1460 }
San Mehat22dd86e2010-01-12 12:21:18 -08001461 }
Suchi Amalapurapue99bb5f2010-03-19 14:36:49 -07001462
1463 public void finishMediaUpdate() {
1464 mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
1465 }
Kenny Root02c87302010-07-01 08:10:18 -07001466
Kenny Roota02b8b02010-08-05 16:14:17 -07001467 private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) {
1468 if (callerUid == android.os.Process.SYSTEM_UID) {
1469 return true;
1470 }
1471
Kenny Root02c87302010-07-01 08:10:18 -07001472 if (packageName == null) {
1473 return false;
1474 }
1475
1476 final int packageUid = mPms.getPackageUid(packageName);
1477
1478 if (DEBUG_OBB) {
1479 Slog.d(TAG, "packageName = " + packageName + ", packageUid = " +
1480 packageUid + ", callerUid = " + callerUid);
1481 }
1482
1483 return callerUid == packageUid;
1484 }
1485
1486 public String getMountedObbPath(String filename) {
1487 waitForReady();
1488 warnOnNotMounted();
1489
Kenny Root02c87302010-07-01 08:10:18 -07001490 try {
1491 ArrayList<String> rsp = mConnector.doCommand(String.format("obb path %s", filename));
1492 String []tok = rsp.get(0).split(" ");
1493 int code = Integer.parseInt(tok[0]);
1494 if (code != VoldResponseCode.AsecPathResult) {
1495 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1496 }
1497 return tok[1];
1498 } catch (NativeDaemonConnectorException e) {
1499 int code = e.getCode();
1500 if (code == VoldResponseCode.OpFailedStorageNotFound) {
Kenny Roota02b8b02010-08-05 16:14:17 -07001501 return null;
Kenny Root02c87302010-07-01 08:10:18 -07001502 } else {
1503 throw new IllegalStateException(String.format("Unexpected response code %d", code));
1504 }
1505 }
1506 }
1507
1508 public boolean isObbMounted(String filename) {
Kenny Roota02b8b02010-08-05 16:14:17 -07001509 synchronized (mObbMounts) {
1510 return mObbPathToStateMap.containsKey(filename);
Kenny Root02c87302010-07-01 08:10:18 -07001511 }
1512 }
1513
Kenny Roota02b8b02010-08-05 16:14:17 -07001514 public void mountObb(String filename, String key, IObbActionListener token) {
Kenny Root02c87302010-07-01 08:10:18 -07001515 waitForReady();
1516 warnOnNotMounted();
1517
Kenny Rootf1121dc2010-09-29 07:30:53 -07001518 if (filename == null) {
1519 throw new IllegalArgumentException("filename cannot be null");
1520 } else if (token == null) {
1521 throw new IllegalArgumentException("token cannot be null");
1522 }
1523
Kenny Roota02b8b02010-08-05 16:14:17 -07001524 final ObbState obbState;
1525
1526 synchronized (mObbMounts) {
1527 if (isObbMounted(filename)) {
1528 throw new IllegalArgumentException("OBB file is already mounted");
Kenny Root02c87302010-07-01 08:10:18 -07001529 }
Kenny Roota02b8b02010-08-05 16:14:17 -07001530
Kenny Roota02b8b02010-08-05 16:14:17 -07001531 final int callerUid = Binder.getCallingUid();
1532 obbState = new ObbState(filename, token, callerUid);
1533 addObbState(obbState);
Kenny Root02c87302010-07-01 08:10:18 -07001534 }
1535
Kenny Root02c87302010-07-01 08:10:18 -07001536 try {
Kenny Roota02b8b02010-08-05 16:14:17 -07001537 token.asBinder().linkToDeath(obbState, 0);
1538 } catch (RemoteException rex) {
1539 Slog.e(TAG, "Failed to link to listener death");
Kenny Root02c87302010-07-01 08:10:18 -07001540 }
1541
Kenny Roota02b8b02010-08-05 16:14:17 -07001542 MountObbAction action = new MountObbAction(obbState, key);
1543 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
1544
1545 if (DEBUG_OBB)
1546 Slog.i(TAG, "Send to OBB handler: " + action.toString());
Kenny Root02c87302010-07-01 08:10:18 -07001547 }
1548
Kenny Roota02b8b02010-08-05 16:14:17 -07001549 public void unmountObb(String filename, boolean force, IObbActionListener token) {
Kenny Rootf1121dc2010-09-29 07:30:53 -07001550 if (filename == null) {
1551 throw new IllegalArgumentException("filename cannot be null");
1552 } else if (token == null) {
1553 throw new IllegalArgumentException("token cannot be null");
1554 }
1555
Kenny Roota02b8b02010-08-05 16:14:17 -07001556 final ObbState obbState;
Kenny Root02c87302010-07-01 08:10:18 -07001557
Kenny Roota02b8b02010-08-05 16:14:17 -07001558 synchronized (mObbMounts) {
1559 if (!isObbMounted(filename)) {
1560 throw new IllegalArgumentException("OBB is not mounted");
1561 }
1562 obbState = mObbPathToStateMap.get(filename);
Kenny Rootf1121dc2010-09-29 07:30:53 -07001563
1564 if (Binder.getCallingUid() != obbState.callerUid) {
1565 throw new SecurityException("caller UID does not match original mount caller UID");
1566 } else if (!token.asBinder().equals(obbState.token.asBinder())) {
1567 throw new SecurityException("caller does not match original mount caller");
1568 }
Kenny Root02c87302010-07-01 08:10:18 -07001569 }
1570
Kenny Roota02b8b02010-08-05 16:14:17 -07001571 UnmountObbAction action = new UnmountObbAction(obbState, force);
1572 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action));
Kenny Root02c87302010-07-01 08:10:18 -07001573
Kenny Roota02b8b02010-08-05 16:14:17 -07001574 if (DEBUG_OBB)
1575 Slog.i(TAG, "Send to OBB handler: " + action.toString());
1576 }
1577
1578 private void addObbState(ObbState obbState) {
1579 synchronized (mObbMounts) {
Kenny Root05105f72010-09-22 17:29:43 -07001580 List<ObbState> obbStates = mObbMounts.get(obbState.token);
1581 if (obbStates == null) {
1582 obbStates = new ArrayList<ObbState>();
1583 mObbMounts.put(obbState.token, obbStates);
1584 }
1585 obbStates.add(obbState);
Kenny Roota02b8b02010-08-05 16:14:17 -07001586 mObbPathToStateMap.put(obbState.filename, obbState);
1587 }
1588 }
1589
1590 private void removeObbState(ObbState obbState) {
1591 synchronized (mObbMounts) {
Kenny Root05105f72010-09-22 17:29:43 -07001592 final List<ObbState> obbStates = mObbMounts.get(obbState.token);
1593 if (obbStates != null) {
1594 obbStates.remove(obbState);
1595 }
1596 if (obbStates == null || obbStates.isEmpty()) {
1597 mObbMounts.remove(obbState.token);
1598 }
Kenny Roota02b8b02010-08-05 16:14:17 -07001599 mObbPathToStateMap.remove(obbState.filename);
1600 }
1601 }
1602
1603 private class ObbActionHandler extends Handler {
1604 private boolean mBound = false;
1605 private List<ObbAction> mActions = new LinkedList<ObbAction>();
1606
1607 ObbActionHandler(Looper l) {
1608 super(l);
1609 }
1610
1611 @Override
1612 public void handleMessage(Message msg) {
1613 switch (msg.what) {
1614 case OBB_RUN_ACTION: {
1615 ObbAction action = (ObbAction) msg.obj;
1616
1617 if (DEBUG_OBB)
1618 Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString());
1619
1620 // If a bind was already initiated we don't really
1621 // need to do anything. The pending install
1622 // will be processed later on.
1623 if (!mBound) {
1624 // If this is the only one pending we might
1625 // have to bind to the service again.
1626 if (!connectToService()) {
1627 Slog.e(TAG, "Failed to bind to media container service");
1628 action.handleError();
1629 return;
1630 } else {
1631 // Once we bind to the service, the first
1632 // pending request will be processed.
1633 mActions.add(action);
1634 }
1635 } else {
1636 // Already bound to the service. Just make
1637 // sure we trigger off processing the first request.
1638 if (mActions.size() == 0) {
1639 mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
1640 }
1641
1642 mActions.add(action);
1643 }
1644 break;
1645 }
1646 case OBB_MCS_BOUND: {
1647 if (DEBUG_OBB)
1648 Slog.i(TAG, "OBB_MCS_BOUND");
1649 if (msg.obj != null) {
1650 mContainerService = (IMediaContainerService) msg.obj;
1651 }
1652 if (mContainerService == null) {
1653 // Something seriously wrong. Bail out
1654 Slog.e(TAG, "Cannot bind to media container service");
1655 for (ObbAction action : mActions) {
1656 // Indicate service bind error
1657 action.handleError();
1658 }
1659 mActions.clear();
1660 } else if (mActions.size() > 0) {
1661 ObbAction action = mActions.get(0);
1662 if (action != null) {
1663 action.execute(this);
1664 }
1665 } else {
1666 // Should never happen ideally.
1667 Slog.w(TAG, "Empty queue");
1668 }
1669 break;
1670 }
1671 case OBB_MCS_RECONNECT: {
1672 if (DEBUG_OBB)
1673 Slog.i(TAG, "OBB_MCS_RECONNECT");
1674 if (mActions.size() > 0) {
1675 if (mBound) {
1676 disconnectService();
1677 }
1678 if (!connectToService()) {
1679 Slog.e(TAG, "Failed to bind to media container service");
1680 for (ObbAction action : mActions) {
1681 // Indicate service bind error
1682 action.handleError();
1683 }
1684 mActions.clear();
1685 }
1686 }
1687 break;
1688 }
1689 case OBB_MCS_UNBIND: {
1690 if (DEBUG_OBB)
1691 Slog.i(TAG, "OBB_MCS_UNBIND");
1692
1693 // Delete pending install
1694 if (mActions.size() > 0) {
1695 mActions.remove(0);
1696 }
1697 if (mActions.size() == 0) {
1698 if (mBound) {
1699 disconnectService();
1700 }
1701 } else {
1702 // There are more pending requests in queue.
1703 // Just post MCS_BOUND message to trigger processing
1704 // of next pending install.
1705 mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND);
1706 }
1707 break;
1708 }
1709 case OBB_MCS_GIVE_UP: {
1710 if (DEBUG_OBB)
1711 Slog.i(TAG, "OBB_MCS_GIVE_UP");
1712 mActions.remove(0);
1713 break;
1714 }
1715 }
1716 }
1717
1718 private boolean connectToService() {
1719 if (DEBUG_OBB)
1720 Slog.i(TAG, "Trying to bind to DefaultContainerService");
1721
1722 Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
1723 if (mContext.bindService(service, mDefContainerConn, Context.BIND_AUTO_CREATE)) {
1724 mBound = true;
1725 return true;
1726 }
1727 return false;
1728 }
1729
1730 private void disconnectService() {
1731 mContainerService = null;
1732 mBound = false;
1733 mContext.unbindService(mDefContainerConn);
1734 }
1735 }
1736
1737 abstract class ObbAction {
1738 private static final int MAX_RETRIES = 3;
1739 private int mRetries;
1740
1741 ObbState mObbState;
1742
1743 ObbAction(ObbState obbState) {
1744 mObbState = obbState;
1745 }
1746
1747 public void execute(ObbActionHandler handler) {
1748 try {
1749 if (DEBUG_OBB)
1750 Slog.i(TAG, "Starting to execute action: " + this.toString());
1751 mRetries++;
1752 if (mRetries > MAX_RETRIES) {
1753 Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
1754 mObbActionHandler.sendEmptyMessage(OBB_MCS_GIVE_UP);
1755 handleError();
1756 return;
1757 } else {
1758 handleExecute();
1759 if (DEBUG_OBB)
1760 Slog.i(TAG, "Posting install MCS_UNBIND");
1761 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND);
1762 }
1763 } catch (RemoteException e) {
1764 if (DEBUG_OBB)
1765 Slog.i(TAG, "Posting install MCS_RECONNECT");
1766 mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT);
1767 } catch (Exception e) {
1768 if (DEBUG_OBB)
1769 Slog.d(TAG, "Error handling OBB action", e);
1770 handleError();
1771 }
1772 }
1773
Kenny Root05105f72010-09-22 17:29:43 -07001774 abstract void handleExecute() throws RemoteException, IOException;
Kenny Roota02b8b02010-08-05 16:14:17 -07001775 abstract void handleError();
1776 }
1777
1778 class MountObbAction extends ObbAction {
1779 private String mKey;
1780
1781 MountObbAction(ObbState obbState, String key) {
1782 super(obbState);
1783 mKey = key;
1784 }
1785
Kenny Root05105f72010-09-22 17:29:43 -07001786 public void handleExecute() throws RemoteException, IOException {
Kenny Rootf1121dc2010-09-29 07:30:53 -07001787 final ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename);
Kenny Root05105f72010-09-22 17:29:43 -07001788 if (obbInfo == null) {
Kenny Rootf1121dc2010-09-29 07:30:53 -07001789 throw new IOException("Couldn't read OBB file: " + mObbState.filename);
Kenny Root05105f72010-09-22 17:29:43 -07001790 }
1791
Kenny Roota02b8b02010-08-05 16:14:17 -07001792 if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mObbState.callerUid)) {
1793 throw new IllegalArgumentException("Caller package does not match OBB file");
1794 }
1795
1796 if (mKey == null) {
1797 mKey = "none";
1798 }
1799
1800 int rc = StorageResultCode.OperationSucceeded;
1801 String cmd = String.format("obb mount %s %s %d", mObbState.filename, mKey,
1802 mObbState.callerUid);
1803 try {
1804 mConnector.doCommand(cmd);
1805 } catch (NativeDaemonConnectorException e) {
1806 int code = e.getCode();
1807 if (code != VoldResponseCode.OpFailedStorageBusy) {
1808 rc = StorageResultCode.OperationFailedInternalError;
1809 }
1810 }
1811
1812 if (rc == StorageResultCode.OperationSucceeded) {
1813 try {
Kenny Root05105f72010-09-22 17:29:43 -07001814 mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_MOUNTED);
Kenny Roota02b8b02010-08-05 16:14:17 -07001815 } catch (RemoteException e) {
1816 Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
1817 }
Kenny Root02c87302010-07-01 08:10:18 -07001818 } else {
Kenny Root05105f72010-09-22 17:29:43 -07001819 Slog.e(TAG, "Couldn't mount OBB file: " + rc);
Kenny Roota02b8b02010-08-05 16:14:17 -07001820
1821 // We didn't succeed, so remove this from the mount-set.
1822 removeObbState(mObbState);
Kenny Root05105f72010-09-22 17:29:43 -07001823
1824 mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
Kenny Root02c87302010-07-01 08:10:18 -07001825 }
1826 }
1827
Kenny Roota02b8b02010-08-05 16:14:17 -07001828 public void handleError() {
1829 removeObbState(mObbState);
1830
1831 try {
Kenny Root05105f72010-09-22 17:29:43 -07001832 mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
Kenny Roota02b8b02010-08-05 16:14:17 -07001833 } catch (RemoteException e) {
1834 Slog.e(TAG, "Couldn't send back OBB mount error for " + mObbState.filename);
Kenny Root02c87302010-07-01 08:10:18 -07001835 }
1836 }
Kenny Roota02b8b02010-08-05 16:14:17 -07001837
1838 @Override
1839 public String toString() {
1840 StringBuilder sb = new StringBuilder();
1841 sb.append("MountObbAction{");
1842 sb.append("filename=");
1843 sb.append(mObbState.filename);
1844 sb.append(",callerUid=");
1845 sb.append(mObbState.callerUid);
1846 sb.append(",token=");
1847 sb.append(mObbState.token != null ? mObbState.token.toString() : "NULL");
1848 sb.append('}');
1849 return sb.toString();
1850 }
1851 }
1852
1853 class UnmountObbAction extends ObbAction {
1854 private boolean mForceUnmount;
1855
1856 UnmountObbAction(ObbState obbState, boolean force) {
1857 super(obbState);
1858 mForceUnmount = force;
1859 }
1860
Kenny Root05105f72010-09-22 17:29:43 -07001861 public void handleExecute() throws RemoteException, IOException {
Kenny Rootf1121dc2010-09-29 07:30:53 -07001862 final ObbInfo obbInfo = mContainerService.getObbInfo(mObbState.filename);
Kenny Root05105f72010-09-22 17:29:43 -07001863 if (obbInfo == null) {
Kenny Rootf1121dc2010-09-29 07:30:53 -07001864 throw new IOException("Couldn't read OBB file: " + mObbState.filename);
Kenny Roota02b8b02010-08-05 16:14:17 -07001865 }
1866
1867 int rc = StorageResultCode.OperationSucceeded;
1868 String cmd = String.format("obb unmount %s%s", mObbState.filename,
1869 (mForceUnmount ? " force" : ""));
1870 try {
1871 mConnector.doCommand(cmd);
1872 } catch (NativeDaemonConnectorException e) {
1873 int code = e.getCode();
1874 if (code == VoldResponseCode.OpFailedStorageBusy) {
1875 rc = StorageResultCode.OperationFailedStorageBusy;
1876 } else {
1877 rc = StorageResultCode.OperationFailedInternalError;
1878 }
1879 }
1880
1881 if (rc == StorageResultCode.OperationSucceeded) {
1882 removeObbState(mObbState);
1883
1884 try {
Kenny Root05105f72010-09-22 17:29:43 -07001885 mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_UNMOUNTED);
Kenny Roota02b8b02010-08-05 16:14:17 -07001886 } catch (RemoteException e) {
1887 Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
1888 }
1889 } else {
1890 try {
Kenny Root05105f72010-09-22 17:29:43 -07001891 mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
Kenny Roota02b8b02010-08-05 16:14:17 -07001892 } catch (RemoteException e) {
1893 Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged");
1894 }
1895 }
1896 }
1897
1898 public void handleError() {
1899 removeObbState(mObbState);
1900
1901 try {
Kenny Root05105f72010-09-22 17:29:43 -07001902 mObbState.token.onObbResult(mObbState.filename, Environment.MEDIA_BAD_REMOVAL);
Kenny Roota02b8b02010-08-05 16:14:17 -07001903 } catch (RemoteException e) {
1904 Slog.e(TAG, "Couldn't send back OBB unmount error for " + mObbState.filename);
1905 }
1906 }
1907
1908 @Override
1909 public String toString() {
1910 StringBuilder sb = new StringBuilder();
1911 sb.append("UnmountObbAction{");
1912 sb.append("filename=");
1913 sb.append(mObbState.filename != null ? mObbState.filename : "null");
1914 sb.append(",force=");
1915 sb.append(mForceUnmount);
1916 sb.append(",callerUid=");
1917 sb.append(mObbState.callerUid);
1918 sb.append(",token=");
1919 sb.append(mObbState.token != null ? mObbState.token.toString() : "null");
1920 sb.append('}');
1921 return sb.toString();
1922 }
Kenny Root02c87302010-07-01 08:10:18 -07001923 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001924}
1925