blob: 1e389ba70f0982d618a5367cff7e3a8eec579474 [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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080019import android.content.BroadcastReceiver;
20import android.content.Context;
21import android.content.Intent;
22import android.content.IntentFilter;
23import android.content.pm.PackageManager;
24import android.content.res.Resources;
25import android.net.Uri;
26import android.os.IMountService;
San Mehat4270e1e2010-01-29 05:32:19 -080027import android.os.IMountServiceListener;
28import android.os.MountServiceResultCode;
29import android.os.RemoteException;
30import android.os.IBinder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.os.Environment;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -080032import android.os.ServiceManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.os.SystemProperties;
34import android.os.UEventObserver;
San Mehat1f6301e2010-01-07 22:40:27 -080035import android.os.Handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.text.TextUtils;
37import android.util.Log;
San Mehat22dd86e2010-01-12 12:21:18 -080038import java.util.ArrayList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039
40import java.io.File;
41import java.io.FileReader;
42
43/**
44 * MountService implements an to the mount service daemon
45 * @hide
46 */
San Mehat22dd86e2010-01-12 12:21:18 -080047class MountService extends IMountService.Stub
48 implements INativeDaemonConnectorCallbacks {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049
50 private static final String TAG = "MountService";
51
San Mehat4270e1e2010-01-29 05:32:19 -080052 /*
53 * Internal vold volume state constants
54 */
San Mehat7fd0fee2009-12-17 07:12:23 -080055 class VolumeState {
56 public static final int Init = -1;
57 public static final int NoMedia = 0;
58 public static final int Idle = 1;
59 public static final int Pending = 2;
60 public static final int Checking = 3;
61 public static final int Mounted = 4;
62 public static final int Unmounting = 5;
63 public static final int Formatting = 6;
64 public static final int Shared = 7;
65 public static final int SharedMnt = 8;
66 }
67
San Mehat4270e1e2010-01-29 05:32:19 -080068 /*
69 * Internal vold response code constants
70 */
San Mehat22dd86e2010-01-12 12:21:18 -080071 class VoldResponseCode {
San Mehat4270e1e2010-01-29 05:32:19 -080072 /*
73 * 100 series - Requestion action was initiated; expect another reply
74 * before proceeding with a new command.
75 */
San Mehat22dd86e2010-01-12 12:21:18 -080076 public static final int VolumeListResult = 110;
77 public static final int AsecListResult = 111;
78
San Mehat4270e1e2010-01-29 05:32:19 -080079 /*
80 * 200 series - Requestion action has been successfully completed.
81 */
82 public static final int ShareStatusResult = 210;
San Mehat22dd86e2010-01-12 12:21:18 -080083 public static final int AsecPathResult = 211;
San Mehat4270e1e2010-01-29 05:32:19 -080084 public static final int ShareEnabledResult = 212;
San Mehat22dd86e2010-01-12 12:21:18 -080085
San Mehat4270e1e2010-01-29 05:32:19 -080086 /*
87 * 400 series - Command was accepted, but the requested action
88 * did not take place.
89 */
90 public static final int OpFailedNoMedia = 401;
91 public static final int OpFailedMediaBlank = 402;
92 public static final int OpFailedMediaCorrupt = 403;
93 public static final int OpFailedVolNotMounted = 404;
94 public static final int OpFailedVolBusy = 405;
95
96 /*
97 * 600 series - Unsolicited broadcasts.
98 */
San Mehat22dd86e2010-01-12 12:21:18 -080099 public static final int VolumeStateChange = 605;
San Mehat22dd86e2010-01-12 12:21:18 -0800100 public static final int ShareAvailabilityChange = 620;
101 public static final int VolumeDiskInserted = 630;
102 public static final int VolumeDiskRemoved = 631;
103 public static final int VolumeBadRemoval = 632;
104 }
105
San Mehat4270e1e2010-01-29 05:32:19 -0800106 private Context mContext;
107 private NativeDaemonConnector mConnector;
108 private String mLegacyState = Environment.MEDIA_REMOVED;
109 private PackageManagerService mPms;
110 private boolean mUmsEnabling;
111 private ArrayList<MountServiceBinderListener> mListeners;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -0800112
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800113 /**
114 * Constructs a new MountService instance
115 *
116 * @param context Binder context for this service
117 */
118 public MountService(Context context) {
119 mContext = context;
120
San Mehat4270e1e2010-01-29 05:32:19 -0800121 // XXX: This will go away soon in favor of IMountServiceObserver
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -0800122 mPms = (PackageManagerService) ServiceManager.getService("package");
San Mehat4270e1e2010-01-29 05:32:19 -0800123
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124 // Register a BOOT_COMPLETED handler so that we can start
San Mehat22dd86e2010-01-12 12:21:18 -0800125 // our NativeDaemonConnector. We defer the startup so that we don't
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126 // start processing events before we ought-to
127 mContext.registerReceiver(mBroadcastReceiver,
128 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
129
San Mehat22dd86e2010-01-12 12:21:18 -0800130 mConnector = new NativeDaemonConnector(this, "vold", 10, "VoldConnector");
San Mehat4270e1e2010-01-29 05:32:19 -0800131 mListeners = new ArrayList<MountServiceBinderListener>();
San Mehat1f6301e2010-01-07 22:40:27 -0800132 }
133
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134 BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
135 public void onReceive(Context context, Intent intent) {
San Mehat91c77612010-01-07 10:39:41 -0800136 String action = intent.getAction();
137
138 if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
San Mehat22dd86e2010-01-12 12:21:18 -0800139 /*
140 * Vold does not run in the simulator, so fake out a mounted
141 * event to trigger MediaScanner
142 */
143 if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
San Mehat4270e1e2010-01-29 05:32:19 -0800144 updatePublicVolumeState("/sdcard", Environment.MEDIA_MOUNTED);
San Mehat22dd86e2010-01-12 12:21:18 -0800145 return;
146 }
147
148 Thread thread = new Thread(
149 mConnector, NativeDaemonConnector.class.getName());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800150 thread.start();
151 }
152 }
153 };
154
San Mehat4270e1e2010-01-29 05:32:19 -0800155 private final class MountServiceBinderListener implements IBinder.DeathRecipient {
156 final IMountServiceListener mListener;
157
158 MountServiceBinderListener(IMountServiceListener listener) {
159 mListener = listener;
160
San Mehat91c77612010-01-07 10:39:41 -0800161 }
162
San Mehat4270e1e2010-01-29 05:32:19 -0800163 public void binderDied() {
164 Log.d(TAG, "An IMountServiceListener has died!");
165 synchronized(mListeners) {
166 mListeners.remove(this);
167 mListener.asBinder().unlinkToDeath(this, 0);
168 }
169 }
170 }
171
172 int doShareUnshareVolume(String path, String method, boolean enable) {
173 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
174
175 // TODO: Add support for multiple share methods
176 if (!method.equals("ums")) {
177 throw new IllegalArgumentException(String.format("Method %s not supported", method));
178 }
179
180 /*
181 * If the volume is mounted and we're enabling then unmount it
182 */
183 String vs = getVolumeState(path);
184 if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
185 mUmsEnabling = enable; // Supress unmounted events
186 unmountVolume(path);
187 mUmsEnabling = false; // Unsupress unmounted events
188 }
189
190 try {
191 mConnector.doCommand(String.format(
192 "volume %sshare %s %s", (enable ? "" : "un"), path, method));
193 } catch (NativeDaemonConnectorException e) {
194 Log.e(TAG, "Failed to share/unshare", e);
195 return MountServiceResultCode.OperationFailedInternalError;
196 }
197
198 /*
199 * If we disabled UMS then mount the volume
200 */
201 if (!enable) {
202 if (mountVolume(path) != MountServiceResultCode.OperationSucceeded) {
203 Log.e(TAG, String.format(
204 "Failed to remount %s after disabling share method %s", path, method));
205 /*
206 * Even though the mount failed, the unshare didn't so don't indicate an error.
207 * The mountVolume() call will have set the storage state and sent the necessary
208 * broadcasts.
209 */
210 }
211 }
212
213 return MountServiceResultCode.OperationSucceeded;
214 }
215
216 void updatePublicVolumeState(String path, String state) {
217 if (!path.equals(Environment.getExternalStorageDirectory().getPath())) {
218 Log.w(TAG, "Multiple volumes not currently supported");
219 return;
220 }
221 Log.i(TAG, "State for {" + path + "} = {" + state + "}");
222
223 String oldState = mLegacyState;
224 mLegacyState = state;
225
226 synchronized (mListeners) {
227 for (int i = mListeners.size() -1; i >= 0; i--) {
228 MountServiceBinderListener bl = mListeners.get(i);
229 try {
230 bl.mListener.onVolumeStateChanged("", path, oldState, state);
231 } catch (RemoteException rex) {
232 Log.e(TAG, "Listener dead");
233 mListeners.remove(i);
234 } catch (Exception ex) {
235 Log.e(TAG, "Listener failed", ex);
236 }
237 }
238 }
239 }
240
241 /**
242 *
243 * Callback from NativeDaemonConnector
244 */
245 public void onDaemonConnected() {
246 /*
247 * Since we'll be calling back into the NativeDaemonConnector,
248 * we need to do our work in a new thread.
249 */
250 new Thread() {
251 public void run() {
252 /**
253 * Determine media state and UMS detection status
254 */
255 String path = Environment.getExternalStorageDirectory().getPath();
256 String state = Environment.MEDIA_REMOVED;
257
258 try {
259 String[] vols = mConnector.doListCommand(
260 "volume list", VoldResponseCode.VolumeListResult);
261 for (String volstr : vols) {
262 String[] tok = volstr.split(" ");
263 // FMT: <label> <mountpoint> <state>
264 if (!tok[1].equals(path)) {
265 Log.w(TAG, String.format(
266 "Skipping unknown volume '%s'",tok[1]));
267 continue;
268 }
269 int st = Integer.parseInt(tok[2]);
270 if (st == VolumeState.NoMedia) {
271 state = Environment.MEDIA_REMOVED;
272 } else if (st == VolumeState.Idle) {
273 state = null;
274 int rc = mountVolume(path);
275 if (rc != MountServiceResultCode.OperationSucceeded) {
276 Log.e(TAG, String.format("Connection-mount failed (%d)", rc));
277 }
278 } else if (st == VolumeState.Mounted) {
279 state = Environment.MEDIA_MOUNTED;
280 Log.i(TAG, "Media already mounted on daemon connection");
281 } else if (st == VolumeState.Shared) {
282 state = Environment.MEDIA_SHARED;
283 Log.i(TAG, "Media shared on daemon connection");
284 } else {
285 throw new Exception(String.format("Unexpected state %d", st));
286 }
287 }
288 if (state != null) {
289 updatePublicVolumeState(path, state);
290 }
291 } catch (Exception e) {
292 Log.e(TAG, "Error processing initial volume state", e);
293 updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
294 }
295
296 try {
297 boolean avail = getShareMethodAvailable("ums");
298 notifyShareAvailabilityChange("ums", avail);
299 } catch (Exception ex) {
300 Log.w(TAG, "Failed to get share availability");
301 }
302 }
303 }.start();
304 }
305
306 /**
307 *
308 * Callback from NativeDaemonConnector
309 */
310 public boolean onEvent(int code, String raw, String[] cooked) {
311 Intent in = null;
312
313 // Log.d(TAG, "event {" + raw + "}");
314 if (code == VoldResponseCode.VolumeStateChange) {
315 /*
316 * One of the volumes we're managing has changed state.
317 * Format: "NNN Volume <label> <path> state changed
318 * from <old_#> (<old_str>) to <new_#> (<new_str>)"
319 */
320 notifyVolumeStateChange(
321 cooked[2], cooked[3], Integer.parseInt(cooked[7]),
322 Integer.parseInt(cooked[10]));
323 } else if (code == VoldResponseCode.ShareAvailabilityChange) {
324 // FMT: NNN Share method <method> now <available|unavailable>
325 boolean avail = false;
326 if (cooked[5].equals("available")) {
327 avail = true;
328 }
329 notifyShareAvailabilityChange(cooked[3], avail);
330 } else if ((code == VoldResponseCode.VolumeDiskInserted) ||
331 (code == VoldResponseCode.VolumeDiskRemoved) ||
332 (code == VoldResponseCode.VolumeBadRemoval)) {
333 // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
334 // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
335 // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
336 final String label = cooked[2];
337 final String path = cooked[3];
338 int major = -1;
339 int minor = -1;
340
341 try {
342 String devComp = cooked[6].substring(1, cooked[6].length() -1);
343 String[] devTok = devComp.split(":");
344 major = Integer.parseInt(devTok[0]);
345 minor = Integer.parseInt(devTok[1]);
346 } catch (Exception ex) {
347 Log.e(TAG, "Failed to parse major/minor", ex);
348 }
349
350 synchronized (mListeners) {
351 for (int i = mListeners.size() -1; i >= 0; i--) {
352 MountServiceBinderListener bl = mListeners.get(i);
353 try {
354 if (code == VoldResponseCode.VolumeDiskInserted) {
355 bl.mListener.onMediaInserted(label, path, major, minor);
356 } else if (code == VoldResponseCode.VolumeDiskRemoved) {
357 bl.mListener.onMediaRemoved(label, path, major, minor, true);
358 } else if (code == VoldResponseCode.VolumeBadRemoval) {
359 bl.mListener.onMediaRemoved(label, path, major, minor, false);
360 } else {
361 Log.e(TAG, String.format("Unknown code {%d}", code));
362 }
363 } catch (RemoteException rex) {
364 Log.e(TAG, "Listener dead");
365 mListeners.remove(i);
366 } catch (Exception ex) {
367 Log.e(TAG, "Listener failed", ex);
368 }
369 }
370 }
371
372 if (code == VoldResponseCode.VolumeDiskInserted) {
373 new Thread() {
374 public void run() {
375 try {
376 int rc;
377 if ((rc = mountVolume(path)) != MountServiceResultCode.OperationSucceeded) {
378 Log.w(TAG, String.format("Insertion mount failed (%d)", rc));
379 }
380 } catch (Exception ex) {
381 Log.w(TAG, "Failed to mount media on insertion", ex);
382 }
383 }
384 }.start();
385 } else if (code == VoldResponseCode.VolumeDiskRemoved) {
386 /*
387 * This event gets trumped if we're already in BAD_REMOVAL state
388 */
389 if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
390 return true;
391 }
392 /* Send the media unmounted event first */
393 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
394 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
395 mContext.sendBroadcast(in);
396
397 updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
398 in = new Intent(Intent.ACTION_MEDIA_REMOVED, Uri.parse("file://" + path));
399 } else if (code == VoldResponseCode.VolumeBadRemoval) {
400 /* Send the media unmounted event first */
401 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
402 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
403 mContext.sendBroadcast(in);
404
405 updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL);
406 in = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL, Uri.parse("file://" + path));
407 } else {
408 Log.e(TAG, String.format("Unknown code {%d}", code));
409 }
410 } else {
411 return false;
412 }
413
414 if (in != null) {
415 mContext.sendBroadcast(in);
416 }
417 return true;
418 }
419
420 void notifyVolumeStateChange(String label, String path, int oldState, int newState) {
421 String vs = getVolumeState(path);
422
423 Intent in = null;
424
425 if (newState == VolumeState.Init) {
426 } else if (newState == VolumeState.NoMedia) {
427 // NoMedia is handled via Disk Remove events
428 } else if (newState == VolumeState.Idle) {
429 /*
430 * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
431 * if we're in the process of enabling UMS
432 */
433 if (!vs.equals(
434 Environment.MEDIA_BAD_REMOVAL) && !vs.equals(
435 Environment.MEDIA_NOFS) && !vs.equals(
436 Environment.MEDIA_UNMOUNTABLE) && !mUmsEnabling) {
437 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
438 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
439 }
440 } else if (newState == VolumeState.Pending) {
441 } else if (newState == VolumeState.Checking) {
442 updatePublicVolumeState(path, Environment.MEDIA_CHECKING);
443 in = new Intent(Intent.ACTION_MEDIA_CHECKING, Uri.parse("file://" + path));
444 } else if (newState == VolumeState.Mounted) {
445 updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
446 // Update media status on PackageManagerService to mount packages on sdcard
447 mPms.updateExternalMediaStatus(true);
448 in = new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + path));
449 in.putExtra("read-only", false);
450 } else if (newState == VolumeState.Unmounting) {
451 mPms.updateExternalMediaStatus(false);
452 in = new Intent(Intent.ACTION_MEDIA_EJECT, Uri.parse("file://" + path));
453 } else if (newState == VolumeState.Formatting) {
454 } else if (newState == VolumeState.Shared) {
455 /* Send the media unmounted event first */
456 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
457 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
458 mContext.sendBroadcast(in);
459
460 updatePublicVolumeState(path, Environment.MEDIA_SHARED);
461 in = new Intent(Intent.ACTION_MEDIA_SHARED, Uri.parse("file://" + path));
462 } else if (newState == VolumeState.SharedMnt) {
463 Log.e(TAG, "Live shared mounts not supported yet!");
464 return;
465 } else {
466 Log.e(TAG, "Unhandled VolumeState {" + newState + "}");
467 }
468
469 if (in != null) {
470 mContext.sendBroadcast(in);
471 }
472 }
473
474 void notifyShareAvailabilityChange(String method, final boolean avail) {
475 if (!method.equals("ums")) {
476 Log.w(TAG, "Ignoring unsupported share method {" + method + "}");
477 return;
478 }
479
480 synchronized (mListeners) {
481 for (int i = mListeners.size() -1; i >= 0; i--) {
482 MountServiceBinderListener bl = mListeners.get(i);
483 try {
484 bl.mListener.onShareAvailabilityChanged(method, avail);
485 } catch (RemoteException rex) {
486 Log.e(TAG, "Listener dead");
487 mListeners.remove(i);
488 } catch (Exception ex) {
489 Log.e(TAG, "Listener failed", ex);
490 }
491 }
492 }
493
494 Intent intent;
495 if (avail) {
496 intent = new Intent(Intent.ACTION_UMS_CONNECTED);
497 } else {
498 intent = new Intent(Intent.ACTION_UMS_DISCONNECTED);
499 }
500 mContext.sendBroadcast(intent);
501 }
502
503 void validatePermission(String perm) {
504 if (mContext.checkCallingOrSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) {
505 throw new SecurityException(String.format("Requires %s permission", perm));
506 }
507 }
508
509 /**
510 * Exposed API calls below here
511 */
512
513 public void registerListener(IMountServiceListener listener) {
514 synchronized (mListeners) {
515 MountServiceBinderListener bl = new MountServiceBinderListener(listener);
516 try {
517 listener.asBinder().linkToDeath(bl, 0);
518 mListeners.add(bl);
519 } catch (RemoteException rex) {
520 Log.e(TAG, "Failed to link to listener death");
521 }
522 }
523 }
524
525 public void unregisterListener(IMountServiceListener listener) {
526 synchronized (mListeners) {
527 for(MountServiceBinderListener bl : mListeners) {
528 if (bl.mListener == listener) {
529 mListeners.remove(mListeners.indexOf(bl));
530 return;
531 }
532 }
533 }
534 }
535
536 public void shutdown() {
537 validatePermission(android.Manifest.permission.SHUTDOWN);
538
539 Log.i(TAG, "Shutting down");
540
541 String path = Environment.getExternalStorageDirectory().getPath();
542 String state = getVolumeState(path);
San Mehat91c77612010-01-07 10:39:41 -0800543
544 if (state.equals(Environment.MEDIA_SHARED)) {
545 /*
546 * If the media is currently shared, unshare it.
547 * XXX: This is still dangerous!. We should not
548 * be rebooting at *all* if UMS is enabled, since
549 * the UMS host could have dirty FAT cache entries
550 * yet to flush.
551 */
San Mehat4270e1e2010-01-29 05:32:19 -0800552 if (unshareVolume(path, "ums") != MountServiceResultCode.OperationSucceeded) {
553 Log.e(TAG, "UMS disable on shutdown failed");
San Mehat91c77612010-01-07 10:39:41 -0800554 }
555 } else if (state.equals(Environment.MEDIA_CHECKING)) {
556 /*
557 * If the media is being checked, then we need to wait for
558 * it to complete before being able to proceed.
559 */
560 // XXX: @hackbod - Should we disable the ANR timer here?
561 int retries = 30;
562 while (state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) {
563 try {
564 Thread.sleep(1000);
565 } catch (InterruptedException iex) {
566 Log.e(TAG, "Interrupted while waiting for media", iex);
567 break;
568 }
569 state = Environment.getExternalStorageState();
570 }
571 if (retries == 0) {
572 Log.e(TAG, "Timed out waiting for media to check");
573 }
574 }
575
576 if (state.equals(Environment.MEDIA_MOUNTED)) {
577 /*
578 * If the media is mounted, then gracefully unmount it.
579 */
San Mehat4270e1e2010-01-29 05:32:19 -0800580 if (unmountVolume(path) != MountServiceResultCode.OperationSucceeded) {
581 Log.e(TAG, "Failed to unmount media for shutdown");
582 }
583 }
584 }
585
586 public String[] getShareMethodList() {
587 String[] rdata = new String[1];
588 rdata[0] = "ums";
589 return rdata;
590 }
591
592 public boolean getShareMethodAvailable(String method) {
593 ArrayList<String> rsp = mConnector.doCommand("share status " + method);
594
595 for (String line : rsp) {
596 String []tok = line.split(" ");
597 int code;
San Mehat91c77612010-01-07 10:39:41 -0800598 try {
San Mehat4270e1e2010-01-29 05:32:19 -0800599 code = Integer.parseInt(tok[0]);
600 } catch (NumberFormatException nfe) {
601 Log.e(TAG, String.format("Error parsing code %s", tok[0]));
602 return false;
603 }
604 if (code == VoldResponseCode.ShareStatusResult) {
605 if (tok[2].equals("available"))
606 return true;
607 return false;
608 } else {
609 Log.e(TAG, String.format("Unexpected response code %d", code));
610 return false;
San Mehat91c77612010-01-07 10:39:41 -0800611 }
612 }
San Mehat4270e1e2010-01-29 05:32:19 -0800613 Log.e(TAG, "Got an empty response");
614 return false;
San Mehat91c77612010-01-07 10:39:41 -0800615 }
616
San Mehat4270e1e2010-01-29 05:32:19 -0800617 public int shareVolume(String path, String method) {
618 return doShareUnshareVolume(path, method, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800619 }
620
San Mehat4270e1e2010-01-29 05:32:19 -0800621 public int unshareVolume(String path, String method) {
622 return doShareUnshareVolume(path, method, false);
623 }
San Mehat7fd0fee2009-12-17 07:12:23 -0800624
San Mehat4270e1e2010-01-29 05:32:19 -0800625 public boolean getVolumeShared(String path, String method) {
626 String cmd = String.format("volume shared %s %s", path, method);
627 ArrayList<String> rsp = mConnector.doCommand(cmd);
628
629 for (String line : rsp) {
630 String []tok = line.split(" ");
631 int code;
632 try {
633 code = Integer.parseInt(tok[0]);
634 } catch (NumberFormatException nfe) {
635 Log.e(TAG, String.format("Error parsing code %s", tok[0]));
636 return false;
San Mehat7fd0fee2009-12-17 07:12:23 -0800637 }
San Mehat4270e1e2010-01-29 05:32:19 -0800638 if (code == VoldResponseCode.ShareEnabledResult) {
639 if (tok[2].equals("enabled"))
640 return true;
641 return false;
642 } else {
643 Log.e(TAG, String.format("Unexpected response code %d", code));
644 return false;
San Mehat7fd0fee2009-12-17 07:12:23 -0800645 }
San Mehat7fd0fee2009-12-17 07:12:23 -0800646 }
San Mehat4270e1e2010-01-29 05:32:19 -0800647 Log.e(TAG, "Got an empty response");
648 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800649 }
San Mehat4270e1e2010-01-29 05:32:19 -0800650
San Mehat7fd0fee2009-12-17 07:12:23 -0800651 /**
652 * @return state of the volume at the specified mount point
653 */
San Mehat4270e1e2010-01-29 05:32:19 -0800654 public String getVolumeState(String mountPoint) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800655 /*
656 * XXX: Until we have multiple volume discovery, just hardwire
657 * this to /sdcard
658 */
659 if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) {
660 Log.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
661 throw new IllegalArgumentException();
662 }
663
664 return mLegacyState;
665 }
666
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800667
668 /**
669 * Attempt to mount external media
670 */
San Mehat4270e1e2010-01-29 05:32:19 -0800671 public int mountVolume(String path) {
672 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehat4270e1e2010-01-29 05:32:19 -0800673 int rc = MountServiceResultCode.OperationSucceeded;
674
675 try {
676 mConnector.doCommand(String.format("volume mount %s", path));
677 } catch (NativeDaemonConnectorException e) {
678 /*
679 * Mount failed for some reason
680 */
681 Intent in = null;
682 int code = e.getCode();
683 if (code == VoldResponseCode.OpFailedNoMedia) {
684 /*
685 * Attempt to mount but no media inserted
686 */
687 rc = MountServiceResultCode.OperationFailedNoMedia;
688 } else if (code == VoldResponseCode.OpFailedMediaBlank) {
689 /*
690 * Media is blank or does not contain a supported filesystem
691 */
692 updatePublicVolumeState(path, Environment.MEDIA_NOFS);
693 in = new Intent(Intent.ACTION_MEDIA_NOFS, Uri.parse("file://" + path));
694 rc = MountServiceResultCode.OperationFailedMediaBlank;
695 } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
696 /*
697 * Volume consistency check failed
698 */
699 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE);
700 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, Uri.parse("file://" + path));
701 rc = MountServiceResultCode.OperationFailedMediaCorrupt;
702 } else {
703 rc = MountServiceResultCode.OperationFailedInternalError;
704 }
705
706 /*
707 * Send broadcast intent (if required for the failure)
708 */
709 if (in != null) {
710 mContext.sendBroadcast(in);
711 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800712 }
San Mehat4270e1e2010-01-29 05:32:19 -0800713
714 return rc;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800715 }
716
717 /**
718 * Attempt to unmount external media to prepare for eject
719 */
San Mehat4270e1e2010-01-29 05:32:19 -0800720 public int unmountVolume(String path) {
721 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800722
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800723 // Check if media has been mounted
724 String oldState = mLegacyState;
725 if (!oldState.equals(Environment.MEDIA_MOUNTED)) {
726 return VoldResponseCode.OpFailedVolNotMounted;
727 }
728 // Notify PackageManager of potential media removal and deal with
729 // return code later on. The caller of this api should be aware or have been
730 // notified that the applications installed on the media will be killed.
731 mPms.updateExternalMediaStatus(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800732 try {
San Mehat4270e1e2010-01-29 05:32:19 -0800733 mConnector.doCommand(String.format("volume unmount %s", path));
734 return MountServiceResultCode.OperationSucceeded;
735 } catch (NativeDaemonConnectorException e) {
Suchi Amalapurapub56ae202010-02-04 22:51:07 -0800736 // Don't worry about mismatch in PackageManager since the
737 // call back will handle the status changes any way.
San Mehat4270e1e2010-01-29 05:32:19 -0800738 int code = e.getCode();
739 if (code == VoldResponseCode.OpFailedVolNotMounted) {
740 return MountServiceResultCode.OperationFailedVolumeNotMounted;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800741 } else {
San Mehat4270e1e2010-01-29 05:32:19 -0800742 return MountServiceResultCode.OperationFailedInternalError;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800743 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800744 }
745 }
746
San Mehat22dd86e2010-01-12 12:21:18 -0800747 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800748 * Synchronously formats a volume
San Mehat22dd86e2010-01-12 12:21:18 -0800749 *
San Mehat4270e1e2010-01-29 05:32:19 -0800750 * @param path The volume path to format
751 * @return Error code from MountServiceResultCode
San Mehat22dd86e2010-01-12 12:21:18 -0800752 */
San Mehat4270e1e2010-01-29 05:32:19 -0800753 public int formatVolume(String path) {
754 validatePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
San Mehat5b77dab2010-01-26 13:28:50 -0800755
San Mehat4270e1e2010-01-29 05:32:19 -0800756 try {
757 String cmd = String.format("volume format %s", path);
758 mConnector.doCommand(cmd);
759 return MountServiceResultCode.OperationSucceeded;
760 } catch (NativeDaemonConnectorException e) {
761 int code = e.getCode();
762 if (code == VoldResponseCode.OpFailedNoMedia) {
763 return MountServiceResultCode.OperationFailedNoMedia;
764 } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
765 return MountServiceResultCode.OperationFailedMediaCorrupt;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800766 } else {
San Mehat4270e1e2010-01-29 05:32:19 -0800767 return MountServiceResultCode.OperationFailedInternalError;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800768 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800769 }
770 }
771
San Mehat4270e1e2010-01-29 05:32:19 -0800772 public String[] getSecureContainerList() {
773 validatePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehatf919cd022010-02-04 15:10:38 -0800774 if (Environment.getExternalStorageState() != Environment.MEDIA_MOUNTED) {
775 Log.w(TAG, "getSecureContainerList() called when storage not mounted");
776 }
777
San Mehat4270e1e2010-01-29 05:32:19 -0800778 try {
779 return mConnector.doListCommand("asec list", VoldResponseCode.AsecListResult);
780 } catch (NativeDaemonConnectorException e) {
781 return new String[0];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800782 }
783 }
San Mehat36972292010-01-06 11:06:32 -0800784
San Mehat4270e1e2010-01-29 05:32:19 -0800785 public int createSecureContainer(String id, int sizeMb, String fstype,
786 String key, int ownerUid) {
787 validatePermission(android.Manifest.permission.ASEC_CREATE);
San Mehatf919cd022010-02-04 15:10:38 -0800788 if (Environment.getExternalStorageState() != Environment.MEDIA_MOUNTED) {
789 Log.w(TAG, "createSecureContainer() called when storage not mounted");
790 }
San Mehat4270e1e2010-01-29 05:32:19 -0800791
792 int rc = MountServiceResultCode.OperationSucceeded;
793 String cmd = String.format("asec create %s %d %s %s %d", id, sizeMb, fstype, key, ownerUid);
794 try {
795 mConnector.doCommand(cmd);
796 } catch (NativeDaemonConnectorException e) {
797 rc = MountServiceResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -0800798 }
San Mehat4270e1e2010-01-29 05:32:19 -0800799 return rc;
San Mehat36972292010-01-06 11:06:32 -0800800 }
801
San Mehat4270e1e2010-01-29 05:32:19 -0800802 public int finalizeSecureContainer(String id) {
803 validatePermission(android.Manifest.permission.ASEC_CREATE);
San Mehatf919cd022010-02-04 15:10:38 -0800804 if (Environment.getExternalStorageState() != Environment.MEDIA_MOUNTED) {
805 Log.w(TAG, "finalizeSecureContainer() called when storage not mounted");
806 }
San Mehat4270e1e2010-01-29 05:32:19 -0800807
808 int rc = MountServiceResultCode.OperationSucceeded;
809 try {
810 mConnector.doCommand(String.format("asec finalize %s", id));
811 } catch (NativeDaemonConnectorException e) {
812 rc = MountServiceResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -0800813 }
San Mehat4270e1e2010-01-29 05:32:19 -0800814 return rc;
San Mehat36972292010-01-06 11:06:32 -0800815 }
816
San Mehat4270e1e2010-01-29 05:32:19 -0800817 public int destroySecureContainer(String id) {
818 validatePermission(android.Manifest.permission.ASEC_DESTROY);
San Mehat36972292010-01-06 11:06:32 -0800819
San Mehatf919cd022010-02-04 15:10:38 -0800820 if (Environment.getExternalStorageState() != Environment.MEDIA_MOUNTED) {
821 Log.w(TAG, "destroySecureContainer() called when storage not mounted");
822 }
823
San Mehat4270e1e2010-01-29 05:32:19 -0800824 int rc = MountServiceResultCode.OperationSucceeded;
825 try {
826 mConnector.doCommand(String.format("asec destroy %s", id));
827 } catch (NativeDaemonConnectorException e) {
828 rc = MountServiceResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -0800829 }
San Mehat4270e1e2010-01-29 05:32:19 -0800830 return rc;
San Mehat36972292010-01-06 11:06:32 -0800831 }
832
San Mehat4270e1e2010-01-29 05:32:19 -0800833 public int mountSecureContainer(String id, String key, int ownerUid) {
834 validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehatf919cd022010-02-04 15:10:38 -0800835 if (Environment.getExternalStorageState() != Environment.MEDIA_MOUNTED) {
836 Log.w(TAG, "mountSecureContainer() called when storage not mounted");
837 }
San Mehat4270e1e2010-01-29 05:32:19 -0800838
839 int rc = MountServiceResultCode.OperationSucceeded;
840 String cmd = String.format("asec mount %s %s %d", id, key, ownerUid);
841 try {
842 mConnector.doCommand(cmd);
843 } catch (NativeDaemonConnectorException e) {
844 rc = MountServiceResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -0800845 }
San Mehat4270e1e2010-01-29 05:32:19 -0800846 return rc;
San Mehat36972292010-01-06 11:06:32 -0800847 }
848
San Mehat4270e1e2010-01-29 05:32:19 -0800849 public int unmountSecureContainer(String id) {
850 validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehatf919cd022010-02-04 15:10:38 -0800851 if (Environment.getExternalStorageState() != Environment.MEDIA_MOUNTED) {
852 Log.w(TAG, "unmountSecureContainer() called when storage not mounted");
853 }
San Mehat4270e1e2010-01-29 05:32:19 -0800854
855 int rc = MountServiceResultCode.OperationSucceeded;
856 String cmd = String.format("asec unmount %s", id);
857 try {
858 mConnector.doCommand(cmd);
859 } catch (NativeDaemonConnectorException e) {
860 rc = MountServiceResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -0800861 }
San Mehat4270e1e2010-01-29 05:32:19 -0800862 return rc;
San Mehat9dba7092010-01-18 06:47:41 -0800863 }
864
San Mehat4270e1e2010-01-29 05:32:19 -0800865 public int renameSecureContainer(String oldId, String newId) {
866 validatePermission(android.Manifest.permission.ASEC_RENAME);
San Mehatf919cd022010-02-04 15:10:38 -0800867 if (Environment.getExternalStorageState() != Environment.MEDIA_MOUNTED) {
868 Log.w(TAG, "renameSecureContainer() called when storage not mounted");
869 }
San Mehat4270e1e2010-01-29 05:32:19 -0800870
871 int rc = MountServiceResultCode.OperationSucceeded;
872 String cmd = String.format("asec rename %s %s", oldId, newId);
873 try {
874 mConnector.doCommand(cmd);
875 } catch (NativeDaemonConnectorException e) {
876 rc = MountServiceResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -0800877 }
San Mehat4270e1e2010-01-29 05:32:19 -0800878 return rc;
San Mehat45f61042010-01-23 08:12:43 -0800879 }
880
San Mehat4270e1e2010-01-29 05:32:19 -0800881 public String getSecureContainerPath(String id) {
882 validatePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehatf919cd022010-02-04 15:10:38 -0800883 if (Environment.getExternalStorageState() != Environment.MEDIA_MOUNTED) {
884 Log.w(TAG, "getSecureContainerPath() called when storage not mounted");
885 }
886
San Mehat4270e1e2010-01-29 05:32:19 -0800887 ArrayList<String> rsp = mConnector.doCommand("asec path " + id);
San Mehat36972292010-01-06 11:06:32 -0800888
San Mehat22dd86e2010-01-12 12:21:18 -0800889 for (String line : rsp) {
890 String []tok = line.split(" ");
891 int code = Integer.parseInt(tok[0]);
892 if (code == VoldResponseCode.AsecPathResult) {
893 return tok[1];
894 } else {
San Mehat4270e1e2010-01-29 05:32:19 -0800895 Log.e(TAG, String.format("Unexpected response code %d", code));
896 return "";
San Mehat22dd86e2010-01-12 12:21:18 -0800897 }
898 }
San Mehat4270e1e2010-01-29 05:32:19 -0800899
900 Log.e(TAG, "Got an empty response");
901 return "";
San Mehat22dd86e2010-01-12 12:21:18 -0800902 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800903}
904