blob: b8b94a1cc881c600ed37f64fa7ab03ce3b618537 [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
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080019import com.android.server.am.ActivityManagerService;
20
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import android.content.BroadcastReceiver;
22import android.content.Context;
23import android.content.Intent;
24import android.content.IntentFilter;
25import android.content.pm.PackageManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.net.Uri;
San Mehatb1043402010-02-05 08:26:50 -080027import android.os.storage.IMountService;
28import android.os.storage.IMountServiceListener;
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -080029import android.os.storage.IMountShutdownObserver;
San Mehatb1043402010-02-05 08:26:50 -080030import android.os.storage.StorageResultCode;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080031import android.os.Handler;
Daniel Sandler5f27ef42010-03-16 15:42:02 -040032import android.os.HandlerThread;
33import android.os.Looper;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -080034import android.os.Message;
San Mehat4270e1e2010-01-29 05:32:19 -080035import android.os.RemoteException;
36import android.os.IBinder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.os.Environment;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -080038import android.os.ServiceManager;
San Mehat207e5382010-02-04 20:46:54 -080039import android.os.SystemClock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.os.SystemProperties;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.util.Log;
San Mehat22dd86e2010-01-12 12:21:18 -080042import java.util.ArrayList;
San Mehat6cdd9c02010-02-09 14:45:20 -080043import java.util.HashSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045/**
San Mehatb1043402010-02-05 08:26:50 -080046 * MountService implements back-end services for platform storage
47 * management.
48 * @hide - Applications should use android.os.storage.StorageManager
49 * to access the MountService.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050 */
San Mehat22dd86e2010-01-12 12:21:18 -080051class MountService extends IMountService.Stub
52 implements INativeDaemonConnectorCallbacks {
San Mehatb1043402010-02-05 08:26:50 -080053 private static final boolean LOCAL_LOGD = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054
55 private static final String TAG = "MountService";
56
San Mehat4270e1e2010-01-29 05:32:19 -080057 /*
58 * Internal vold volume state constants
59 */
San Mehat7fd0fee2009-12-17 07:12:23 -080060 class VolumeState {
61 public static final int Init = -1;
62 public static final int NoMedia = 0;
63 public static final int Idle = 1;
64 public static final int Pending = 2;
65 public static final int Checking = 3;
66 public static final int Mounted = 4;
67 public static final int Unmounting = 5;
68 public static final int Formatting = 6;
69 public static final int Shared = 7;
70 public static final int SharedMnt = 8;
71 }
72
San Mehat4270e1e2010-01-29 05:32:19 -080073 /*
74 * Internal vold response code constants
75 */
San Mehat22dd86e2010-01-12 12:21:18 -080076 class VoldResponseCode {
San Mehat4270e1e2010-01-29 05:32:19 -080077 /*
78 * 100 series - Requestion action was initiated; expect another reply
79 * before proceeding with a new command.
80 */
San Mehat22dd86e2010-01-12 12:21:18 -080081 public static final int VolumeListResult = 110;
82 public static final int AsecListResult = 111;
San Mehatc1b4ce92010-02-16 17:13:03 -080083 public static final int StorageUsersListResult = 112;
San Mehat22dd86e2010-01-12 12:21:18 -080084
San Mehat4270e1e2010-01-29 05:32:19 -080085 /*
86 * 200 series - Requestion action has been successfully completed.
87 */
88 public static final int ShareStatusResult = 210;
San Mehat22dd86e2010-01-12 12:21:18 -080089 public static final int AsecPathResult = 211;
San Mehat4270e1e2010-01-29 05:32:19 -080090 public static final int ShareEnabledResult = 212;
San Mehat22dd86e2010-01-12 12:21:18 -080091
San Mehat4270e1e2010-01-29 05:32:19 -080092 /*
93 * 400 series - Command was accepted, but the requested action
94 * did not take place.
95 */
96 public static final int OpFailedNoMedia = 401;
97 public static final int OpFailedMediaBlank = 402;
98 public static final int OpFailedMediaCorrupt = 403;
99 public static final int OpFailedVolNotMounted = 404;
San Mehatd9709982010-02-18 11:43:03 -0800100 public static final int OpFailedStorageBusy = 405;
San Mehat4270e1e2010-01-29 05:32:19 -0800101
102 /*
103 * 600 series - Unsolicited broadcasts.
104 */
San Mehat22dd86e2010-01-12 12:21:18 -0800105 public static final int VolumeStateChange = 605;
San Mehat22dd86e2010-01-12 12:21:18 -0800106 public static final int ShareAvailabilityChange = 620;
107 public static final int VolumeDiskInserted = 630;
108 public static final int VolumeDiskRemoved = 631;
109 public static final int VolumeBadRemoval = 632;
110 }
111
San Mehat4270e1e2010-01-29 05:32:19 -0800112 private Context mContext;
113 private NativeDaemonConnector mConnector;
114 private String mLegacyState = Environment.MEDIA_REMOVED;
115 private PackageManagerService mPms;
116 private boolean mUmsEnabling;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800117 // Used as a lock for methods that register/unregister listeners.
118 final private ArrayList<MountServiceBinderListener> mListeners =
119 new ArrayList<MountServiceBinderListener>();
San Mehat6a965af22010-02-24 17:47:30 -0800120 private boolean mBooted = false;
121 private boolean mReady = false;
122 private boolean mSendUmsConnectedOnBoot = false;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -0800123
San Mehat6cdd9c02010-02-09 14:45:20 -0800124 /**
125 * Private hash of currently mounted secure containers.
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800126 * Used as a lock in methods to manipulate secure containers.
San Mehat6cdd9c02010-02-09 14:45:20 -0800127 */
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800128 final private HashSet<String> mAsecMountSet = new HashSet<String>();
San Mehat6cdd9c02010-02-09 14:45:20 -0800129
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800130 private static final int H_UNMOUNT_PM_UPDATE = 1;
131 private static final int H_UNMOUNT_PM_DONE = 2;
132 private static final int H_UNMOUNT_MS = 3;
133 private static final int RETRY_UNMOUNT_DELAY = 30; // in ms
134 private static final int MAX_UNMOUNT_RETRIES = 4;
135
136 private IntentFilter mPmFilter = new IntentFilter(
137 Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
138 private BroadcastReceiver mPmReceiver = new BroadcastReceiver() {
139 public void onReceive(Context context, Intent intent) {
140 String action = intent.getAction();
141 if (Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE.equals(action)) {
142 mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
143 }
144 }
145 };
146
147 class UnmountCallBack {
148 String path;
149 int retries;
150 boolean force;
151
152 UnmountCallBack(String path, boolean force) {
153 retries = 0;
154 this.path = path;
155 this.force = force;
156 }
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800157
158 void handleFinished() {
159 doUnmountVolume(path, true);
160 }
161 }
162
163 class UmsEnableCallBack extends UnmountCallBack {
164 String method;
165
166 UmsEnableCallBack(String path, String method, boolean force) {
167 super(path, force);
168 this.method = method;
169 }
170
171 @Override
172 void handleFinished() {
173 super.handleFinished();
174 doShareUnshareVolume(path, method, true);
175 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800176 }
177
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800178 class ShutdownCallBack extends UnmountCallBack {
179 IMountShutdownObserver observer;
180 ShutdownCallBack(String path, IMountShutdownObserver observer) {
181 super(path, true);
182 this.observer = observer;
183 }
184
185 @Override
186 void handleFinished() {
187 int ret = doUnmountVolume(path, true);
188 if (observer != null) {
189 try {
190 observer.onShutDownComplete(ret);
191 } catch (RemoteException e) {
192 Log.w(TAG, "RemoteException when shutting down");
193 }
194 }
195 }
196 }
197
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400198 class MountServiceHandler extends Handler {
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800199 ArrayList<UnmountCallBack> mForceUnmounts = new ArrayList<UnmountCallBack>();
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800200 boolean mRegistered = false;
201
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400202 MountServiceHandler(Looper l) {
203 super(l);
204 }
205
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800206 void registerReceiver() {
207 mRegistered = true;
208 mContext.registerReceiver(mPmReceiver, mPmFilter);
209 }
210
211 void unregisterReceiver() {
212 mRegistered = false;
213 mContext.unregisterReceiver(mPmReceiver);
214 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800215
216 public void handleMessage(Message msg) {
217 switch (msg.what) {
218 case H_UNMOUNT_PM_UPDATE: {
219 UnmountCallBack ucb = (UnmountCallBack) msg.obj;
220 mForceUnmounts.add(ucb);
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800221 // Register only if needed.
222 if (!mRegistered) {
223 registerReceiver();
224 boolean hasExtPkgs = mPms.updateExternalMediaStatus(false);
225 if (!hasExtPkgs) {
226 // Unregister right away
227 mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE);
228 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800229 }
230 break;
231 }
232 case H_UNMOUNT_PM_DONE: {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800233 // Unregister now.
234 if (mRegistered) {
235 unregisterReceiver();
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800236 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800237 int size = mForceUnmounts.size();
238 int sizeArr[] = new int[size];
239 int sizeArrN = 0;
240 for (int i = 0; i < size; i++) {
241 UnmountCallBack ucb = mForceUnmounts.get(i);
242 String path = ucb.path;
243 boolean done = false;
244 if (!ucb.force) {
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800245 done = true;
246 } else {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800247 int pids[] = getStorageUsers(path);
248 if (pids == null || pids.length == 0) {
249 done = true;
250 } else {
251 // Kill processes holding references first
252 ActivityManagerService ams = (ActivityManagerService)
253 ServiceManager.getService("activity");
254 // Eliminate system process here?
255 boolean ret = ams.killPidsForMemory(pids);
256 if (ret) {
257 // Confirm if file references have been freed.
258 pids = getStorageUsers(path);
259 if (pids == null || pids.length == 0) {
260 done = true;
261 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800262 }
263 }
264 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800265 if (done) {
266 sizeArr[sizeArrN++] = i;
267 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS,
268 ucb));
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800269 } else {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800270 if (ucb.retries >= MAX_UNMOUNT_RETRIES) {
271 Log.i(TAG, "Cannot unmount inspite of " +
272 MAX_UNMOUNT_RETRIES + " to unmount media");
273 // Send final broadcast indicating failure to unmount.
274 } else {
275 mHandler.sendMessageDelayed(
276 mHandler.obtainMessage(H_UNMOUNT_PM_DONE,
277 ucb.retries++),
278 RETRY_UNMOUNT_DELAY);
279 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800280 }
281 }
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800282 // Remove already processed elements from list.
283 for (int i = (sizeArrN-1); i >= 0; i--) {
284 mForceUnmounts.remove(sizeArr[i]);
285 }
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800286 break;
287 }
288 case H_UNMOUNT_MS : {
289 UnmountCallBack ucb = (UnmountCallBack) msg.obj;
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800290 ucb.handleFinished();
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800291 break;
292 }
293 }
294 }
295 };
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400296 final private HandlerThread mHandlerThread;
297 final private Handler mHandler;
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800298
San Mehat207e5382010-02-04 20:46:54 -0800299 private void waitForReady() {
300 while (mReady == false) {
301 for (int retries = 5; retries > 0; retries--) {
302 if (mReady) {
303 return;
304 }
305 SystemClock.sleep(1000);
306 }
307 Log.w(TAG, "Waiting too long for mReady!");
308 }
San Mehat1f6301e2010-01-07 22:40:27 -0800309 }
310
San Mehat207e5382010-02-04 20:46:54 -0800311 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 public void onReceive(Context context, Intent intent) {
San Mehat91c77612010-01-07 10:39:41 -0800313 String action = intent.getAction();
314
315 if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
San Mehat207e5382010-02-04 20:46:54 -0800316 mBooted = true;
San Mehat22dd86e2010-01-12 12:21:18 -0800317
Marco Nelissenc34ebce2010-02-18 13:39:41 -0800318 /*
319 * In the simulator, we need to broadcast a volume mounted event
320 * to make the media scanner run.
321 */
322 if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
323 notifyVolumeStateChange(null, "/sdcard", VolumeState.NoMedia, VolumeState.Mounted);
324 return;
325 }
San Mehatfafb0412010-02-18 19:40:04 -0800326 new Thread() {
327 public void run() {
328 try {
329 String path = Environment.getExternalStorageDirectory().getPath();
330 if (getVolumeState(
331 Environment.getExternalStorageDirectory().getPath()).equals(
332 Environment.MEDIA_UNMOUNTED)) {
333 int rc = doMountVolume(path);
334 if (rc != StorageResultCode.OperationSucceeded) {
335 Log.e(TAG, String.format("Boot-time mount failed (%d)", rc));
336 }
337 }
San Mehat6a965af22010-02-24 17:47:30 -0800338 /*
339 * If UMS is connected in boot, send the connected event
340 * now that we're up.
341 */
342 if (mSendUmsConnectedOnBoot) {
343 sendUmsIntent(true);
344 mSendUmsConnectedOnBoot = false;
345 }
San Mehatfafb0412010-02-18 19:40:04 -0800346 } catch (Exception ex) {
347 Log.e(TAG, "Boot-time mount exception", ex);
348 }
San Mehat207e5382010-02-04 20:46:54 -0800349 }
San Mehatfafb0412010-02-18 19:40:04 -0800350 }.start();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351 }
352 }
353 };
354
San Mehat4270e1e2010-01-29 05:32:19 -0800355 private final class MountServiceBinderListener implements IBinder.DeathRecipient {
356 final IMountServiceListener mListener;
357
358 MountServiceBinderListener(IMountServiceListener listener) {
359 mListener = listener;
360
San Mehat91c77612010-01-07 10:39:41 -0800361 }
362
San Mehat4270e1e2010-01-29 05:32:19 -0800363 public void binderDied() {
San Mehatb1043402010-02-05 08:26:50 -0800364 if (LOCAL_LOGD) Log.d(TAG, "An IMountServiceListener has died!");
San Mehat4270e1e2010-01-29 05:32:19 -0800365 synchronized(mListeners) {
366 mListeners.remove(this);
367 mListener.asBinder().unlinkToDeath(this, 0);
368 }
369 }
370 }
371
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800372 private void doShareUnshareVolume(String path, String method, boolean enable) {
San Mehat4270e1e2010-01-29 05:32:19 -0800373 // TODO: Add support for multiple share methods
374 if (!method.equals("ums")) {
375 throw new IllegalArgumentException(String.format("Method %s not supported", method));
376 }
377
San Mehat4270e1e2010-01-29 05:32:19 -0800378 try {
379 mConnector.doCommand(String.format(
380 "volume %sshare %s %s", (enable ? "" : "un"), path, method));
381 } catch (NativeDaemonConnectorException e) {
382 Log.e(TAG, "Failed to share/unshare", e);
San Mehat4270e1e2010-01-29 05:32:19 -0800383 }
San Mehat4270e1e2010-01-29 05:32:19 -0800384 }
385
San Mehat207e5382010-02-04 20:46:54 -0800386 private void updatePublicVolumeState(String path, String state) {
San Mehat4270e1e2010-01-29 05:32:19 -0800387 if (!path.equals(Environment.getExternalStorageDirectory().getPath())) {
388 Log.w(TAG, "Multiple volumes not currently supported");
389 return;
390 }
San Mehatb1043402010-02-05 08:26:50 -0800391
392 if (mLegacyState.equals(state)) {
393 Log.w(TAG, String.format("Duplicate state transition (%s -> %s)", mLegacyState, state));
394 return;
395 }
San Mehat4270e1e2010-01-29 05:32:19 -0800396
397 String oldState = mLegacyState;
398 mLegacyState = state;
399
400 synchronized (mListeners) {
401 for (int i = mListeners.size() -1; i >= 0; i--) {
402 MountServiceBinderListener bl = mListeners.get(i);
403 try {
San Mehatb1043402010-02-05 08:26:50 -0800404 bl.mListener.onStorageStateChanged(path, oldState, state);
San Mehat4270e1e2010-01-29 05:32:19 -0800405 } catch (RemoteException rex) {
406 Log.e(TAG, "Listener dead");
407 mListeners.remove(i);
408 } catch (Exception ex) {
409 Log.e(TAG, "Listener failed", ex);
410 }
411 }
412 }
413 }
414
415 /**
416 *
417 * Callback from NativeDaemonConnector
418 */
419 public void onDaemonConnected() {
420 /*
421 * Since we'll be calling back into the NativeDaemonConnector,
422 * we need to do our work in a new thread.
423 */
424 new Thread() {
425 public void run() {
426 /**
427 * Determine media state and UMS detection status
428 */
429 String path = Environment.getExternalStorageDirectory().getPath();
430 String state = Environment.MEDIA_REMOVED;
431
432 try {
433 String[] vols = mConnector.doListCommand(
434 "volume list", VoldResponseCode.VolumeListResult);
435 for (String volstr : vols) {
436 String[] tok = volstr.split(" ");
437 // FMT: <label> <mountpoint> <state>
438 if (!tok[1].equals(path)) {
439 Log.w(TAG, String.format(
440 "Skipping unknown volume '%s'",tok[1]));
441 continue;
442 }
443 int st = Integer.parseInt(tok[2]);
444 if (st == VolumeState.NoMedia) {
445 state = Environment.MEDIA_REMOVED;
446 } else if (st == VolumeState.Idle) {
San Mehat207e5382010-02-04 20:46:54 -0800447 state = Environment.MEDIA_UNMOUNTED;
San Mehat4270e1e2010-01-29 05:32:19 -0800448 } else if (st == VolumeState.Mounted) {
449 state = Environment.MEDIA_MOUNTED;
450 Log.i(TAG, "Media already mounted on daemon connection");
451 } else if (st == VolumeState.Shared) {
452 state = Environment.MEDIA_SHARED;
453 Log.i(TAG, "Media shared on daemon connection");
454 } else {
455 throw new Exception(String.format("Unexpected state %d", st));
456 }
457 }
458 if (state != null) {
459 updatePublicVolumeState(path, state);
460 }
461 } catch (Exception e) {
462 Log.e(TAG, "Error processing initial volume state", e);
463 updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
464 }
465
466 try {
San Mehat207e5382010-02-04 20:46:54 -0800467 boolean avail = doGetShareMethodAvailable("ums");
San Mehat4270e1e2010-01-29 05:32:19 -0800468 notifyShareAvailabilityChange("ums", avail);
469 } catch (Exception ex) {
470 Log.w(TAG, "Failed to get share availability");
471 }
San Mehat207e5382010-02-04 20:46:54 -0800472 /*
473 * Now that we've done our initialization, release
474 * the hounds!
475 */
476 mReady = true;
San Mehat4270e1e2010-01-29 05:32:19 -0800477 }
478 }.start();
479 }
480
481 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800482 * Callback from NativeDaemonConnector
483 */
484 public boolean onEvent(int code, String raw, String[] cooked) {
485 Intent in = null;
486
San Mehat4270e1e2010-01-29 05:32:19 -0800487 if (code == VoldResponseCode.VolumeStateChange) {
488 /*
489 * One of the volumes we're managing has changed state.
490 * Format: "NNN Volume <label> <path> state changed
491 * from <old_#> (<old_str>) to <new_#> (<new_str>)"
492 */
493 notifyVolumeStateChange(
494 cooked[2], cooked[3], Integer.parseInt(cooked[7]),
495 Integer.parseInt(cooked[10]));
496 } else if (code == VoldResponseCode.ShareAvailabilityChange) {
497 // FMT: NNN Share method <method> now <available|unavailable>
498 boolean avail = false;
499 if (cooked[5].equals("available")) {
500 avail = true;
501 }
502 notifyShareAvailabilityChange(cooked[3], avail);
503 } else if ((code == VoldResponseCode.VolumeDiskInserted) ||
504 (code == VoldResponseCode.VolumeDiskRemoved) ||
505 (code == VoldResponseCode.VolumeBadRemoval)) {
506 // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
507 // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
508 // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
509 final String label = cooked[2];
510 final String path = cooked[3];
511 int major = -1;
512 int minor = -1;
513
514 try {
515 String devComp = cooked[6].substring(1, cooked[6].length() -1);
516 String[] devTok = devComp.split(":");
517 major = Integer.parseInt(devTok[0]);
518 minor = Integer.parseInt(devTok[1]);
519 } catch (Exception ex) {
520 Log.e(TAG, "Failed to parse major/minor", ex);
521 }
522
San Mehat4270e1e2010-01-29 05:32:19 -0800523 if (code == VoldResponseCode.VolumeDiskInserted) {
524 new Thread() {
525 public void run() {
526 try {
527 int rc;
San Mehatb1043402010-02-05 08:26:50 -0800528 if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
San Mehat4270e1e2010-01-29 05:32:19 -0800529 Log.w(TAG, String.format("Insertion mount failed (%d)", rc));
530 }
531 } catch (Exception ex) {
532 Log.w(TAG, "Failed to mount media on insertion", ex);
533 }
534 }
535 }.start();
536 } else if (code == VoldResponseCode.VolumeDiskRemoved) {
537 /*
538 * This event gets trumped if we're already in BAD_REMOVAL state
539 */
540 if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
541 return true;
542 }
543 /* Send the media unmounted event first */
544 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
545 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
546 mContext.sendBroadcast(in);
547
548 updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
549 in = new Intent(Intent.ACTION_MEDIA_REMOVED, Uri.parse("file://" + path));
550 } else if (code == VoldResponseCode.VolumeBadRemoval) {
551 /* Send the media unmounted event first */
552 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
553 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
554 mContext.sendBroadcast(in);
555
556 updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL);
557 in = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL, Uri.parse("file://" + path));
558 } else {
559 Log.e(TAG, String.format("Unknown code {%d}", code));
560 }
561 } else {
562 return false;
563 }
564
565 if (in != null) {
566 mContext.sendBroadcast(in);
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400567 }
568 return true;
San Mehat4270e1e2010-01-29 05:32:19 -0800569 }
570
San Mehat207e5382010-02-04 20:46:54 -0800571 private void notifyVolumeStateChange(String label, String path, int oldState, int newState) {
San Mehat4270e1e2010-01-29 05:32:19 -0800572 String vs = getVolumeState(path);
573
574 Intent in = null;
575
Mike Lockwoodbf2dd442010-03-03 06:16:52 -0500576 if (oldState == VolumeState.Shared && newState != oldState) {
San Mehat2fe718a2010-03-11 12:01:49 -0800577 if (LOCAL_LOGD) Log.d(TAG, "Sending ACTION_MEDIA_UNSHARED intent");
Mike Lockwoodbf2dd442010-03-03 06:16:52 -0500578 mContext.sendBroadcast(new Intent(Intent.ACTION_MEDIA_UNSHARED,
579 Uri.parse("file://" + path)));
580 }
581
San Mehat4270e1e2010-01-29 05:32:19 -0800582 if (newState == VolumeState.Init) {
583 } else if (newState == VolumeState.NoMedia) {
584 // NoMedia is handled via Disk Remove events
585 } else if (newState == VolumeState.Idle) {
586 /*
587 * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
588 * if we're in the process of enabling UMS
589 */
590 if (!vs.equals(
591 Environment.MEDIA_BAD_REMOVAL) && !vs.equals(
592 Environment.MEDIA_NOFS) && !vs.equals(
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800593 Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) {
San Mehat4270e1e2010-01-29 05:32:19 -0800594 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
595 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
596 }
597 } else if (newState == VolumeState.Pending) {
598 } else if (newState == VolumeState.Checking) {
599 updatePublicVolumeState(path, Environment.MEDIA_CHECKING);
600 in = new Intent(Intent.ACTION_MEDIA_CHECKING, Uri.parse("file://" + path));
601 } else if (newState == VolumeState.Mounted) {
602 updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
603 // Update media status on PackageManagerService to mount packages on sdcard
604 mPms.updateExternalMediaStatus(true);
605 in = new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + path));
606 in.putExtra("read-only", false);
607 } else if (newState == VolumeState.Unmounting) {
608 mPms.updateExternalMediaStatus(false);
609 in = new Intent(Intent.ACTION_MEDIA_EJECT, Uri.parse("file://" + path));
610 } else if (newState == VolumeState.Formatting) {
611 } else if (newState == VolumeState.Shared) {
612 /* Send the media unmounted event first */
613 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
614 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
615 mContext.sendBroadcast(in);
616
617 updatePublicVolumeState(path, Environment.MEDIA_SHARED);
618 in = new Intent(Intent.ACTION_MEDIA_SHARED, Uri.parse("file://" + path));
San Mehat2fe718a2010-03-11 12:01:49 -0800619 if (LOCAL_LOGD) Log.d(TAG, "Sending ACTION_MEDIA_SHARED intent");
San Mehat4270e1e2010-01-29 05:32:19 -0800620 } else if (newState == VolumeState.SharedMnt) {
621 Log.e(TAG, "Live shared mounts not supported yet!");
622 return;
623 } else {
624 Log.e(TAG, "Unhandled VolumeState {" + newState + "}");
625 }
626
627 if (in != null) {
628 mContext.sendBroadcast(in);
629 }
630 }
631
San Mehat207e5382010-02-04 20:46:54 -0800632 private boolean doGetShareMethodAvailable(String method) {
633 ArrayList<String> rsp = mConnector.doCommand("share status " + method);
634
635 for (String line : rsp) {
636 String []tok = line.split(" ");
637 int code;
638 try {
639 code = Integer.parseInt(tok[0]);
640 } catch (NumberFormatException nfe) {
641 Log.e(TAG, String.format("Error parsing code %s", tok[0]));
642 return false;
643 }
644 if (code == VoldResponseCode.ShareStatusResult) {
645 if (tok[2].equals("available"))
646 return true;
647 return false;
648 } else {
649 Log.e(TAG, String.format("Unexpected response code %d", code));
650 return false;
651 }
652 }
653 Log.e(TAG, "Got an empty response");
654 return false;
655 }
656
657 private int doMountVolume(String path) {
San Mehatb1043402010-02-05 08:26:50 -0800658 int rc = StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -0800659
660 try {
661 mConnector.doCommand(String.format("volume mount %s", path));
662 } catch (NativeDaemonConnectorException e) {
663 /*
664 * Mount failed for some reason
665 */
666 Intent in = null;
667 int code = e.getCode();
668 if (code == VoldResponseCode.OpFailedNoMedia) {
669 /*
670 * Attempt to mount but no media inserted
671 */
San Mehatb1043402010-02-05 08:26:50 -0800672 rc = StorageResultCode.OperationFailedNoMedia;
San Mehat207e5382010-02-04 20:46:54 -0800673 } else if (code == VoldResponseCode.OpFailedMediaBlank) {
674 /*
675 * Media is blank or does not contain a supported filesystem
676 */
677 updatePublicVolumeState(path, Environment.MEDIA_NOFS);
678 in = new Intent(Intent.ACTION_MEDIA_NOFS, Uri.parse("file://" + path));
San Mehatb1043402010-02-05 08:26:50 -0800679 rc = StorageResultCode.OperationFailedMediaBlank;
San Mehat207e5382010-02-04 20:46:54 -0800680 } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
681 /*
682 * Volume consistency check failed
683 */
684 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE);
685 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, Uri.parse("file://" + path));
San Mehatb1043402010-02-05 08:26:50 -0800686 rc = StorageResultCode.OperationFailedMediaCorrupt;
San Mehat207e5382010-02-04 20:46:54 -0800687 } else {
San Mehatb1043402010-02-05 08:26:50 -0800688 rc = StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -0800689 }
690
691 /*
692 * Send broadcast intent (if required for the failure)
693 */
694 if (in != null) {
695 mContext.sendBroadcast(in);
696 }
697 }
698
699 return rc;
700 }
701
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800702 /*
703 * If force is not set, we do not unmount if there are
704 * processes holding references to the volume about to be unmounted.
705 * If force is set, all the processes holding references need to be
706 * killed via the ActivityManager before actually unmounting the volume.
707 * This might even take a while and might be retried after timed delays
708 * to make sure we dont end up in an instable state and kill some core
709 * processes.
710 */
San Mehatd9709982010-02-18 11:43:03 -0800711 private int doUnmountVolume(String path, boolean force) {
San Mehat59443a62010-02-09 13:28:45 -0800712 if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) {
San Mehat207e5382010-02-04 20:46:54 -0800713 return VoldResponseCode.OpFailedVolNotMounted;
714 }
715
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800716 // We unmounted the volume. No of the asec containers are available now.
717 synchronized (mAsecMountSet) {
718 mAsecMountSet.clear();
719 }
San Mehat207e5382010-02-04 20:46:54 -0800720 // Notify PackageManager of potential media removal and deal with
721 // return code later on. The caller of this api should be aware or have been
722 // notified that the applications installed on the media will be killed.
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -0800723 // Redundant probably. But no harm in updating state again.
San Mehat207e5382010-02-04 20:46:54 -0800724 mPms.updateExternalMediaStatus(false);
725 try {
San Mehatd9709982010-02-18 11:43:03 -0800726 mConnector.doCommand(String.format(
727 "volume unmount %s%s", path, (force ? " force" : "")));
San Mehatb1043402010-02-05 08:26:50 -0800728 return StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -0800729 } catch (NativeDaemonConnectorException e) {
730 // Don't worry about mismatch in PackageManager since the
731 // call back will handle the status changes any way.
732 int code = e.getCode();
733 if (code == VoldResponseCode.OpFailedVolNotMounted) {
San Mehata181b212010-02-11 06:50:20 -0800734 return StorageResultCode.OperationFailedStorageNotMounted;
San Mehatd9709982010-02-18 11:43:03 -0800735 } else if (code == VoldResponseCode.OpFailedStorageBusy) {
736 return StorageResultCode.OperationFailedStorageBusy;
San Mehat207e5382010-02-04 20:46:54 -0800737 } else {
San Mehatb1043402010-02-05 08:26:50 -0800738 return StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -0800739 }
740 }
741 }
742
743 private int doFormatVolume(String path) {
744 try {
745 String cmd = String.format("volume format %s", path);
746 mConnector.doCommand(cmd);
San Mehatb1043402010-02-05 08:26:50 -0800747 return StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -0800748 } catch (NativeDaemonConnectorException e) {
749 int code = e.getCode();
750 if (code == VoldResponseCode.OpFailedNoMedia) {
San Mehatb1043402010-02-05 08:26:50 -0800751 return StorageResultCode.OperationFailedNoMedia;
San Mehat207e5382010-02-04 20:46:54 -0800752 } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
San Mehatb1043402010-02-05 08:26:50 -0800753 return StorageResultCode.OperationFailedMediaCorrupt;
San Mehat207e5382010-02-04 20:46:54 -0800754 } else {
San Mehatb1043402010-02-05 08:26:50 -0800755 return StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -0800756 }
757 }
758 }
759
San Mehatb1043402010-02-05 08:26:50 -0800760 private boolean doGetVolumeShared(String path, String method) {
761 String cmd = String.format("volume shared %s %s", path, method);
762 ArrayList<String> rsp = mConnector.doCommand(cmd);
763
764 for (String line : rsp) {
765 String []tok = line.split(" ");
766 int code;
767 try {
768 code = Integer.parseInt(tok[0]);
769 } catch (NumberFormatException nfe) {
770 Log.e(TAG, String.format("Error parsing code %s", tok[0]));
771 return false;
772 }
773 if (code == VoldResponseCode.ShareEnabledResult) {
774 if (tok[2].equals("enabled"))
775 return true;
776 return false;
777 } else {
778 Log.e(TAG, String.format("Unexpected response code %d", code));
779 return false;
780 }
781 }
782 Log.e(TAG, "Got an empty response");
783 return false;
784 }
785
San Mehat207e5382010-02-04 20:46:54 -0800786 private void notifyShareAvailabilityChange(String method, final boolean avail) {
San Mehat4270e1e2010-01-29 05:32:19 -0800787 if (!method.equals("ums")) {
788 Log.w(TAG, "Ignoring unsupported share method {" + method + "}");
789 return;
790 }
791
792 synchronized (mListeners) {
793 for (int i = mListeners.size() -1; i >= 0; i--) {
794 MountServiceBinderListener bl = mListeners.get(i);
795 try {
San Mehatb1043402010-02-05 08:26:50 -0800796 bl.mListener.onUsbMassStorageConnectionChanged(avail);
San Mehat4270e1e2010-01-29 05:32:19 -0800797 } catch (RemoteException rex) {
798 Log.e(TAG, "Listener dead");
799 mListeners.remove(i);
800 } catch (Exception ex) {
801 Log.e(TAG, "Listener failed", ex);
802 }
803 }
804 }
805
San Mehat207e5382010-02-04 20:46:54 -0800806 if (mBooted == true) {
San Mehat6a965af22010-02-24 17:47:30 -0800807 sendUmsIntent(avail);
808 } else {
809 mSendUmsConnectedOnBoot = avail;
San Mehat4270e1e2010-01-29 05:32:19 -0800810 }
San Mehat2fe718a2010-03-11 12:01:49 -0800811
812 final String path = Environment.getExternalStorageDirectory().getPath();
813 if (avail == false && getVolumeState(path).equals(Environment.MEDIA_SHARED)) {
814 /*
815 * USB mass storage disconnected while enabled
816 */
817 new Thread() {
818 public void run() {
819 try {
820 int rc;
821 Log.w(TAG, "Disabling UMS after cable disconnect");
822 doShareUnshareVolume(path, "ums", false);
823 if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
824 Log.e(TAG, String.format(
825 "Failed to remount {%s} on UMS enabled-disconnect (%d)",
826 path, rc));
827 }
828 } catch (Exception ex) {
829 Log.w(TAG, "Failed to mount media on UMS enabled-disconnect", ex);
830 }
831 }
832 }.start();
833 }
San Mehat4270e1e2010-01-29 05:32:19 -0800834 }
835
San Mehat6a965af22010-02-24 17:47:30 -0800836 private void sendUmsIntent(boolean c) {
837 mContext.sendBroadcast(
838 new Intent((c ? Intent.ACTION_UMS_CONNECTED : Intent.ACTION_UMS_DISCONNECTED)));
839 }
840
San Mehat207e5382010-02-04 20:46:54 -0800841 private void validatePermission(String perm) {
San Mehat4270e1e2010-01-29 05:32:19 -0800842 if (mContext.checkCallingOrSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) {
843 throw new SecurityException(String.format("Requires %s permission", perm));
844 }
845 }
846
847 /**
San Mehat207e5382010-02-04 20:46:54 -0800848 * Constructs a new MountService instance
849 *
850 * @param context Binder context for this service
851 */
852 public MountService(Context context) {
853 mContext = context;
854
San Mehat207e5382010-02-04 20:46:54 -0800855 // XXX: This will go away soon in favor of IMountServiceObserver
856 mPms = (PackageManagerService) ServiceManager.getService("package");
857
858 mContext.registerReceiver(mBroadcastReceiver,
859 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
860
Daniel Sandler5f27ef42010-03-16 15:42:02 -0400861 mHandlerThread = new HandlerThread("MountService");
862 mHandlerThread.start();
863 mHandler = new MountServiceHandler(mHandlerThread.getLooper());
864
Marco Nelissenc34ebce2010-02-18 13:39:41 -0800865 /*
866 * Vold does not run in the simulator, so pretend the connector thread
867 * ran and did its thing.
868 */
869 if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
870 mReady = true;
871 mUmsEnabling = true;
872 return;
873 }
874
San Mehat207e5382010-02-04 20:46:54 -0800875 mConnector = new NativeDaemonConnector(this, "vold", 10, "VoldConnector");
876 mReady = false;
877 Thread thread = new Thread(mConnector, NativeDaemonConnector.class.getName());
878 thread.start();
879 }
880
881 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800882 * Exposed API calls below here
883 */
884
885 public void registerListener(IMountServiceListener listener) {
886 synchronized (mListeners) {
887 MountServiceBinderListener bl = new MountServiceBinderListener(listener);
888 try {
889 listener.asBinder().linkToDeath(bl, 0);
890 mListeners.add(bl);
891 } catch (RemoteException rex) {
892 Log.e(TAG, "Failed to link to listener death");
893 }
894 }
895 }
896
897 public void unregisterListener(IMountServiceListener listener) {
898 synchronized (mListeners) {
899 for(MountServiceBinderListener bl : mListeners) {
900 if (bl.mListener == listener) {
901 mListeners.remove(mListeners.indexOf(bl));
902 return;
903 }
904 }
905 }
906 }
907
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800908 public void shutdown(final IMountShutdownObserver observer) {
San Mehat4270e1e2010-01-29 05:32:19 -0800909 validatePermission(android.Manifest.permission.SHUTDOWN);
910
911 Log.i(TAG, "Shutting down");
912
913 String path = Environment.getExternalStorageDirectory().getPath();
914 String state = getVolumeState(path);
San Mehat91c77612010-01-07 10:39:41 -0800915
916 if (state.equals(Environment.MEDIA_SHARED)) {
917 /*
918 * If the media is currently shared, unshare it.
919 * XXX: This is still dangerous!. We should not
920 * be rebooting at *all* if UMS is enabled, since
921 * the UMS host could have dirty FAT cache entries
922 * yet to flush.
923 */
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800924 setUsbMassStorageEnabled(false);
San Mehat91c77612010-01-07 10:39:41 -0800925 } else if (state.equals(Environment.MEDIA_CHECKING)) {
926 /*
927 * If the media is being checked, then we need to wait for
928 * it to complete before being able to proceed.
929 */
930 // XXX: @hackbod - Should we disable the ANR timer here?
931 int retries = 30;
932 while (state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) {
933 try {
934 Thread.sleep(1000);
935 } catch (InterruptedException iex) {
936 Log.e(TAG, "Interrupted while waiting for media", iex);
937 break;
938 }
939 state = Environment.getExternalStorageState();
940 }
941 if (retries == 0) {
942 Log.e(TAG, "Timed out waiting for media to check");
943 }
944 }
945
946 if (state.equals(Environment.MEDIA_MOUNTED)) {
Suchi Amalapurapu6ffce2e2010-03-08 14:48:40 -0800947 // Post a unmount message.
948 ShutdownCallBack ucb = new ShutdownCallBack(path, observer);
949 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
San Mehat4270e1e2010-01-29 05:32:19 -0800950 }
951 }
952
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800953 private boolean getUmsEnabling() {
954 synchronized (mListeners) {
955 return mUmsEnabling;
956 }
957 }
958
959 private void setUmsEnabling(boolean enable) {
960 synchronized (mListeners) {
961 mUmsEnabling = true;
962 }
963 }
964
San Mehatb1043402010-02-05 08:26:50 -0800965 public boolean isUsbMassStorageConnected() {
San Mehat207e5382010-02-04 20:46:54 -0800966 waitForReady();
San Mehat91c77612010-01-07 10:39:41 -0800967
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800968 if (getUmsEnabling()) {
San Mehatb1043402010-02-05 08:26:50 -0800969 return true;
San Mehat7fd0fee2009-12-17 07:12:23 -0800970 }
San Mehatb1043402010-02-05 08:26:50 -0800971 return doGetShareMethodAvailable("ums");
972 }
973
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800974 public void setUsbMassStorageEnabled(boolean enable) {
San Mehatb1043402010-02-05 08:26:50 -0800975 waitForReady();
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800976 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehatb1043402010-02-05 08:26:50 -0800977
Suchi Amalapurapu0eec21d2010-02-25 17:07:14 -0800978 // TODO: Add support for multiple share methods
979
980 /*
981 * If the volume is mounted and we're enabling then unmount it
982 */
983 String path = Environment.getExternalStorageDirectory().getPath();
984 String vs = getVolumeState(path);
985 String method = "ums";
986 if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
987 // Override for isUsbMassStorageEnabled()
988 setUmsEnabling(enable);
989 UmsEnableCallBack umscb = new UmsEnableCallBack(path, method, true);
990 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, umscb));
991 // Clear override
992 setUmsEnabling(false);
993 }
994 /*
995 * If we disabled UMS then mount the volume
996 */
997 if (!enable) {
998 doShareUnshareVolume(path, method, enable);
999 if (doMountVolume(path) != StorageResultCode.OperationSucceeded) {
1000 Log.e(TAG, "Failed to remount " + path +
1001 " after disabling share method " + method);
1002 /*
1003 * Even though the mount failed, the unshare didn't so don't indicate an error.
1004 * The mountVolume() call will have set the storage state and sent the necessary
1005 * broadcasts.
1006 */
1007 }
1008 }
San Mehatb1043402010-02-05 08:26:50 -08001009 }
1010
1011 public boolean isUsbMassStorageEnabled() {
1012 waitForReady();
1013 return doGetVolumeShared(Environment.getExternalStorageDirectory().getPath(), "ums");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001014 }
San Mehat4270e1e2010-01-29 05:32:19 -08001015
San Mehat7fd0fee2009-12-17 07:12:23 -08001016 /**
1017 * @return state of the volume at the specified mount point
1018 */
San Mehat4270e1e2010-01-29 05:32:19 -08001019 public String getVolumeState(String mountPoint) {
San Mehat7fd0fee2009-12-17 07:12:23 -08001020 /*
1021 * XXX: Until we have multiple volume discovery, just hardwire
1022 * this to /sdcard
1023 */
1024 if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) {
1025 Log.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
1026 throw new IllegalArgumentException();
1027 }
1028
1029 return mLegacyState;
1030 }
1031
San Mehat4270e1e2010-01-29 05:32:19 -08001032 public int mountVolume(String path) {
1033 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehat4270e1e2010-01-29 05:32:19 -08001034
San Mehat207e5382010-02-04 20:46:54 -08001035 waitForReady();
1036 return doMountVolume(path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001037 }
1038
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -08001039 public void unmountVolume(String path, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -08001040 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -08001041 waitForReady();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001042
Suchi Amalapurapuc42e29e2010-02-22 16:03:53 -08001043 UnmountCallBack ucb = new UnmountCallBack(path, force);
1044 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001045 }
1046
San Mehat4270e1e2010-01-29 05:32:19 -08001047 public int formatVolume(String path) {
1048 validatePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -08001049 waitForReady();
San Mehat5b77dab2010-01-26 13:28:50 -08001050
San Mehat207e5382010-02-04 20:46:54 -08001051 return doFormatVolume(path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001052 }
1053
San Mehatc1b4ce92010-02-16 17:13:03 -08001054 public int []getStorageUsers(String path) {
1055 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
1056 waitForReady();
1057 try {
1058 String[] r = mConnector.doListCommand(
1059 String.format("storage users %s", path),
1060 VoldResponseCode.StorageUsersListResult);
1061 // FMT: <pid> <process name>
1062 int[] data = new int[r.length];
1063 for (int i = 0; i < r.length; i++) {
1064 String []tok = r[i].split(" ");
1065 try {
1066 data[i] = Integer.parseInt(tok[0]);
1067 } catch (NumberFormatException nfe) {
1068 Log.e(TAG, String.format("Error parsing pid %s", tok[0]));
1069 return new int[0];
1070 }
1071 }
1072 return data;
1073 } catch (NativeDaemonConnectorException e) {
1074 Log.e(TAG, "Failed to retrieve storage users list", e);
1075 return new int[0];
1076 }
1077 }
1078
San Mehatb1043402010-02-05 08:26:50 -08001079 private void warnOnNotMounted() {
1080 if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
1081 Log.w(TAG, "getSecureContainerList() called when storage not mounted");
1082 }
1083 }
1084
San Mehat4270e1e2010-01-29 05:32:19 -08001085 public String[] getSecureContainerList() {
1086 validatePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001087 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001088 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001089
San Mehat4270e1e2010-01-29 05:32:19 -08001090 try {
1091 return mConnector.doListCommand("asec list", VoldResponseCode.AsecListResult);
1092 } catch (NativeDaemonConnectorException e) {
1093 return new String[0];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001094 }
1095 }
San Mehat36972292010-01-06 11:06:32 -08001096
San Mehat4270e1e2010-01-29 05:32:19 -08001097 public int createSecureContainer(String id, int sizeMb, String fstype,
1098 String key, int ownerUid) {
1099 validatePermission(android.Manifest.permission.ASEC_CREATE);
San Mehat207e5382010-02-04 20:46:54 -08001100 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001101 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001102
San Mehatb1043402010-02-05 08:26:50 -08001103 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001104 String cmd = String.format("asec create %s %d %s %s %d", id, sizeMb, fstype, key, ownerUid);
1105 try {
1106 mConnector.doCommand(cmd);
1107 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001108 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001109 }
San Mehata181b212010-02-11 06:50:20 -08001110
1111 if (rc == StorageResultCode.OperationSucceeded) {
1112 synchronized (mAsecMountSet) {
1113 mAsecMountSet.add(id);
1114 }
1115 }
San Mehat4270e1e2010-01-29 05:32:19 -08001116 return rc;
San Mehat36972292010-01-06 11:06:32 -08001117 }
1118
San Mehat4270e1e2010-01-29 05:32:19 -08001119 public int finalizeSecureContainer(String id) {
1120 validatePermission(android.Manifest.permission.ASEC_CREATE);
San Mehatb1043402010-02-05 08:26:50 -08001121 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001122
San Mehatb1043402010-02-05 08:26:50 -08001123 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001124 try {
1125 mConnector.doCommand(String.format("asec finalize %s", id));
San Mehata181b212010-02-11 06:50:20 -08001126 /*
1127 * Finalization does a remount, so no need
1128 * to update mAsecMountSet
1129 */
San Mehat4270e1e2010-01-29 05:32:19 -08001130 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001131 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001132 }
San Mehat4270e1e2010-01-29 05:32:19 -08001133 return rc;
San Mehat36972292010-01-06 11:06:32 -08001134 }
1135
San Mehatd9709982010-02-18 11:43:03 -08001136 public int destroySecureContainer(String id, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -08001137 validatePermission(android.Manifest.permission.ASEC_DESTROY);
San Mehat207e5382010-02-04 20:46:54 -08001138 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001139 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001140
San Mehatb1043402010-02-05 08:26:50 -08001141 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001142 try {
San Mehatd9709982010-02-18 11:43:03 -08001143 mConnector.doCommand(String.format("asec destroy %s%s", id, (force ? " force" : "")));
San Mehat4270e1e2010-01-29 05:32:19 -08001144 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08001145 int code = e.getCode();
1146 if (code == VoldResponseCode.OpFailedStorageBusy) {
1147 rc = StorageResultCode.OperationFailedStorageBusy;
1148 } else {
1149 rc = StorageResultCode.OperationFailedInternalError;
1150 }
San Mehat02735bc2010-01-26 15:18:08 -08001151 }
San Mehata181b212010-02-11 06:50:20 -08001152
1153 if (rc == StorageResultCode.OperationSucceeded) {
1154 synchronized (mAsecMountSet) {
1155 if (mAsecMountSet.contains(id)) {
1156 mAsecMountSet.remove(id);
1157 }
1158 }
1159 }
1160
San Mehat4270e1e2010-01-29 05:32:19 -08001161 return rc;
San Mehat36972292010-01-06 11:06:32 -08001162 }
1163
San Mehat4270e1e2010-01-29 05:32:19 -08001164 public int mountSecureContainer(String id, String key, int ownerUid) {
1165 validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08001166 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001167 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001168
San Mehata181b212010-02-11 06:50:20 -08001169 synchronized (mAsecMountSet) {
1170 if (mAsecMountSet.contains(id)) {
1171 return StorageResultCode.OperationFailedStorageMounted;
1172 }
1173 }
1174
San Mehatb1043402010-02-05 08:26:50 -08001175 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001176 String cmd = String.format("asec mount %s %s %d", id, key, ownerUid);
1177 try {
1178 mConnector.doCommand(cmd);
1179 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001180 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001181 }
San Mehat6cdd9c02010-02-09 14:45:20 -08001182
1183 if (rc == StorageResultCode.OperationSucceeded) {
1184 synchronized (mAsecMountSet) {
1185 mAsecMountSet.add(id);
1186 }
1187 }
San Mehat4270e1e2010-01-29 05:32:19 -08001188 return rc;
San Mehat36972292010-01-06 11:06:32 -08001189 }
1190
San Mehatd9709982010-02-18 11:43:03 -08001191 public int unmountSecureContainer(String id, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -08001192 validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -08001193 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001194 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001195
San Mehat6cdd9c02010-02-09 14:45:20 -08001196 synchronized (mAsecMountSet) {
1197 if (!mAsecMountSet.contains(id)) {
San Mehata181b212010-02-11 06:50:20 -08001198 return StorageResultCode.OperationFailedStorageNotMounted;
San Mehat6cdd9c02010-02-09 14:45:20 -08001199 }
1200 }
1201
San Mehatb1043402010-02-05 08:26:50 -08001202 int rc = StorageResultCode.OperationSucceeded;
San Mehatd9709982010-02-18 11:43:03 -08001203 String cmd = String.format("asec unmount %s%s", id, (force ? " force" : ""));
San Mehat4270e1e2010-01-29 05:32:19 -08001204 try {
1205 mConnector.doCommand(cmd);
1206 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -08001207 int code = e.getCode();
1208 if (code == VoldResponseCode.OpFailedStorageBusy) {
1209 rc = StorageResultCode.OperationFailedStorageBusy;
1210 } else {
1211 rc = StorageResultCode.OperationFailedInternalError;
1212 }
San Mehat02735bc2010-01-26 15:18:08 -08001213 }
San Mehat6cdd9c02010-02-09 14:45:20 -08001214
1215 if (rc == StorageResultCode.OperationSucceeded) {
1216 synchronized (mAsecMountSet) {
1217 mAsecMountSet.remove(id);
1218 }
1219 }
San Mehat4270e1e2010-01-29 05:32:19 -08001220 return rc;
San Mehat9dba7092010-01-18 06:47:41 -08001221 }
1222
San Mehat6cdd9c02010-02-09 14:45:20 -08001223 public boolean isSecureContainerMounted(String id) {
1224 validatePermission(android.Manifest.permission.ASEC_ACCESS);
1225 waitForReady();
1226 warnOnNotMounted();
1227
1228 synchronized (mAsecMountSet) {
1229 return mAsecMountSet.contains(id);
1230 }
1231 }
1232
San Mehat4270e1e2010-01-29 05:32:19 -08001233 public int renameSecureContainer(String oldId, String newId) {
1234 validatePermission(android.Manifest.permission.ASEC_RENAME);
San Mehat207e5382010-02-04 20:46:54 -08001235 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001236 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001237
San Mehata181b212010-02-11 06:50:20 -08001238 synchronized (mAsecMountSet) {
San Mehat85451ee2010-02-24 08:54:18 -08001239 /*
1240 * Because a mounted container has active internal state which cannot be
1241 * changed while active, we must ensure both ids are not currently mounted.
1242 */
1243 if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) {
San Mehata181b212010-02-11 06:50:20 -08001244 return StorageResultCode.OperationFailedStorageMounted;
1245 }
1246 }
1247
San Mehatb1043402010-02-05 08:26:50 -08001248 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001249 String cmd = String.format("asec rename %s %s", oldId, newId);
1250 try {
1251 mConnector.doCommand(cmd);
1252 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001253 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001254 }
San Mehata181b212010-02-11 06:50:20 -08001255
San Mehat4270e1e2010-01-29 05:32:19 -08001256 return rc;
San Mehat45f61042010-01-23 08:12:43 -08001257 }
1258
San Mehat4270e1e2010-01-29 05:32:19 -08001259 public String getSecureContainerPath(String id) {
1260 validatePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001261 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001262 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001263
San Mehat4270e1e2010-01-29 05:32:19 -08001264 ArrayList<String> rsp = mConnector.doCommand("asec path " + id);
San Mehat36972292010-01-06 11:06:32 -08001265
San Mehat22dd86e2010-01-12 12:21:18 -08001266 for (String line : rsp) {
1267 String []tok = line.split(" ");
1268 int code = Integer.parseInt(tok[0]);
1269 if (code == VoldResponseCode.AsecPathResult) {
1270 return tok[1];
1271 } else {
San Mehat4270e1e2010-01-29 05:32:19 -08001272 Log.e(TAG, String.format("Unexpected response code %d", code));
1273 return "";
San Mehat22dd86e2010-01-12 12:21:18 -08001274 }
1275 }
San Mehat4270e1e2010-01-29 05:32:19 -08001276
1277 Log.e(TAG, "Got an empty response");
1278 return "";
San Mehat22dd86e2010-01-12 12:21:18 -08001279 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001280}
1281