blob: 2dc12f6e5347902ae064441fea71b4d8018c6982 [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;
San Mehatb1043402010-02-05 08:26:50 -080026import android.os.storage.IMountService;
27import android.os.storage.IMountServiceListener;
28import android.os.storage.StorageResultCode;
San Mehat4270e1e2010-01-29 05:32:19 -080029import 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;
San Mehat207e5382010-02-04 20:46:54 -080033import android.os.SystemClock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.os.SystemProperties;
35import android.os.UEventObserver;
San Mehat1f6301e2010-01-07 22:40:27 -080036import android.os.Handler;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.text.TextUtils;
38import android.util.Log;
San Mehat22dd86e2010-01-12 12:21:18 -080039import java.util.ArrayList;
San Mehat6cdd9c02010-02-09 14:45:20 -080040import java.util.HashSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041
42import java.io.File;
43import java.io.FileReader;
44
45/**
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;
117 private ArrayList<MountServiceBinderListener> mListeners;
San Mehat207e5382010-02-04 20:46:54 -0800118 private boolean mBooted;
119 private boolean mReady;
Suchi Amalapurapufd3530f2010-01-18 00:15:59 -0800120
San Mehat6cdd9c02010-02-09 14:45:20 -0800121 /**
122 * Private hash of currently mounted secure containers.
123 */
124 private HashSet<String> mAsecMountSet = new HashSet<String>();
125
San Mehat207e5382010-02-04 20:46:54 -0800126 private void waitForReady() {
127 while (mReady == false) {
128 for (int retries = 5; retries > 0; retries--) {
129 if (mReady) {
130 return;
131 }
132 SystemClock.sleep(1000);
133 }
134 Log.w(TAG, "Waiting too long for mReady!");
135 }
San Mehat1f6301e2010-01-07 22:40:27 -0800136 }
137
San Mehat207e5382010-02-04 20:46:54 -0800138 private BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139 public void onReceive(Context context, Intent intent) {
San Mehat91c77612010-01-07 10:39:41 -0800140 String action = intent.getAction();
141
142 if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
San Mehat207e5382010-02-04 20:46:54 -0800143 mBooted = true;
San Mehat22dd86e2010-01-12 12:21:18 -0800144
Marco Nelissenc34ebce2010-02-18 13:39:41 -0800145 /*
146 * In the simulator, we need to broadcast a volume mounted event
147 * to make the media scanner run.
148 */
149 if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
150 notifyVolumeStateChange(null, "/sdcard", VolumeState.NoMedia, VolumeState.Mounted);
151 return;
152 }
San Mehatfafb0412010-02-18 19:40:04 -0800153 new Thread() {
154 public void run() {
155 try {
156 String path = Environment.getExternalStorageDirectory().getPath();
157 if (getVolumeState(
158 Environment.getExternalStorageDirectory().getPath()).equals(
159 Environment.MEDIA_UNMOUNTED)) {
160 int rc = doMountVolume(path);
161 if (rc != StorageResultCode.OperationSucceeded) {
162 Log.e(TAG, String.format("Boot-time mount failed (%d)", rc));
163 }
164 }
165 } catch (Exception ex) {
166 Log.e(TAG, "Boot-time mount exception", ex);
167 }
San Mehat207e5382010-02-04 20:46:54 -0800168 }
San Mehatfafb0412010-02-18 19:40:04 -0800169 }.start();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800170 }
171 }
172 };
173
San Mehat4270e1e2010-01-29 05:32:19 -0800174 private final class MountServiceBinderListener implements IBinder.DeathRecipient {
175 final IMountServiceListener mListener;
176
177 MountServiceBinderListener(IMountServiceListener listener) {
178 mListener = listener;
179
San Mehat91c77612010-01-07 10:39:41 -0800180 }
181
San Mehat4270e1e2010-01-29 05:32:19 -0800182 public void binderDied() {
San Mehatb1043402010-02-05 08:26:50 -0800183 if (LOCAL_LOGD) Log.d(TAG, "An IMountServiceListener has died!");
San Mehat4270e1e2010-01-29 05:32:19 -0800184 synchronized(mListeners) {
185 mListeners.remove(this);
186 mListener.asBinder().unlinkToDeath(this, 0);
187 }
188 }
189 }
190
San Mehat207e5382010-02-04 20:46:54 -0800191 private int doShareUnshareVolume(String path, String method, boolean enable) {
San Mehat4270e1e2010-01-29 05:32:19 -0800192 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
193
194 // TODO: Add support for multiple share methods
195 if (!method.equals("ums")) {
196 throw new IllegalArgumentException(String.format("Method %s not supported", method));
197 }
198
199 /*
200 * If the volume is mounted and we're enabling then unmount it
201 */
202 String vs = getVolumeState(path);
203 if (enable && vs.equals(Environment.MEDIA_MOUNTED)) {
San Mehatb1043402010-02-05 08:26:50 -0800204 mUmsEnabling = enable; // Override for isUsbMassStorageEnabled()
San Mehat3d488102010-02-22 11:39:16 -0800205 int rc = doUnmountVolume(path, true);
San Mehatb1043402010-02-05 08:26:50 -0800206 mUmsEnabling = false; // Clear override
San Mehat59443a62010-02-09 13:28:45 -0800207 if (rc != StorageResultCode.OperationSucceeded) {
208 Log.e(TAG, String.format("Failed to unmount before enabling UMS (%d)", rc));
209 return rc;
210 }
San Mehat4270e1e2010-01-29 05:32:19 -0800211 }
212
213 try {
214 mConnector.doCommand(String.format(
215 "volume %sshare %s %s", (enable ? "" : "un"), path, method));
216 } catch (NativeDaemonConnectorException e) {
217 Log.e(TAG, "Failed to share/unshare", e);
San Mehatb1043402010-02-05 08:26:50 -0800218 return StorageResultCode.OperationFailedInternalError;
San Mehat4270e1e2010-01-29 05:32:19 -0800219 }
220
221 /*
222 * If we disabled UMS then mount the volume
223 */
224 if (!enable) {
San Mehatb1043402010-02-05 08:26:50 -0800225 if (doMountVolume(path) != StorageResultCode.OperationSucceeded) {
San Mehat4270e1e2010-01-29 05:32:19 -0800226 Log.e(TAG, String.format(
227 "Failed to remount %s after disabling share method %s", path, method));
228 /*
229 * Even though the mount failed, the unshare didn't so don't indicate an error.
230 * The mountVolume() call will have set the storage state and sent the necessary
231 * broadcasts.
232 */
233 }
234 }
235
San Mehatb1043402010-02-05 08:26:50 -0800236 return StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -0800237 }
238
San Mehat207e5382010-02-04 20:46:54 -0800239 private void updatePublicVolumeState(String path, String state) {
San Mehat4270e1e2010-01-29 05:32:19 -0800240 if (!path.equals(Environment.getExternalStorageDirectory().getPath())) {
241 Log.w(TAG, "Multiple volumes not currently supported");
242 return;
243 }
San Mehatb1043402010-02-05 08:26:50 -0800244
245 if (mLegacyState.equals(state)) {
246 Log.w(TAG, String.format("Duplicate state transition (%s -> %s)", mLegacyState, state));
247 return;
248 }
San Mehat4270e1e2010-01-29 05:32:19 -0800249
250 String oldState = mLegacyState;
251 mLegacyState = state;
252
253 synchronized (mListeners) {
254 for (int i = mListeners.size() -1; i >= 0; i--) {
255 MountServiceBinderListener bl = mListeners.get(i);
256 try {
San Mehatb1043402010-02-05 08:26:50 -0800257 bl.mListener.onStorageStateChanged(path, oldState, state);
San Mehat4270e1e2010-01-29 05:32:19 -0800258 } catch (RemoteException rex) {
259 Log.e(TAG, "Listener dead");
260 mListeners.remove(i);
261 } catch (Exception ex) {
262 Log.e(TAG, "Listener failed", ex);
263 }
264 }
265 }
266 }
267
268 /**
269 *
270 * Callback from NativeDaemonConnector
271 */
272 public void onDaemonConnected() {
273 /*
274 * Since we'll be calling back into the NativeDaemonConnector,
275 * we need to do our work in a new thread.
276 */
277 new Thread() {
278 public void run() {
279 /**
280 * Determine media state and UMS detection status
281 */
282 String path = Environment.getExternalStorageDirectory().getPath();
283 String state = Environment.MEDIA_REMOVED;
284
285 try {
286 String[] vols = mConnector.doListCommand(
287 "volume list", VoldResponseCode.VolumeListResult);
288 for (String volstr : vols) {
289 String[] tok = volstr.split(" ");
290 // FMT: <label> <mountpoint> <state>
291 if (!tok[1].equals(path)) {
292 Log.w(TAG, String.format(
293 "Skipping unknown volume '%s'",tok[1]));
294 continue;
295 }
296 int st = Integer.parseInt(tok[2]);
297 if (st == VolumeState.NoMedia) {
298 state = Environment.MEDIA_REMOVED;
299 } else if (st == VolumeState.Idle) {
San Mehat207e5382010-02-04 20:46:54 -0800300 state = Environment.MEDIA_UNMOUNTED;
San Mehat4270e1e2010-01-29 05:32:19 -0800301 } else if (st == VolumeState.Mounted) {
302 state = Environment.MEDIA_MOUNTED;
303 Log.i(TAG, "Media already mounted on daemon connection");
304 } else if (st == VolumeState.Shared) {
305 state = Environment.MEDIA_SHARED;
306 Log.i(TAG, "Media shared on daemon connection");
307 } else {
308 throw new Exception(String.format("Unexpected state %d", st));
309 }
310 }
311 if (state != null) {
312 updatePublicVolumeState(path, state);
313 }
314 } catch (Exception e) {
315 Log.e(TAG, "Error processing initial volume state", e);
316 updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
317 }
318
319 try {
San Mehat207e5382010-02-04 20:46:54 -0800320 boolean avail = doGetShareMethodAvailable("ums");
San Mehat4270e1e2010-01-29 05:32:19 -0800321 notifyShareAvailabilityChange("ums", avail);
322 } catch (Exception ex) {
323 Log.w(TAG, "Failed to get share availability");
324 }
San Mehat207e5382010-02-04 20:46:54 -0800325 /*
326 * Now that we've done our initialization, release
327 * the hounds!
328 */
329 mReady = true;
San Mehat4270e1e2010-01-29 05:32:19 -0800330 }
331 }.start();
332 }
333
334 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800335 * Callback from NativeDaemonConnector
336 */
337 public boolean onEvent(int code, String raw, String[] cooked) {
338 Intent in = null;
339
San Mehat4270e1e2010-01-29 05:32:19 -0800340 if (code == VoldResponseCode.VolumeStateChange) {
341 /*
342 * One of the volumes we're managing has changed state.
343 * Format: "NNN Volume <label> <path> state changed
344 * from <old_#> (<old_str>) to <new_#> (<new_str>)"
345 */
346 notifyVolumeStateChange(
347 cooked[2], cooked[3], Integer.parseInt(cooked[7]),
348 Integer.parseInt(cooked[10]));
349 } else if (code == VoldResponseCode.ShareAvailabilityChange) {
350 // FMT: NNN Share method <method> now <available|unavailable>
351 boolean avail = false;
352 if (cooked[5].equals("available")) {
353 avail = true;
354 }
355 notifyShareAvailabilityChange(cooked[3], avail);
356 } else if ((code == VoldResponseCode.VolumeDiskInserted) ||
357 (code == VoldResponseCode.VolumeDiskRemoved) ||
358 (code == VoldResponseCode.VolumeBadRemoval)) {
359 // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>)
360 // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>)
361 // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>)
362 final String label = cooked[2];
363 final String path = cooked[3];
364 int major = -1;
365 int minor = -1;
366
367 try {
368 String devComp = cooked[6].substring(1, cooked[6].length() -1);
369 String[] devTok = devComp.split(":");
370 major = Integer.parseInt(devTok[0]);
371 minor = Integer.parseInt(devTok[1]);
372 } catch (Exception ex) {
373 Log.e(TAG, "Failed to parse major/minor", ex);
374 }
375
San Mehat4270e1e2010-01-29 05:32:19 -0800376 if (code == VoldResponseCode.VolumeDiskInserted) {
377 new Thread() {
378 public void run() {
379 try {
380 int rc;
San Mehatb1043402010-02-05 08:26:50 -0800381 if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) {
San Mehat4270e1e2010-01-29 05:32:19 -0800382 Log.w(TAG, String.format("Insertion mount failed (%d)", rc));
383 }
384 } catch (Exception ex) {
385 Log.w(TAG, "Failed to mount media on insertion", ex);
386 }
387 }
388 }.start();
389 } else if (code == VoldResponseCode.VolumeDiskRemoved) {
390 /*
391 * This event gets trumped if we're already in BAD_REMOVAL state
392 */
393 if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) {
394 return true;
395 }
396 /* Send the media unmounted event first */
397 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
398 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
399 mContext.sendBroadcast(in);
400
401 updatePublicVolumeState(path, Environment.MEDIA_REMOVED);
402 in = new Intent(Intent.ACTION_MEDIA_REMOVED, Uri.parse("file://" + path));
403 } else if (code == VoldResponseCode.VolumeBadRemoval) {
404 /* Send the media unmounted event first */
405 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
406 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
407 mContext.sendBroadcast(in);
408
409 updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL);
410 in = new Intent(Intent.ACTION_MEDIA_BAD_REMOVAL, Uri.parse("file://" + path));
411 } else {
412 Log.e(TAG, String.format("Unknown code {%d}", code));
413 }
414 } else {
415 return false;
416 }
417
418 if (in != null) {
419 mContext.sendBroadcast(in);
420 }
421 return true;
422 }
423
San Mehat207e5382010-02-04 20:46:54 -0800424 private void notifyVolumeStateChange(String label, String path, int oldState, int newState) {
San Mehat4270e1e2010-01-29 05:32:19 -0800425 String vs = getVolumeState(path);
426
427 Intent in = null;
428
429 if (newState == VolumeState.Init) {
430 } else if (newState == VolumeState.NoMedia) {
431 // NoMedia is handled via Disk Remove events
432 } else if (newState == VolumeState.Idle) {
433 /*
434 * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or
435 * if we're in the process of enabling UMS
436 */
437 if (!vs.equals(
438 Environment.MEDIA_BAD_REMOVAL) && !vs.equals(
439 Environment.MEDIA_NOFS) && !vs.equals(
440 Environment.MEDIA_UNMOUNTABLE) && !mUmsEnabling) {
441 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
442 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
443 }
444 } else if (newState == VolumeState.Pending) {
445 } else if (newState == VolumeState.Checking) {
446 updatePublicVolumeState(path, Environment.MEDIA_CHECKING);
447 in = new Intent(Intent.ACTION_MEDIA_CHECKING, Uri.parse("file://" + path));
448 } else if (newState == VolumeState.Mounted) {
449 updatePublicVolumeState(path, Environment.MEDIA_MOUNTED);
450 // Update media status on PackageManagerService to mount packages on sdcard
451 mPms.updateExternalMediaStatus(true);
452 in = new Intent(Intent.ACTION_MEDIA_MOUNTED, Uri.parse("file://" + path));
453 in.putExtra("read-only", false);
454 } else if (newState == VolumeState.Unmounting) {
455 mPms.updateExternalMediaStatus(false);
456 in = new Intent(Intent.ACTION_MEDIA_EJECT, Uri.parse("file://" + path));
457 } else if (newState == VolumeState.Formatting) {
458 } else if (newState == VolumeState.Shared) {
459 /* Send the media unmounted event first */
460 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED);
461 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTED, Uri.parse("file://" + path));
462 mContext.sendBroadcast(in);
463
464 updatePublicVolumeState(path, Environment.MEDIA_SHARED);
465 in = new Intent(Intent.ACTION_MEDIA_SHARED, Uri.parse("file://" + path));
466 } else if (newState == VolumeState.SharedMnt) {
467 Log.e(TAG, "Live shared mounts not supported yet!");
468 return;
469 } else {
470 Log.e(TAG, "Unhandled VolumeState {" + newState + "}");
471 }
472
473 if (in != null) {
474 mContext.sendBroadcast(in);
475 }
476 }
477
San Mehat207e5382010-02-04 20:46:54 -0800478 private boolean doGetShareMethodAvailable(String method) {
479 ArrayList<String> rsp = mConnector.doCommand("share status " + method);
480
481 for (String line : rsp) {
482 String []tok = line.split(" ");
483 int code;
484 try {
485 code = Integer.parseInt(tok[0]);
486 } catch (NumberFormatException nfe) {
487 Log.e(TAG, String.format("Error parsing code %s", tok[0]));
488 return false;
489 }
490 if (code == VoldResponseCode.ShareStatusResult) {
491 if (tok[2].equals("available"))
492 return true;
493 return false;
494 } else {
495 Log.e(TAG, String.format("Unexpected response code %d", code));
496 return false;
497 }
498 }
499 Log.e(TAG, "Got an empty response");
500 return false;
501 }
502
503 private int doMountVolume(String path) {
San Mehatb1043402010-02-05 08:26:50 -0800504 int rc = StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -0800505
506 try {
507 mConnector.doCommand(String.format("volume mount %s", path));
508 } catch (NativeDaemonConnectorException e) {
509 /*
510 * Mount failed for some reason
511 */
512 Intent in = null;
513 int code = e.getCode();
514 if (code == VoldResponseCode.OpFailedNoMedia) {
515 /*
516 * Attempt to mount but no media inserted
517 */
San Mehatb1043402010-02-05 08:26:50 -0800518 rc = StorageResultCode.OperationFailedNoMedia;
San Mehat207e5382010-02-04 20:46:54 -0800519 } else if (code == VoldResponseCode.OpFailedMediaBlank) {
520 /*
521 * Media is blank or does not contain a supported filesystem
522 */
523 updatePublicVolumeState(path, Environment.MEDIA_NOFS);
524 in = new Intent(Intent.ACTION_MEDIA_NOFS, Uri.parse("file://" + path));
San Mehatb1043402010-02-05 08:26:50 -0800525 rc = StorageResultCode.OperationFailedMediaBlank;
San Mehat207e5382010-02-04 20:46:54 -0800526 } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
527 /*
528 * Volume consistency check failed
529 */
530 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE);
531 in = new Intent(Intent.ACTION_MEDIA_UNMOUNTABLE, Uri.parse("file://" + path));
San Mehatb1043402010-02-05 08:26:50 -0800532 rc = StorageResultCode.OperationFailedMediaCorrupt;
San Mehat207e5382010-02-04 20:46:54 -0800533 } else {
San Mehatb1043402010-02-05 08:26:50 -0800534 rc = StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -0800535 }
536
537 /*
538 * Send broadcast intent (if required for the failure)
539 */
540 if (in != null) {
541 mContext.sendBroadcast(in);
542 }
543 }
544
545 return rc;
546 }
547
San Mehatd9709982010-02-18 11:43:03 -0800548 private int doUnmountVolume(String path, boolean force) {
San Mehat59443a62010-02-09 13:28:45 -0800549 if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) {
San Mehat207e5382010-02-04 20:46:54 -0800550 return VoldResponseCode.OpFailedVolNotMounted;
551 }
552
553 // Notify PackageManager of potential media removal and deal with
554 // return code later on. The caller of this api should be aware or have been
555 // notified that the applications installed on the media will be killed.
556 mPms.updateExternalMediaStatus(false);
557 try {
San Mehatd9709982010-02-18 11:43:03 -0800558 mConnector.doCommand(String.format(
559 "volume unmount %s%s", path, (force ? " force" : "")));
San Mehatb1043402010-02-05 08:26:50 -0800560 return StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -0800561 } catch (NativeDaemonConnectorException e) {
562 // Don't worry about mismatch in PackageManager since the
563 // call back will handle the status changes any way.
564 int code = e.getCode();
565 if (code == VoldResponseCode.OpFailedVolNotMounted) {
San Mehata181b212010-02-11 06:50:20 -0800566 return StorageResultCode.OperationFailedStorageNotMounted;
San Mehatd9709982010-02-18 11:43:03 -0800567 } else if (code == VoldResponseCode.OpFailedStorageBusy) {
568 return StorageResultCode.OperationFailedStorageBusy;
San Mehat207e5382010-02-04 20:46:54 -0800569 } else {
San Mehatb1043402010-02-05 08:26:50 -0800570 return StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -0800571 }
572 }
573 }
574
575 private int doFormatVolume(String path) {
576 try {
577 String cmd = String.format("volume format %s", path);
578 mConnector.doCommand(cmd);
San Mehatb1043402010-02-05 08:26:50 -0800579 return StorageResultCode.OperationSucceeded;
San Mehat207e5382010-02-04 20:46:54 -0800580 } catch (NativeDaemonConnectorException e) {
581 int code = e.getCode();
582 if (code == VoldResponseCode.OpFailedNoMedia) {
San Mehatb1043402010-02-05 08:26:50 -0800583 return StorageResultCode.OperationFailedNoMedia;
San Mehat207e5382010-02-04 20:46:54 -0800584 } else if (code == VoldResponseCode.OpFailedMediaCorrupt) {
San Mehatb1043402010-02-05 08:26:50 -0800585 return StorageResultCode.OperationFailedMediaCorrupt;
San Mehat207e5382010-02-04 20:46:54 -0800586 } else {
San Mehatb1043402010-02-05 08:26:50 -0800587 return StorageResultCode.OperationFailedInternalError;
San Mehat207e5382010-02-04 20:46:54 -0800588 }
589 }
590 }
591
San Mehatb1043402010-02-05 08:26:50 -0800592 private boolean doGetVolumeShared(String path, String method) {
593 String cmd = String.format("volume shared %s %s", path, method);
594 ArrayList<String> rsp = mConnector.doCommand(cmd);
595
596 for (String line : rsp) {
597 String []tok = line.split(" ");
598 int code;
599 try {
600 code = Integer.parseInt(tok[0]);
601 } catch (NumberFormatException nfe) {
602 Log.e(TAG, String.format("Error parsing code %s", tok[0]));
603 return false;
604 }
605 if (code == VoldResponseCode.ShareEnabledResult) {
606 if (tok[2].equals("enabled"))
607 return true;
608 return false;
609 } else {
610 Log.e(TAG, String.format("Unexpected response code %d", code));
611 return false;
612 }
613 }
614 Log.e(TAG, "Got an empty response");
615 return false;
616 }
617
San Mehat207e5382010-02-04 20:46:54 -0800618 private void notifyShareAvailabilityChange(String method, final boolean avail) {
San Mehat4270e1e2010-01-29 05:32:19 -0800619 if (!method.equals("ums")) {
620 Log.w(TAG, "Ignoring unsupported share method {" + method + "}");
621 return;
622 }
623
624 synchronized (mListeners) {
625 for (int i = mListeners.size() -1; i >= 0; i--) {
626 MountServiceBinderListener bl = mListeners.get(i);
627 try {
San Mehatb1043402010-02-05 08:26:50 -0800628 bl.mListener.onUsbMassStorageConnectionChanged(avail);
San Mehat4270e1e2010-01-29 05:32:19 -0800629 } catch (RemoteException rex) {
630 Log.e(TAG, "Listener dead");
631 mListeners.remove(i);
632 } catch (Exception ex) {
633 Log.e(TAG, "Listener failed", ex);
634 }
635 }
636 }
637
San Mehat207e5382010-02-04 20:46:54 -0800638 if (mBooted == true) {
639 Intent intent;
640 if (avail) {
641 intent = new Intent(Intent.ACTION_UMS_CONNECTED);
642 } else {
643 intent = new Intent(Intent.ACTION_UMS_DISCONNECTED);
644 }
645 mContext.sendBroadcast(intent);
San Mehat4270e1e2010-01-29 05:32:19 -0800646 }
San Mehat4270e1e2010-01-29 05:32:19 -0800647 }
648
San Mehat207e5382010-02-04 20:46:54 -0800649 private void validatePermission(String perm) {
San Mehat4270e1e2010-01-29 05:32:19 -0800650 if (mContext.checkCallingOrSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) {
651 throw new SecurityException(String.format("Requires %s permission", perm));
652 }
653 }
654
655 /**
San Mehat207e5382010-02-04 20:46:54 -0800656 * Constructs a new MountService instance
657 *
658 * @param context Binder context for this service
659 */
660 public MountService(Context context) {
661 mContext = context;
662
San Mehat207e5382010-02-04 20:46:54 -0800663 // XXX: This will go away soon in favor of IMountServiceObserver
664 mPms = (PackageManagerService) ServiceManager.getService("package");
665
666 mContext.registerReceiver(mBroadcastReceiver,
667 new IntentFilter(Intent.ACTION_BOOT_COMPLETED), null, null);
668
669 mListeners = new ArrayList<MountServiceBinderListener>();
670
Marco Nelissenc34ebce2010-02-18 13:39:41 -0800671 /*
672 * Vold does not run in the simulator, so pretend the connector thread
673 * ran and did its thing.
674 */
675 if ("simulator".equals(SystemProperties.get("ro.product.device"))) {
676 mReady = true;
677 mUmsEnabling = true;
678 return;
679 }
680
San Mehat207e5382010-02-04 20:46:54 -0800681 mConnector = new NativeDaemonConnector(this, "vold", 10, "VoldConnector");
682 mReady = false;
683 Thread thread = new Thread(mConnector, NativeDaemonConnector.class.getName());
684 thread.start();
685 }
686
687 /**
San Mehat4270e1e2010-01-29 05:32:19 -0800688 * Exposed API calls below here
689 */
690
691 public void registerListener(IMountServiceListener listener) {
692 synchronized (mListeners) {
693 MountServiceBinderListener bl = new MountServiceBinderListener(listener);
694 try {
695 listener.asBinder().linkToDeath(bl, 0);
696 mListeners.add(bl);
697 } catch (RemoteException rex) {
698 Log.e(TAG, "Failed to link to listener death");
699 }
700 }
701 }
702
703 public void unregisterListener(IMountServiceListener listener) {
704 synchronized (mListeners) {
705 for(MountServiceBinderListener bl : mListeners) {
706 if (bl.mListener == listener) {
707 mListeners.remove(mListeners.indexOf(bl));
708 return;
709 }
710 }
711 }
712 }
713
714 public void shutdown() {
715 validatePermission(android.Manifest.permission.SHUTDOWN);
716
717 Log.i(TAG, "Shutting down");
718
719 String path = Environment.getExternalStorageDirectory().getPath();
720 String state = getVolumeState(path);
San Mehat91c77612010-01-07 10:39:41 -0800721
722 if (state.equals(Environment.MEDIA_SHARED)) {
723 /*
724 * If the media is currently shared, unshare it.
725 * XXX: This is still dangerous!. We should not
726 * be rebooting at *all* if UMS is enabled, since
727 * the UMS host could have dirty FAT cache entries
728 * yet to flush.
729 */
San Mehatb1043402010-02-05 08:26:50 -0800730 if (setUsbMassStorageEnabled(false) != StorageResultCode.OperationSucceeded) {
San Mehat4270e1e2010-01-29 05:32:19 -0800731 Log.e(TAG, "UMS disable on shutdown failed");
San Mehat91c77612010-01-07 10:39:41 -0800732 }
733 } else if (state.equals(Environment.MEDIA_CHECKING)) {
734 /*
735 * If the media is being checked, then we need to wait for
736 * it to complete before being able to proceed.
737 */
738 // XXX: @hackbod - Should we disable the ANR timer here?
739 int retries = 30;
740 while (state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) {
741 try {
742 Thread.sleep(1000);
743 } catch (InterruptedException iex) {
744 Log.e(TAG, "Interrupted while waiting for media", iex);
745 break;
746 }
747 state = Environment.getExternalStorageState();
748 }
749 if (retries == 0) {
750 Log.e(TAG, "Timed out waiting for media to check");
751 }
752 }
753
754 if (state.equals(Environment.MEDIA_MOUNTED)) {
755 /*
756 * If the media is mounted, then gracefully unmount it.
757 */
San Mehatd9709982010-02-18 11:43:03 -0800758 if (doUnmountVolume(path, true) != StorageResultCode.OperationSucceeded) {
San Mehat4270e1e2010-01-29 05:32:19 -0800759 Log.e(TAG, "Failed to unmount media for shutdown");
760 }
761 }
762 }
763
San Mehatb1043402010-02-05 08:26:50 -0800764 public boolean isUsbMassStorageConnected() {
San Mehat207e5382010-02-04 20:46:54 -0800765 waitForReady();
San Mehat91c77612010-01-07 10:39:41 -0800766
San Mehatb1043402010-02-05 08:26:50 -0800767 if (mUmsEnabling) {
768 return true;
San Mehat7fd0fee2009-12-17 07:12:23 -0800769 }
San Mehatb1043402010-02-05 08:26:50 -0800770 return doGetShareMethodAvailable("ums");
771 }
772
773 public int setUsbMassStorageEnabled(boolean enable) {
774 waitForReady();
775
776 return doShareUnshareVolume(Environment.getExternalStorageDirectory().getPath(), "ums", enable);
777 }
778
779 public boolean isUsbMassStorageEnabled() {
780 waitForReady();
781 return doGetVolumeShared(Environment.getExternalStorageDirectory().getPath(), "ums");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800782 }
San Mehat4270e1e2010-01-29 05:32:19 -0800783
San Mehat7fd0fee2009-12-17 07:12:23 -0800784 /**
785 * @return state of the volume at the specified mount point
786 */
San Mehat4270e1e2010-01-29 05:32:19 -0800787 public String getVolumeState(String mountPoint) {
San Mehat7fd0fee2009-12-17 07:12:23 -0800788 /*
789 * XXX: Until we have multiple volume discovery, just hardwire
790 * this to /sdcard
791 */
792 if (!mountPoint.equals(Environment.getExternalStorageDirectory().getPath())) {
793 Log.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume");
794 throw new IllegalArgumentException();
795 }
796
797 return mLegacyState;
798 }
799
San Mehat4270e1e2010-01-29 05:32:19 -0800800 public int mountVolume(String path) {
801 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehat4270e1e2010-01-29 05:32:19 -0800802
San Mehat207e5382010-02-04 20:46:54 -0800803 waitForReady();
804 return doMountVolume(path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800805 }
806
San Mehatd9709982010-02-18 11:43:03 -0800807 public int unmountVolume(String path, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -0800808 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -0800809 waitForReady();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800810
San Mehatd9709982010-02-18 11:43:03 -0800811 return doUnmountVolume(path, force);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800812 }
813
San Mehat4270e1e2010-01-29 05:32:19 -0800814 public int formatVolume(String path) {
815 validatePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS);
San Mehat207e5382010-02-04 20:46:54 -0800816 waitForReady();
San Mehat5b77dab2010-01-26 13:28:50 -0800817
San Mehat207e5382010-02-04 20:46:54 -0800818 return doFormatVolume(path);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800819 }
820
San Mehatc1b4ce92010-02-16 17:13:03 -0800821 public int []getStorageUsers(String path) {
822 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS);
823 waitForReady();
824 try {
825 String[] r = mConnector.doListCommand(
826 String.format("storage users %s", path),
827 VoldResponseCode.StorageUsersListResult);
828 // FMT: <pid> <process name>
829 int[] data = new int[r.length];
830 for (int i = 0; i < r.length; i++) {
831 String []tok = r[i].split(" ");
832 try {
833 data[i] = Integer.parseInt(tok[0]);
834 } catch (NumberFormatException nfe) {
835 Log.e(TAG, String.format("Error parsing pid %s", tok[0]));
836 return new int[0];
837 }
838 }
839 return data;
840 } catch (NativeDaemonConnectorException e) {
841 Log.e(TAG, "Failed to retrieve storage users list", e);
842 return new int[0];
843 }
844 }
845
San Mehatb1043402010-02-05 08:26:50 -0800846 private void warnOnNotMounted() {
847 if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
848 Log.w(TAG, "getSecureContainerList() called when storage not mounted");
849 }
850 }
851
San Mehat4270e1e2010-01-29 05:32:19 -0800852 public String[] getSecureContainerList() {
853 validatePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -0800854 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -0800855 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -0800856
San Mehat4270e1e2010-01-29 05:32:19 -0800857 try {
858 return mConnector.doListCommand("asec list", VoldResponseCode.AsecListResult);
859 } catch (NativeDaemonConnectorException e) {
860 return new String[0];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800861 }
862 }
San Mehat36972292010-01-06 11:06:32 -0800863
San Mehat4270e1e2010-01-29 05:32:19 -0800864 public int createSecureContainer(String id, int sizeMb, String fstype,
865 String key, int ownerUid) {
866 validatePermission(android.Manifest.permission.ASEC_CREATE);
San Mehat207e5382010-02-04 20:46:54 -0800867 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -0800868 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -0800869
San Mehatb1043402010-02-05 08:26:50 -0800870 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -0800871 String cmd = String.format("asec create %s %d %s %s %d", id, sizeMb, fstype, key, ownerUid);
872 try {
873 mConnector.doCommand(cmd);
874 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -0800875 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -0800876 }
San Mehata181b212010-02-11 06:50:20 -0800877
878 if (rc == StorageResultCode.OperationSucceeded) {
879 synchronized (mAsecMountSet) {
880 mAsecMountSet.add(id);
881 }
882 }
San Mehat4270e1e2010-01-29 05:32:19 -0800883 return rc;
San Mehat36972292010-01-06 11:06:32 -0800884 }
885
San Mehat4270e1e2010-01-29 05:32:19 -0800886 public int finalizeSecureContainer(String id) {
887 validatePermission(android.Manifest.permission.ASEC_CREATE);
San Mehatb1043402010-02-05 08:26:50 -0800888 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -0800889
San Mehatb1043402010-02-05 08:26:50 -0800890 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -0800891 try {
892 mConnector.doCommand(String.format("asec finalize %s", id));
San Mehata181b212010-02-11 06:50:20 -0800893 /*
894 * Finalization does a remount, so no need
895 * to update mAsecMountSet
896 */
San Mehat4270e1e2010-01-29 05:32:19 -0800897 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -0800898 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -0800899 }
San Mehat4270e1e2010-01-29 05:32:19 -0800900 return rc;
San Mehat36972292010-01-06 11:06:32 -0800901 }
902
San Mehatd9709982010-02-18 11:43:03 -0800903 public int destroySecureContainer(String id, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -0800904 validatePermission(android.Manifest.permission.ASEC_DESTROY);
San Mehat207e5382010-02-04 20:46:54 -0800905 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -0800906 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -0800907
San Mehatb1043402010-02-05 08:26:50 -0800908 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -0800909 try {
San Mehatd9709982010-02-18 11:43:03 -0800910 mConnector.doCommand(String.format("asec destroy %s%s", id, (force ? " force" : "")));
San Mehat4270e1e2010-01-29 05:32:19 -0800911 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -0800912 int code = e.getCode();
913 if (code == VoldResponseCode.OpFailedStorageBusy) {
914 rc = StorageResultCode.OperationFailedStorageBusy;
915 } else {
916 rc = StorageResultCode.OperationFailedInternalError;
917 }
San Mehat02735bc2010-01-26 15:18:08 -0800918 }
San Mehata181b212010-02-11 06:50:20 -0800919
920 if (rc == StorageResultCode.OperationSucceeded) {
921 synchronized (mAsecMountSet) {
922 if (mAsecMountSet.contains(id)) {
923 mAsecMountSet.remove(id);
924 }
925 }
926 }
927
San Mehat4270e1e2010-01-29 05:32:19 -0800928 return rc;
San Mehat36972292010-01-06 11:06:32 -0800929 }
930
San Mehat4270e1e2010-01-29 05:32:19 -0800931 public int mountSecureContainer(String id, String key, int ownerUid) {
932 validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -0800933 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -0800934 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -0800935
San Mehata181b212010-02-11 06:50:20 -0800936 synchronized (mAsecMountSet) {
937 if (mAsecMountSet.contains(id)) {
938 return StorageResultCode.OperationFailedStorageMounted;
939 }
940 }
941
San Mehatb1043402010-02-05 08:26:50 -0800942 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -0800943 String cmd = String.format("asec mount %s %s %d", id, key, ownerUid);
944 try {
945 mConnector.doCommand(cmd);
946 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -0800947 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -0800948 }
San Mehat6cdd9c02010-02-09 14:45:20 -0800949
950 if (rc == StorageResultCode.OperationSucceeded) {
951 synchronized (mAsecMountSet) {
952 mAsecMountSet.add(id);
953 }
954 }
San Mehat4270e1e2010-01-29 05:32:19 -0800955 return rc;
San Mehat36972292010-01-06 11:06:32 -0800956 }
957
San Mehatd9709982010-02-18 11:43:03 -0800958 public int unmountSecureContainer(String id, boolean force) {
San Mehat4270e1e2010-01-29 05:32:19 -0800959 validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT);
San Mehat207e5382010-02-04 20:46:54 -0800960 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -0800961 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -0800962
San Mehat6cdd9c02010-02-09 14:45:20 -0800963 synchronized (mAsecMountSet) {
964 if (!mAsecMountSet.contains(id)) {
San Mehata181b212010-02-11 06:50:20 -0800965 return StorageResultCode.OperationFailedStorageNotMounted;
San Mehat6cdd9c02010-02-09 14:45:20 -0800966 }
967 }
968
San Mehatb1043402010-02-05 08:26:50 -0800969 int rc = StorageResultCode.OperationSucceeded;
San Mehatd9709982010-02-18 11:43:03 -0800970 String cmd = String.format("asec unmount %s%s", id, (force ? " force" : ""));
San Mehat4270e1e2010-01-29 05:32:19 -0800971 try {
972 mConnector.doCommand(cmd);
973 } catch (NativeDaemonConnectorException e) {
San Mehatd9709982010-02-18 11:43:03 -0800974 int code = e.getCode();
975 if (code == VoldResponseCode.OpFailedStorageBusy) {
976 rc = StorageResultCode.OperationFailedStorageBusy;
977 } else {
978 rc = StorageResultCode.OperationFailedInternalError;
979 }
San Mehat02735bc2010-01-26 15:18:08 -0800980 }
San Mehat6cdd9c02010-02-09 14:45:20 -0800981
982 if (rc == StorageResultCode.OperationSucceeded) {
983 synchronized (mAsecMountSet) {
984 mAsecMountSet.remove(id);
985 }
986 }
San Mehat4270e1e2010-01-29 05:32:19 -0800987 return rc;
San Mehat9dba7092010-01-18 06:47:41 -0800988 }
989
San Mehat6cdd9c02010-02-09 14:45:20 -0800990 public boolean isSecureContainerMounted(String id) {
991 validatePermission(android.Manifest.permission.ASEC_ACCESS);
992 waitForReady();
993 warnOnNotMounted();
994
995 synchronized (mAsecMountSet) {
996 return mAsecMountSet.contains(id);
997 }
998 }
999
San Mehat4270e1e2010-01-29 05:32:19 -08001000 public int renameSecureContainer(String oldId, String newId) {
1001 validatePermission(android.Manifest.permission.ASEC_RENAME);
San Mehat207e5382010-02-04 20:46:54 -08001002 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001003 warnOnNotMounted();
San Mehat4270e1e2010-01-29 05:32:19 -08001004
San Mehata181b212010-02-11 06:50:20 -08001005 synchronized (mAsecMountSet) {
San Mehat85451ee2010-02-24 08:54:18 -08001006 /*
1007 * Because a mounted container has active internal state which cannot be
1008 * changed while active, we must ensure both ids are not currently mounted.
1009 */
1010 if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) {
San Mehata181b212010-02-11 06:50:20 -08001011 return StorageResultCode.OperationFailedStorageMounted;
1012 }
1013 }
1014
San Mehatb1043402010-02-05 08:26:50 -08001015 int rc = StorageResultCode.OperationSucceeded;
San Mehat4270e1e2010-01-29 05:32:19 -08001016 String cmd = String.format("asec rename %s %s", oldId, newId);
1017 try {
1018 mConnector.doCommand(cmd);
1019 } catch (NativeDaemonConnectorException e) {
San Mehatb1043402010-02-05 08:26:50 -08001020 rc = StorageResultCode.OperationFailedInternalError;
San Mehat02735bc2010-01-26 15:18:08 -08001021 }
San Mehata181b212010-02-11 06:50:20 -08001022
San Mehat4270e1e2010-01-29 05:32:19 -08001023 return rc;
San Mehat45f61042010-01-23 08:12:43 -08001024 }
1025
San Mehat4270e1e2010-01-29 05:32:19 -08001026 public String getSecureContainerPath(String id) {
1027 validatePermission(android.Manifest.permission.ASEC_ACCESS);
San Mehat207e5382010-02-04 20:46:54 -08001028 waitForReady();
San Mehatb1043402010-02-05 08:26:50 -08001029 warnOnNotMounted();
San Mehatf919cd022010-02-04 15:10:38 -08001030
San Mehat4270e1e2010-01-29 05:32:19 -08001031 ArrayList<String> rsp = mConnector.doCommand("asec path " + id);
San Mehat36972292010-01-06 11:06:32 -08001032
San Mehat22dd86e2010-01-12 12:21:18 -08001033 for (String line : rsp) {
1034 String []tok = line.split(" ");
1035 int code = Integer.parseInt(tok[0]);
1036 if (code == VoldResponseCode.AsecPathResult) {
1037 return tok[1];
1038 } else {
San Mehat4270e1e2010-01-29 05:32:19 -08001039 Log.e(TAG, String.format("Unexpected response code %d", code));
1040 return "";
San Mehat22dd86e2010-01-12 12:21:18 -08001041 }
1042 }
San Mehat4270e1e2010-01-29 05:32:19 -08001043
1044 Log.e(TAG, "Got an empty response");
1045 return "";
San Mehat22dd86e2010-01-12 12:21:18 -08001046 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001047}
1048