1 /* 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 17 package com.android.server; 18 19 import com.android.internal.app.IMediaContainerService; 20 import com.android.internal.util.XmlUtils; 21 import com.android.server.am.ActivityManagerService; 22 import com.android.server.pm.PackageManagerService; 23 24 import android.Manifest; 25 import android.content.BroadcastReceiver; 26 import android.content.ComponentName; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.IntentFilter; 30 import android.content.ServiceConnection; 31 import android.content.pm.PackageManager; 32 import android.content.res.ObbInfo; 33 import android.content.res.Resources; 34 import android.content.res.TypedArray; 35 import android.content.res.XmlResourceParser; 36 import android.hardware.usb.UsbManager; 37 import android.net.Uri; 38 import android.os.Binder; 39 import android.os.Environment; 40 import android.os.Handler; 41 import android.os.HandlerThread; 42 import android.os.IBinder; 43 import android.os.Looper; 44 import android.os.Message; 45 import android.os.Parcelable; 46 import android.os.RemoteException; 47 import android.os.ServiceManager; 48 import android.os.SystemClock; 49 import android.os.SystemProperties; 50 import android.os.storage.IMountService; 51 import android.os.storage.IMountServiceListener; 52 import android.os.storage.IMountShutdownObserver; 53 import android.os.storage.IObbActionListener; 54 import android.os.storage.OnObbStateChangeListener; 55 import android.os.storage.StorageResultCode; 56 import android.os.storage.StorageVolume; 57 import android.text.TextUtils; 58 import android.util.AttributeSet; 59 import android.util.Slog; 60 import android.util.Xml; 61 62 import org.xmlpull.v1.XmlPullParser; 63 import org.xmlpull.v1.XmlPullParserException; 64 65 import java.io.FileDescriptor; 66 import java.io.IOException; 67 import java.io.PrintWriter; 68 import java.math.BigInteger; 69 import java.security.NoSuchAlgorithmException; 70 import java.security.spec.InvalidKeySpecException; 71 import java.security.spec.KeySpec; 72 import java.util.ArrayList; 73 import java.util.HashMap; 74 import java.util.HashSet; 75 import java.util.Iterator; 76 import java.util.LinkedList; 77 import java.util.List; 78 import java.util.Map; 79 import java.util.Map.Entry; 80 import java.util.Set; 81 82 import javax.crypto.SecretKey; 83 import javax.crypto.SecretKeyFactory; 84 import javax.crypto.spec.PBEKeySpec; 85 86 /** 87 * MountService implements back-end services for platform storage 88 * management. 89 * @hide - Applications should use android.os.storage.StorageManager 90 * to access the MountService. 91 */ 92 class MountService extends IMountService.Stub 93 implements INativeDaemonConnectorCallbacks, Watchdog.Monitor { 94 95 private static final boolean LOCAL_LOGD = false; 96 private static final boolean DEBUG_UNMOUNT = false; 97 private static final boolean DEBUG_EVENTS = false; 98 private static final boolean DEBUG_OBB = false; 99 100 // Disable this since it messes up long-running cryptfs operations. 101 private static final boolean WATCHDOG_ENABLE = false; 102 103 private static final String TAG = "MountService"; 104 105 private static final String VOLD_TAG = "VoldConnector"; 106 107 /** Maximum number of ASEC containers allowed to be mounted. */ 108 private static final int MAX_CONTAINERS = 250; 109 110 /* 111 * Internal vold volume state constants 112 */ 113 class VolumeState { 114 public static final int Init = -1; 115 public static final int NoMedia = 0; 116 public static final int Idle = 1; 117 public static final int Pending = 2; 118 public static final int Checking = 3; 119 public static final int Mounted = 4; 120 public static final int Unmounting = 5; 121 public static final int Formatting = 6; 122 public static final int Shared = 7; 123 public static final int SharedMnt = 8; 124 } 125 126 /* 127 * Internal vold response code constants 128 */ 129 class VoldResponseCode { 130 /* 131 * 100 series - Requestion action was initiated; expect another reply 132 * before proceeding with a new command. 133 */ 134 public static final int VolumeListResult = 110; 135 public static final int AsecListResult = 111; 136 public static final int StorageUsersListResult = 112; 137 138 /* 139 * 200 series - Requestion action has been successfully completed. 140 */ 141 public static final int ShareStatusResult = 210; 142 public static final int AsecPathResult = 211; 143 public static final int ShareEnabledResult = 212; 144 145 /* 146 * 400 series - Command was accepted, but the requested action 147 * did not take place. 148 */ 149 public static final int OpFailedNoMedia = 401; 150 public static final int OpFailedMediaBlank = 402; 151 public static final int OpFailedMediaCorrupt = 403; 152 public static final int OpFailedVolNotMounted = 404; 153 public static final int OpFailedStorageBusy = 405; 154 public static final int OpFailedStorageNotFound = 406; 155 156 /* 157 * 600 series - Unsolicited broadcasts. 158 */ 159 public static final int VolumeStateChange = 605; 160 public static final int VolumeDiskInserted = 630; 161 public static final int VolumeDiskRemoved = 631; 162 public static final int VolumeBadRemoval = 632; 163 } 164 165 private Context mContext; 166 private NativeDaemonConnector mConnector; 167 private final ArrayList<StorageVolume> mVolumes = new ArrayList<StorageVolume>(); 168 private StorageVolume mPrimaryVolume; 169 private final HashMap<String, String> mVolumeStates = new HashMap<String, String>(); 170 private final HashMap<String, StorageVolume> mVolumeMap = new HashMap<String, StorageVolume>(); 171 private String mExternalStoragePath; 172 private PackageManagerService mPms; 173 private boolean mUmsEnabling; 174 private boolean mUmsAvailable = false; 175 // Used as a lock for methods that register/unregister listeners. 176 final private ArrayList<MountServiceBinderListener> mListeners = 177 new ArrayList<MountServiceBinderListener>(); 178 private boolean mBooted = false; 179 private boolean mReady = false; 180 private boolean mSendUmsConnectedOnBoot = false; 181 // true if we should fake MEDIA_MOUNTED state for external storage 182 private boolean mEmulateExternalStorage = false; 183 184 /** 185 * Private hash of currently mounted secure containers. 186 * Used as a lock in methods to manipulate secure containers. 187 */ 188 final private HashSet<String> mAsecMountSet = new HashSet<String>(); 189 190 /** 191 * The size of the crypto algorithm key in bits for OBB files. Currently 192 * Twofish is used which takes 128-bit keys. 193 */ 194 private static final int CRYPTO_ALGORITHM_KEY_SIZE = 128; 195 196 /** 197 * The number of times to run SHA1 in the PBKDF2 function for OBB files. 198 * 1024 is reasonably secure and not too slow. 199 */ 200 private static final int PBKDF2_HASH_ROUNDS = 1024; 201 202 /** 203 * Mounted OBB tracking information. Used to track the current state of all 204 * OBBs. 205 */ 206 final private Map<IBinder, List<ObbState>> mObbMounts = new HashMap<IBinder, List<ObbState>>(); 207 final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>(); 208 209 class ObbState implements IBinder.DeathRecipient { 210 public ObbState(String filename, int callerUid, IObbActionListener token, int nonce) 211 throws RemoteException { 212 this.filename = filename; 213 this.callerUid = callerUid; 214 this.token = token; 215 this.nonce = nonce; 216 } 217 218 // OBB source filename 219 String filename; 220 221 // Binder.callingUid() 222 final public int callerUid; 223 224 // Token of remote Binder caller 225 final IObbActionListener token; 226 227 // Identifier to pass back to the token 228 final int nonce; 229 230 public IBinder getBinder() { 231 return token.asBinder(); 232 } 233 234 @Override 235 public void binderDied() { 236 ObbAction action = new UnmountObbAction(this, true); 237 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action)); 238 } 239 240 public void link() throws RemoteException { 241 getBinder().linkToDeath(this, 0); 242 } 243 244 public void unlink() { 245 getBinder().unlinkToDeath(this, 0); 246 } 247 248 @Override 249 public String toString() { 250 StringBuilder sb = new StringBuilder("ObbState{"); 251 sb.append("filename="); 252 sb.append(filename); 253 sb.append(",token="); 254 sb.append(token.toString()); 255 sb.append(",callerUid="); 256 sb.append(callerUid); 257 sb.append('}'); 258 return sb.toString(); 259 } 260 } 261 262 // OBB Action Handler 263 final private ObbActionHandler mObbActionHandler; 264 265 // OBB action handler messages 266 private static final int OBB_RUN_ACTION = 1; 267 private static final int OBB_MCS_BOUND = 2; 268 private static final int OBB_MCS_UNBIND = 3; 269 private static final int OBB_MCS_RECONNECT = 4; 270 private static final int OBB_FLUSH_MOUNT_STATE = 5; 271 272 /* 273 * Default Container Service information 274 */ 275 static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName( 276 "com.android.defcontainer", "com.android.defcontainer.DefaultContainerService"); 277 278 final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection(); 279 280 class DefaultContainerConnection implements ServiceConnection { 281 public void onServiceConnected(ComponentName name, IBinder service) { 282 if (DEBUG_OBB) 283 Slog.i(TAG, "onServiceConnected"); 284 IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service); 285 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs)); 286 } 287 288 public void onServiceDisconnected(ComponentName name) { 289 if (DEBUG_OBB) 290 Slog.i(TAG, "onServiceDisconnected"); 291 } 292 }; 293 294 // Used in the ObbActionHandler 295 private IMediaContainerService mContainerService = null; 296 297 // Handler messages 298 private static final int H_UNMOUNT_PM_UPDATE = 1; 299 private static final int H_UNMOUNT_PM_DONE = 2; 300 private static final int H_UNMOUNT_MS = 3; 301 private static final int RETRY_UNMOUNT_DELAY = 30; // in ms 302 private static final int MAX_UNMOUNT_RETRIES = 4; 303 304 class UnmountCallBack { 305 final String path; 306 final boolean force; 307 final boolean removeEncryption; 308 int retries; 309 310 UnmountCallBack(String path, boolean force, boolean removeEncryption) { 311 retries = 0; 312 this.path = path; 313 this.force = force; 314 this.removeEncryption = removeEncryption; 315 } 316 317 void handleFinished() { 318 if (DEBUG_UNMOUNT) Slog.i(TAG, "Unmounting " + path); 319 doUnmountVolume(path, true, removeEncryption); 320 } 321 } 322 323 class UmsEnableCallBack extends UnmountCallBack { 324 final String method; 325 326 UmsEnableCallBack(String path, String method, boolean force) { 327 super(path, force, false); 328 this.method = method; 329 } 330 331 @Override 332 void handleFinished() { 333 super.handleFinished(); 334 doShareUnshareVolume(path, method, true); 335 } 336 } 337 338 class ShutdownCallBack extends UnmountCallBack { 339 IMountShutdownObserver observer; 340 ShutdownCallBack(String path, IMountShutdownObserver observer) { 341 super(path, true, false); 342 this.observer = observer; 343 } 344 345 @Override 346 void handleFinished() { 347 int ret = doUnmountVolume(path, true, removeEncryption); 348 if (observer != null) { 349 try { 350 observer.onShutDownComplete(ret); 351 } catch (RemoteException e) { 352 Slog.w(TAG, "RemoteException when shutting down"); 353 } 354 } 355 } 356 } 357 358 class MountServiceHandler extends Handler { 359 ArrayList<UnmountCallBack> mForceUnmounts = new ArrayList<UnmountCallBack>(); 360 boolean mUpdatingStatus = false; 361 362 MountServiceHandler(Looper l) { 363 super(l); 364 } 365 366 @Override 367 public void handleMessage(Message msg) { 368 switch (msg.what) { 369 case H_UNMOUNT_PM_UPDATE: { 370 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_UPDATE"); 371 UnmountCallBack ucb = (UnmountCallBack) msg.obj; 372 mForceUnmounts.add(ucb); 373 if (DEBUG_UNMOUNT) Slog.i(TAG, " registered = " + mUpdatingStatus); 374 // Register only if needed. 375 if (!mUpdatingStatus) { 376 if (DEBUG_UNMOUNT) Slog.i(TAG, "Updating external media status on PackageManager"); 377 mUpdatingStatus = true; 378 mPms.updateExternalMediaStatus(false, true); 379 } 380 break; 381 } 382 case H_UNMOUNT_PM_DONE: { 383 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_PM_DONE"); 384 if (DEBUG_UNMOUNT) Slog.i(TAG, "Updated status. Processing requests"); 385 mUpdatingStatus = false; 386 int size = mForceUnmounts.size(); 387 int sizeArr[] = new int[size]; 388 int sizeArrN = 0; 389 // Kill processes holding references first 390 ActivityManagerService ams = (ActivityManagerService) 391 ServiceManager.getService("activity"); 392 for (int i = 0; i < size; i++) { 393 UnmountCallBack ucb = mForceUnmounts.get(i); 394 String path = ucb.path; 395 boolean done = false; 396 if (!ucb.force) { 397 done = true; 398 } else { 399 int pids[] = getStorageUsers(path); 400 if (pids == null || pids.length == 0) { 401 done = true; 402 } else { 403 // Eliminate system process here? 404 ams.killPids(pids, "unmount media", true); 405 // Confirm if file references have been freed. 406 pids = getStorageUsers(path); 407 if (pids == null || pids.length == 0) { 408 done = true; 409 } 410 } 411 } 412 if (!done && (ucb.retries < MAX_UNMOUNT_RETRIES)) { 413 // Retry again 414 Slog.i(TAG, "Retrying to kill storage users again"); 415 mHandler.sendMessageDelayed( 416 mHandler.obtainMessage(H_UNMOUNT_PM_DONE, 417 ucb.retries++), 418 RETRY_UNMOUNT_DELAY); 419 } else { 420 if (ucb.retries >= MAX_UNMOUNT_RETRIES) { 421 Slog.i(TAG, "Failed to unmount media inspite of " + 422 MAX_UNMOUNT_RETRIES + " retries. Forcibly killing processes now"); 423 } 424 sizeArr[sizeArrN++] = i; 425 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_MS, 426 ucb)); 427 } 428 } 429 // Remove already processed elements from list. 430 for (int i = (sizeArrN-1); i >= 0; i--) { 431 mForceUnmounts.remove(sizeArr[i]); 432 } 433 break; 434 } 435 case H_UNMOUNT_MS : { 436 if (DEBUG_UNMOUNT) Slog.i(TAG, "H_UNMOUNT_MS"); 437 UnmountCallBack ucb = (UnmountCallBack) msg.obj; 438 ucb.handleFinished(); 439 break; 440 } 441 } 442 } 443 }; 444 final private HandlerThread mHandlerThread; 445 final private Handler mHandler; 446 447 private void waitForReady() { 448 while (mReady == false) { 449 for (int retries = 5; retries > 0; retries--) { 450 if (mReady) { 451 return; 452 } 453 SystemClock.sleep(1000); 454 } 455 Slog.w(TAG, "Waiting too long for mReady!"); 456 } 457 } 458 459 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 460 @Override 461 public void onReceive(Context context, Intent intent) { 462 String action = intent.getAction(); 463 464 if (action.equals(Intent.ACTION_BOOT_COMPLETED)) { 465 mBooted = true; 466 467 /* 468 * In the simulator, we need to broadcast a volume mounted event 469 * to make the media scanner run. 470 */ 471 if ("simulator".equals(SystemProperties.get("ro.product.device"))) { 472 notifyVolumeStateChange(null, "/sdcard", VolumeState.NoMedia, 473 VolumeState.Mounted); 474 return; 475 } 476 new Thread() { 477 @Override 478 public void run() { 479 try { 480 // it is not safe to call vold with mVolumeStates locked 481 // so we make a copy of the paths and states and process them 482 // outside the lock 483 String[] paths; 484 String[] states; 485 int count; 486 synchronized (mVolumeStates) { 487 Set<String> keys = mVolumeStates.keySet(); 488 count = keys.size(); 489 paths = keys.toArray(new String[count]); 490 states = new String[count]; 491 for (int i = 0; i < count; i++) { 492 states[i] = mVolumeStates.get(paths[i]); 493 } 494 } 495 496 for (int i = 0; i < count; i++) { 497 String path = paths[i]; 498 String state = states[i]; 499 500 if (state.equals(Environment.MEDIA_UNMOUNTED)) { 501 int rc = doMountVolume(path); 502 if (rc != StorageResultCode.OperationSucceeded) { 503 Slog.e(TAG, String.format("Boot-time mount failed (%d)", 504 rc)); 505 } 506 } else if (state.equals(Environment.MEDIA_SHARED)) { 507 /* 508 * Bootstrap UMS enabled state since vold indicates 509 * the volume is shared (runtime restart while ums enabled) 510 */ 511 notifyVolumeStateChange(null, path, VolumeState.NoMedia, 512 VolumeState.Shared); 513 } 514 } 515 516 /* notify external storage has mounted to trigger media scanner */ 517 if (mEmulateExternalStorage) { 518 notifyVolumeStateChange(null, 519 Environment.getExternalStorageDirectory().getPath(), 520 VolumeState.NoMedia, VolumeState.Mounted); 521 } 522 523 /* 524 * If UMS was connected on boot, send the connected event 525 * now that we're up. 526 */ 527 if (mSendUmsConnectedOnBoot) { 528 sendUmsIntent(true); 529 mSendUmsConnectedOnBoot = false; 530 } 531 } catch (Exception ex) { 532 Slog.e(TAG, "Boot-time mount exception", ex); 533 } 534 } 535 }.start(); 536 } else if (action.equals(UsbManager.ACTION_USB_STATE)) { 537 boolean available = (intent.getBooleanExtra(UsbManager.USB_CONNECTED, false) && 538 intent.getBooleanExtra(UsbManager.USB_FUNCTION_MASS_STORAGE, false)); 539 notifyShareAvailabilityChange(available); 540 } 541 } 542 }; 543 private final class MountServiceBinderListener implements IBinder.DeathRecipient { 544 final IMountServiceListener mListener; 545 546 MountServiceBinderListener(IMountServiceListener listener) { 547 mListener = listener; 548 549 } 550 551 public void binderDied() { 552 if (LOCAL_LOGD) Slog.d(TAG, "An IMountServiceListener has died!"); 553 synchronized (mListeners) { 554 mListeners.remove(this); 555 mListener.asBinder().unlinkToDeath(this, 0); 556 } 557 } 558 } 559 560 private void doShareUnshareVolume(String path, String method, boolean enable) { 561 // TODO: Add support for multiple share methods 562 if (!method.equals("ums")) { 563 throw new IllegalArgumentException(String.format("Method %s not supported", method)); 564 } 565 566 try { 567 mConnector.doCommand(String.format( 568 "volume %sshare %s %s", (enable ? "" : "un"), path, method)); 569 } catch (NativeDaemonConnectorException e) { 570 Slog.e(TAG, "Failed to share/unshare", e); 571 } 572 } 573 574 private void updatePublicVolumeState(String path, String state) { 575 String oldState; 576 synchronized(mVolumeStates) { 577 oldState = mVolumeStates.put(path, state); 578 } 579 if (state.equals(oldState)) { 580 Slog.w(TAG, String.format("Duplicate state transition (%s -> %s) for %s", 581 state, state, path)); 582 return; 583 } 584 585 Slog.d(TAG, "volume state changed for " + path + " (" + oldState + " -> " + state + ")"); 586 587 if (path.equals(mExternalStoragePath)) { 588 // Update state on PackageManager, but only of real events 589 if (!mEmulateExternalStorage) { 590 if (Environment.MEDIA_UNMOUNTED.equals(state)) { 591 mPms.updateExternalMediaStatus(false, false); 592 593 /* 594 * Some OBBs might have been unmounted when this volume was 595 * unmounted, so send a message to the handler to let it know to 596 * remove those from the list of mounted OBBS. 597 */ 598 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage( 599 OBB_FLUSH_MOUNT_STATE, path)); 600 } else if (Environment.MEDIA_MOUNTED.equals(state)) { 601 mPms.updateExternalMediaStatus(true, false); 602 } 603 } 604 } 605 synchronized (mListeners) { 606 for (int i = mListeners.size() -1; i >= 0; i--) { 607 MountServiceBinderListener bl = mListeners.get(i); 608 try { 609 bl.mListener.onStorageStateChanged(path, oldState, state); 610 } catch (RemoteException rex) { 611 Slog.e(TAG, "Listener dead"); 612 mListeners.remove(i); 613 } catch (Exception ex) { 614 Slog.e(TAG, "Listener failed", ex); 615 } 616 } 617 } 618 } 619 620 /** 621 * 622 * Callback from NativeDaemonConnector 623 */ 624 public void onDaemonConnected() { 625 /* 626 * Since we'll be calling back into the NativeDaemonConnector, 627 * we need to do our work in a new thread. 628 */ 629 new Thread() { 630 @Override 631 public void run() { 632 /** 633 * Determine media state and UMS detection status 634 */ 635 try { 636 String[] vols = mConnector.doListCommand( 637 "volume list", VoldResponseCode.VolumeListResult); 638 for (String volstr : vols) { 639 String[] tok = volstr.split(" "); 640 // FMT: <label> <mountpoint> <state> 641 String path = tok[1]; 642 String state = Environment.MEDIA_REMOVED; 643 644 int st = Integer.parseInt(tok[2]); 645 if (st == VolumeState.NoMedia) { 646 state = Environment.MEDIA_REMOVED; 647 } else if (st == VolumeState.Idle) { 648 state = Environment.MEDIA_UNMOUNTED; 649 } else if (st == VolumeState.Mounted) { 650 state = Environment.MEDIA_MOUNTED; 651 Slog.i(TAG, "Media already mounted on daemon connection"); 652 } else if (st == VolumeState.Shared) { 653 state = Environment.MEDIA_SHARED; 654 Slog.i(TAG, "Media shared on daemon connection"); 655 } else { 656 throw new Exception(String.format("Unexpected state %d", st)); 657 } 658 659 if (state != null) { 660 if (DEBUG_EVENTS) Slog.i(TAG, "Updating valid state " + state); 661 updatePublicVolumeState(path, state); 662 } 663 } 664 } catch (Exception e) { 665 Slog.e(TAG, "Error processing initial volume state", e); 666 updatePublicVolumeState(mExternalStoragePath, Environment.MEDIA_REMOVED); 667 } 668 669 /* 670 * Now that we've done our initialization, release 671 * the hounds! 672 */ 673 mReady = true; 674 } 675 }.start(); 676 } 677 678 /** 679 * Callback from NativeDaemonConnector 680 */ 681 public boolean onEvent(int code, String raw, String[] cooked) { 682 if (DEBUG_EVENTS) { 683 StringBuilder builder = new StringBuilder(); 684 builder.append("onEvent::"); 685 builder.append(" raw= " + raw); 686 if (cooked != null) { 687 builder.append(" cooked = " ); 688 for (String str : cooked) { 689 builder.append(" " + str); 690 } 691 } 692 Slog.i(TAG, builder.toString()); 693 } 694 if (code == VoldResponseCode.VolumeStateChange) { 695 /* 696 * One of the volumes we're managing has changed state. 697 * Format: "NNN Volume <label> <path> state changed 698 * from <old_#> (<old_str>) to <new_#> (<new_str>)" 699 */ 700 notifyVolumeStateChange( 701 cooked[2], cooked[3], Integer.parseInt(cooked[7]), 702 Integer.parseInt(cooked[10])); 703 } else if ((code == VoldResponseCode.VolumeDiskInserted) || 704 (code == VoldResponseCode.VolumeDiskRemoved) || 705 (code == VoldResponseCode.VolumeBadRemoval)) { 706 // FMT: NNN Volume <label> <mountpoint> disk inserted (<major>:<minor>) 707 // FMT: NNN Volume <label> <mountpoint> disk removed (<major>:<minor>) 708 // FMT: NNN Volume <label> <mountpoint> bad removal (<major>:<minor>) 709 String action = null; 710 final String label = cooked[2]; 711 final String path = cooked[3]; 712 int major = -1; 713 int minor = -1; 714 715 try { 716 String devComp = cooked[6].substring(1, cooked[6].length() -1); 717 String[] devTok = devComp.split(":"); 718 major = Integer.parseInt(devTok[0]); 719 minor = Integer.parseInt(devTok[1]); 720 } catch (Exception ex) { 721 Slog.e(TAG, "Failed to parse major/minor", ex); 722 } 723 724 if (code == VoldResponseCode.VolumeDiskInserted) { 725 new Thread() { 726 @Override 727 public void run() { 728 try { 729 int rc; 730 if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) { 731 Slog.w(TAG, String.format("Insertion mount failed (%d)", rc)); 732 } 733 } catch (Exception ex) { 734 Slog.w(TAG, "Failed to mount media on insertion", ex); 735 } 736 } 737 }.start(); 738 } else if (code == VoldResponseCode.VolumeDiskRemoved) { 739 /* 740 * This event gets trumped if we're already in BAD_REMOVAL state 741 */ 742 if (getVolumeState(path).equals(Environment.MEDIA_BAD_REMOVAL)) { 743 return true; 744 } 745 /* Send the media unmounted event first */ 746 if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first"); 747 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED); 748 sendStorageIntent(Environment.MEDIA_UNMOUNTED, path); 749 750 if (DEBUG_EVENTS) Slog.i(TAG, "Sending media removed"); 751 updatePublicVolumeState(path, Environment.MEDIA_REMOVED); 752 action = Intent.ACTION_MEDIA_REMOVED; 753 } else if (code == VoldResponseCode.VolumeBadRemoval) { 754 if (DEBUG_EVENTS) Slog.i(TAG, "Sending unmounted event first"); 755 /* Send the media unmounted event first */ 756 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED); 757 action = Intent.ACTION_MEDIA_UNMOUNTED; 758 759 if (DEBUG_EVENTS) Slog.i(TAG, "Sending media bad removal"); 760 updatePublicVolumeState(path, Environment.MEDIA_BAD_REMOVAL); 761 action = Intent.ACTION_MEDIA_BAD_REMOVAL; 762 } else { 763 Slog.e(TAG, String.format("Unknown code {%d}", code)); 764 } 765 766 if (action != null) { 767 sendStorageIntent(action, path); 768 } 769 } else { 770 return false; 771 } 772 773 return true; 774 } 775 776 private void notifyVolumeStateChange(String label, String path, int oldState, int newState) { 777 String vs = getVolumeState(path); 778 if (DEBUG_EVENTS) Slog.i(TAG, "notifyVolumeStateChanged::" + vs); 779 780 String action = null; 781 782 if (oldState == VolumeState.Shared && newState != oldState) { 783 if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_UNSHARED intent"); 784 sendStorageIntent(Intent.ACTION_MEDIA_UNSHARED, path); 785 } 786 787 if (newState == VolumeState.Init) { 788 } else if (newState == VolumeState.NoMedia) { 789 // NoMedia is handled via Disk Remove events 790 } else if (newState == VolumeState.Idle) { 791 /* 792 * Don't notify if we're in BAD_REMOVAL, NOFS, UNMOUNTABLE, or 793 * if we're in the process of enabling UMS 794 */ 795 if (!vs.equals( 796 Environment.MEDIA_BAD_REMOVAL) && !vs.equals( 797 Environment.MEDIA_NOFS) && !vs.equals( 798 Environment.MEDIA_UNMOUNTABLE) && !getUmsEnabling()) { 799 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state for media bad removal nofs and unmountable"); 800 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED); 801 action = Intent.ACTION_MEDIA_UNMOUNTED; 802 } 803 } else if (newState == VolumeState.Pending) { 804 } else if (newState == VolumeState.Checking) { 805 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state checking"); 806 updatePublicVolumeState(path, Environment.MEDIA_CHECKING); 807 action = Intent.ACTION_MEDIA_CHECKING; 808 } else if (newState == VolumeState.Mounted) { 809 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state mounted"); 810 updatePublicVolumeState(path, Environment.MEDIA_MOUNTED); 811 action = Intent.ACTION_MEDIA_MOUNTED; 812 } else if (newState == VolumeState.Unmounting) { 813 action = Intent.ACTION_MEDIA_EJECT; 814 } else if (newState == VolumeState.Formatting) { 815 } else if (newState == VolumeState.Shared) { 816 if (DEBUG_EVENTS) Slog.i(TAG, "Updating volume state media mounted"); 817 /* Send the media unmounted event first */ 818 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTED); 819 sendStorageIntent(Intent.ACTION_MEDIA_UNMOUNTED, path); 820 821 if (DEBUG_EVENTS) Slog.i(TAG, "Updating media shared"); 822 updatePublicVolumeState(path, Environment.MEDIA_SHARED); 823 action = Intent.ACTION_MEDIA_SHARED; 824 if (LOCAL_LOGD) Slog.d(TAG, "Sending ACTION_MEDIA_SHARED intent"); 825 } else if (newState == VolumeState.SharedMnt) { 826 Slog.e(TAG, "Live shared mounts not supported yet!"); 827 return; 828 } else { 829 Slog.e(TAG, "Unhandled VolumeState {" + newState + "}"); 830 } 831 832 if (action != null) { 833 sendStorageIntent(action, path); 834 } 835 } 836 837 private int doMountVolume(String path) { 838 int rc = StorageResultCode.OperationSucceeded; 839 840 if (DEBUG_EVENTS) Slog.i(TAG, "doMountVolume: Mouting " + path); 841 try { 842 mConnector.doCommand(String.format("volume mount %s", path)); 843 } catch (NativeDaemonConnectorException e) { 844 /* 845 * Mount failed for some reason 846 */ 847 String action = null; 848 int code = e.getCode(); 849 if (code == VoldResponseCode.OpFailedNoMedia) { 850 /* 851 * Attempt to mount but no media inserted 852 */ 853 rc = StorageResultCode.OperationFailedNoMedia; 854 } else if (code == VoldResponseCode.OpFailedMediaBlank) { 855 if (DEBUG_EVENTS) Slog.i(TAG, " updating volume state :: media nofs"); 856 /* 857 * Media is blank or does not contain a supported filesystem 858 */ 859 updatePublicVolumeState(path, Environment.MEDIA_NOFS); 860 action = Intent.ACTION_MEDIA_NOFS; 861 rc = StorageResultCode.OperationFailedMediaBlank; 862 } else if (code == VoldResponseCode.OpFailedMediaCorrupt) { 863 if (DEBUG_EVENTS) Slog.i(TAG, "updating volume state media corrupt"); 864 /* 865 * Volume consistency check failed 866 */ 867 updatePublicVolumeState(path, Environment.MEDIA_UNMOUNTABLE); 868 action = Intent.ACTION_MEDIA_UNMOUNTABLE; 869 rc = StorageResultCode.OperationFailedMediaCorrupt; 870 } else { 871 rc = StorageResultCode.OperationFailedInternalError; 872 } 873 874 /* 875 * Send broadcast intent (if required for the failure) 876 */ 877 if (action != null) { 878 sendStorageIntent(action, path); 879 } 880 } 881 882 return rc; 883 } 884 885 /* 886 * If force is not set, we do not unmount if there are 887 * processes holding references to the volume about to be unmounted. 888 * If force is set, all the processes holding references need to be 889 * killed via the ActivityManager before actually unmounting the volume. 890 * This might even take a while and might be retried after timed delays 891 * to make sure we dont end up in an instable state and kill some core 892 * processes. 893 * If removeEncryption is set, force is implied, and the system will remove any encryption 894 * mapping set on the volume when unmounting. 895 */ 896 private int doUnmountVolume(String path, boolean force, boolean removeEncryption) { 897 if (!getVolumeState(path).equals(Environment.MEDIA_MOUNTED)) { 898 return VoldResponseCode.OpFailedVolNotMounted; 899 } 900 901 /* 902 * Force a GC to make sure AssetManagers in other threads of the 903 * system_server are cleaned up. We have to do this since AssetManager 904 * instances are kept as a WeakReference and it's possible we have files 905 * open on the external storage. 906 */ 907 Runtime.getRuntime().gc(); 908 909 // Redundant probably. But no harm in updating state again. 910 mPms.updateExternalMediaStatus(false, false); 911 try { 912 String arg = removeEncryption 913 ? " force_and_revert" 914 : (force ? " force" : ""); 915 mConnector.doCommand(String.format("volume unmount %s%s", path, arg)); 916 // We unmounted the volume. None of the asec containers are available now. 917 synchronized (mAsecMountSet) { 918 mAsecMountSet.clear(); 919 } 920 return StorageResultCode.OperationSucceeded; 921 } catch (NativeDaemonConnectorException e) { 922 // Don't worry about mismatch in PackageManager since the 923 // call back will handle the status changes any way. 924 int code = e.getCode(); 925 if (code == VoldResponseCode.OpFailedVolNotMounted) { 926 return StorageResultCode.OperationFailedStorageNotMounted; 927 } else if (code == VoldResponseCode.OpFailedStorageBusy) { 928 return StorageResultCode.OperationFailedStorageBusy; 929 } else { 930 return StorageResultCode.OperationFailedInternalError; 931 } 932 } 933 } 934 935 private int doFormatVolume(String path) { 936 try { 937 String cmd = String.format("volume format %s", path); 938 mConnector.doCommand(cmd); 939 return StorageResultCode.OperationSucceeded; 940 } catch (NativeDaemonConnectorException e) { 941 int code = e.getCode(); 942 if (code == VoldResponseCode.OpFailedNoMedia) { 943 return StorageResultCode.OperationFailedNoMedia; 944 } else if (code == VoldResponseCode.OpFailedMediaCorrupt) { 945 return StorageResultCode.OperationFailedMediaCorrupt; 946 } else { 947 return StorageResultCode.OperationFailedInternalError; 948 } 949 } 950 } 951 952 private boolean doGetVolumeShared(String path, String method) { 953 String cmd = String.format("volume shared %s %s", path, method); 954 ArrayList<String> rsp; 955 956 try { 957 rsp = mConnector.doCommand(cmd); 958 } catch (NativeDaemonConnectorException ex) { 959 Slog.e(TAG, "Failed to read response to volume shared " + path + " " + method); 960 return false; 961 } 962 963 for (String line : rsp) { 964 String[] tok = line.split(" "); 965 if (tok.length < 3) { 966 Slog.e(TAG, "Malformed response to volume shared " + path + " " + method + " command"); 967 return false; 968 } 969 970 int code; 971 try { 972 code = Integer.parseInt(tok[0]); 973 } catch (NumberFormatException nfe) { 974 Slog.e(TAG, String.format("Error parsing code %s", tok[0])); 975 return false; 976 } 977 if (code == VoldResponseCode.ShareEnabledResult) { 978 return "enabled".equals(tok[2]); 979 } else { 980 Slog.e(TAG, String.format("Unexpected response code %d", code)); 981 return false; 982 } 983 } 984 Slog.e(TAG, "Got an empty response"); 985 return false; 986 } 987 988 private void notifyShareAvailabilityChange(final boolean avail) { 989 synchronized (mListeners) { 990 mUmsAvailable = avail; 991 for (int i = mListeners.size() -1; i >= 0; i--) { 992 MountServiceBinderListener bl = mListeners.get(i); 993 try { 994 bl.mListener.onUsbMassStorageConnectionChanged(avail); 995 } catch (RemoteException rex) { 996 Slog.e(TAG, "Listener dead"); 997 mListeners.remove(i); 998 } catch (Exception ex) { 999 Slog.e(TAG, "Listener failed", ex); 1000 } 1001 } 1002 } 1003 1004 if (mBooted == true) { 1005 sendUmsIntent(avail); 1006 } else { 1007 mSendUmsConnectedOnBoot = avail; 1008 } 1009 1010 final String path = Environment.getExternalStorageDirectory().getPath(); 1011 if (avail == false && getVolumeState(path).equals(Environment.MEDIA_SHARED)) { 1012 /* 1013 * USB mass storage disconnected while enabled 1014 */ 1015 new Thread() { 1016 @Override 1017 public void run() { 1018 try { 1019 int rc; 1020 Slog.w(TAG, "Disabling UMS after cable disconnect"); 1021 doShareUnshareVolume(path, "ums", false); 1022 if ((rc = doMountVolume(path)) != StorageResultCode.OperationSucceeded) { 1023 Slog.e(TAG, String.format( 1024 "Failed to remount {%s} on UMS enabled-disconnect (%d)", 1025 path, rc)); 1026 } 1027 } catch (Exception ex) { 1028 Slog.w(TAG, "Failed to mount media on UMS enabled-disconnect", ex); 1029 } 1030 } 1031 }.start(); 1032 } 1033 } 1034 1035 private void sendStorageIntent(String action, String path) { 1036 Intent intent = new Intent(action, Uri.parse("file://" + path)); 1037 // add StorageVolume extra 1038 intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, mVolumeMap.get(path)); 1039 Slog.d(TAG, "sendStorageIntent " + intent); 1040 mContext.sendBroadcast(intent); 1041 } 1042 1043 private void sendUmsIntent(boolean c) { 1044 mContext.sendBroadcast( 1045 new Intent((c ? Intent.ACTION_UMS_CONNECTED : Intent.ACTION_UMS_DISCONNECTED))); 1046 } 1047 1048 private void validatePermission(String perm) { 1049 if (mContext.checkCallingOrSelfPermission(perm) != PackageManager.PERMISSION_GRANTED) { 1050 throw new SecurityException(String.format("Requires %s permission", perm)); 1051 } 1052 } 1053 1054 // Storage list XML tags 1055 private static final String TAG_STORAGE_LIST = "StorageList"; 1056 private static final String TAG_STORAGE = "storage"; 1057 1058 private void readStorageList(Resources resources) { 1059 int id = com.android.internal.R.xml.storage_list; 1060 XmlResourceParser parser = resources.getXml(id); 1061 AttributeSet attrs = Xml.asAttributeSet(parser); 1062 1063 try { 1064 XmlUtils.beginDocument(parser, TAG_STORAGE_LIST); 1065 while (true) { 1066 XmlUtils.nextElement(parser); 1067 1068 String element = parser.getName(); 1069 if (element == null) break; 1070 1071 if (TAG_STORAGE.equals(element)) { 1072 TypedArray a = resources.obtainAttributes(attrs, 1073 com.android.internal.R.styleable.Storage); 1074 1075 CharSequence path = a.getText( 1076 com.android.internal.R.styleable.Storage_mountPoint); 1077 CharSequence description = a.getText( 1078 com.android.internal.R.styleable.Storage_storageDescription); 1079 boolean primary = a.getBoolean( 1080 com.android.internal.R.styleable.Storage_primary, false); 1081 boolean removable = a.getBoolean( 1082 com.android.internal.R.styleable.Storage_removable, false); 1083 boolean emulated = a.getBoolean( 1084 com.android.internal.R.styleable.Storage_emulated, false); 1085 int mtpReserve = a.getInt( 1086 com.android.internal.R.styleable.Storage_mtpReserve, 0); 1087 boolean allowMassStorage = a.getBoolean( 1088 com.android.internal.R.styleable.Storage_allowMassStorage, false); 1089 // resource parser does not support longs, so XML value is in megabytes 1090 long maxFileSize = a.getInt( 1091 com.android.internal.R.styleable.Storage_maxFileSize, 0) * 1024L * 1024L; 1092 1093 Slog.d(TAG, "got storage path: " + path + " description: " + description + 1094 " primary: " + primary + " removable: " + removable + 1095 " emulated: " + emulated + " mtpReserve: " + mtpReserve + 1096 " allowMassStorage: " + allowMassStorage + 1097 " maxFileSize: " + maxFileSize); 1098 if (path == null || description == null) { 1099 Slog.e(TAG, "path or description is null in readStorageList"); 1100 } else { 1101 String pathString = path.toString(); 1102 StorageVolume volume = new StorageVolume(pathString, 1103 description.toString(), removable, emulated, 1104 mtpReserve, allowMassStorage, maxFileSize); 1105 if (primary) { 1106 if (mPrimaryVolume == null) { 1107 mPrimaryVolume = volume; 1108 } else { 1109 Slog.e(TAG, "multiple primary volumes in storage list"); 1110 } 1111 } 1112 if (mPrimaryVolume == volume) { 1113 // primay volume must be first 1114 mVolumes.add(0, volume); 1115 } else { 1116 mVolumes.add(volume); 1117 } 1118 mVolumeMap.put(pathString, volume); 1119 } 1120 a.recycle(); 1121 } 1122 } 1123 } catch (XmlPullParserException e) { 1124 throw new RuntimeException(e); 1125 } catch (IOException e) { 1126 throw new RuntimeException(e); 1127 } finally { 1128 // compute storage ID for each volume 1129 int length = mVolumes.size(); 1130 for (int i = 0; i < length; i++) { 1131 mVolumes.get(i).setStorageId(i); 1132 } 1133 parser.close(); 1134 } 1135 } 1136 1137 /** 1138 * Constructs a new MountService instance 1139 * 1140 * @param context Binder context for this service 1141 */ 1142 public MountService(Context context) { 1143 mContext = context; 1144 Resources resources = context.getResources(); 1145 readStorageList(resources); 1146 1147 if (mPrimaryVolume != null) { 1148 mExternalStoragePath = mPrimaryVolume.getPath(); 1149 mEmulateExternalStorage = mPrimaryVolume.isEmulated(); 1150 if (mEmulateExternalStorage) { 1151 Slog.d(TAG, "using emulated external storage"); 1152 mVolumeStates.put(mExternalStoragePath, Environment.MEDIA_MOUNTED); 1153 } 1154 } 1155 1156 // XXX: This will go away soon in favor of IMountServiceObserver 1157 mPms = (PackageManagerService) ServiceManager.getService("package"); 1158 1159 IntentFilter filter = new IntentFilter(); 1160 filter.addAction(Intent.ACTION_BOOT_COMPLETED); 1161 // don't bother monitoring USB if mass storage is not supported on our primary volume 1162 if (mPrimaryVolume != null && mPrimaryVolume.allowMassStorage()) { 1163 filter.addAction(UsbManager.ACTION_USB_STATE); 1164 } 1165 mContext.registerReceiver(mBroadcastReceiver, filter, null, null); 1166 1167 mHandlerThread = new HandlerThread("MountService"); 1168 mHandlerThread.start(); 1169 mHandler = new MountServiceHandler(mHandlerThread.getLooper()); 1170 1171 // Add OBB Action Handler to MountService thread. 1172 mObbActionHandler = new ObbActionHandler(mHandlerThread.getLooper()); 1173 1174 /* 1175 * Vold does not run in the simulator, so pretend the connector thread 1176 * ran and did its thing. 1177 */ 1178 if ("simulator".equals(SystemProperties.get("ro.product.device"))) { 1179 mReady = true; 1180 mUmsEnabling = true; 1181 return; 1182 } 1183 1184 /* 1185 * Create the connection to vold with a maximum queue of twice the 1186 * amount of containers we'd ever expect to have. This keeps an 1187 * "asec list" from blocking a thread repeatedly. 1188 */ 1189 mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG); 1190 mReady = false; 1191 Thread thread = new Thread(mConnector, VOLD_TAG); 1192 thread.start(); 1193 1194 // Add ourself to the Watchdog monitors if enabled. 1195 if (WATCHDOG_ENABLE) { 1196 Watchdog.getInstance().addMonitor(this); 1197 } 1198 } 1199 1200 /** 1201 * Exposed API calls below here 1202 */ 1203 1204 public void registerListener(IMountServiceListener listener) { 1205 synchronized (mListeners) { 1206 MountServiceBinderListener bl = new MountServiceBinderListener(listener); 1207 try { 1208 listener.asBinder().linkToDeath(bl, 0); 1209 mListeners.add(bl); 1210 } catch (RemoteException rex) { 1211 Slog.e(TAG, "Failed to link to listener death"); 1212 } 1213 } 1214 } 1215 1216 public void unregisterListener(IMountServiceListener listener) { 1217 synchronized (mListeners) { 1218 for(MountServiceBinderListener bl : mListeners) { 1219 if (bl.mListener == listener) { 1220 mListeners.remove(mListeners.indexOf(bl)); 1221 return; 1222 } 1223 } 1224 } 1225 } 1226 1227 public void shutdown(final IMountShutdownObserver observer) { 1228 validatePermission(android.Manifest.permission.SHUTDOWN); 1229 1230 Slog.i(TAG, "Shutting down"); 1231 synchronized (mVolumeStates) { 1232 for (String path : mVolumeStates.keySet()) { 1233 String state = mVolumeStates.get(path); 1234 1235 if (state.equals(Environment.MEDIA_SHARED)) { 1236 /* 1237 * If the media is currently shared, unshare it. 1238 * XXX: This is still dangerous!. We should not 1239 * be rebooting at *all* if UMS is enabled, since 1240 * the UMS host could have dirty FAT cache entries 1241 * yet to flush. 1242 */ 1243 setUsbMassStorageEnabled(false); 1244 } else if (state.equals(Environment.MEDIA_CHECKING)) { 1245 /* 1246 * If the media is being checked, then we need to wait for 1247 * it to complete before being able to proceed. 1248 */ 1249 // XXX: @hackbod - Should we disable the ANR timer here? 1250 int retries = 30; 1251 while (state.equals(Environment.MEDIA_CHECKING) && (retries-- >=0)) { 1252 try { 1253 Thread.sleep(1000); 1254 } catch (InterruptedException iex) { 1255 Slog.e(TAG, "Interrupted while waiting for media", iex); 1256 break; 1257 } 1258 state = Environment.getExternalStorageState(); 1259 } 1260 if (retries == 0) { 1261 Slog.e(TAG, "Timed out waiting for media to check"); 1262 } 1263 } 1264 1265 if (state.equals(Environment.MEDIA_MOUNTED)) { 1266 // Post a unmount message. 1267 ShutdownCallBack ucb = new ShutdownCallBack(path, observer); 1268 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb)); 1269 } else if (observer != null) { 1270 /* 1271 * Observer is waiting for onShutDownComplete when we are done. 1272 * Since nothing will be done send notification directly so shutdown 1273 * sequence can continue. 1274 */ 1275 try { 1276 observer.onShutDownComplete(StorageResultCode.OperationSucceeded); 1277 } catch (RemoteException e) { 1278 Slog.w(TAG, "RemoteException when shutting down"); 1279 } 1280 } 1281 } 1282 } 1283 } 1284 1285 private boolean getUmsEnabling() { 1286 synchronized (mListeners) { 1287 return mUmsEnabling; 1288 } 1289 } 1290 1291 private void setUmsEnabling(boolean enable) { 1292 synchronized (mListeners) { 1293 mUmsEnabling = enable; 1294 } 1295 } 1296 1297 public boolean isUsbMassStorageConnected() { 1298 waitForReady(); 1299 1300 if (getUmsEnabling()) { 1301 return true; 1302 } 1303 synchronized (mListeners) { 1304 return mUmsAvailable; 1305 } 1306 } 1307 1308 public void setUsbMassStorageEnabled(boolean enable) { 1309 waitForReady(); 1310 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); 1311 1312 // TODO: Add support for multiple share methods 1313 1314 /* 1315 * If the volume is mounted and we're enabling then unmount it 1316 */ 1317 String path = Environment.getExternalStorageDirectory().getPath(); 1318 String vs = getVolumeState(path); 1319 String method = "ums"; 1320 if (enable && vs.equals(Environment.MEDIA_MOUNTED)) { 1321 // Override for isUsbMassStorageEnabled() 1322 setUmsEnabling(enable); 1323 UmsEnableCallBack umscb = new UmsEnableCallBack(path, method, true); 1324 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, umscb)); 1325 // Clear override 1326 setUmsEnabling(false); 1327 } 1328 /* 1329 * If we disabled UMS then mount the volume 1330 */ 1331 if (!enable) { 1332 doShareUnshareVolume(path, method, enable); 1333 if (doMountVolume(path) != StorageResultCode.OperationSucceeded) { 1334 Slog.e(TAG, "Failed to remount " + path + 1335 " after disabling share method " + method); 1336 /* 1337 * Even though the mount failed, the unshare didn't so don't indicate an error. 1338 * The mountVolume() call will have set the storage state and sent the necessary 1339 * broadcasts. 1340 */ 1341 } 1342 } 1343 } 1344 1345 public boolean isUsbMassStorageEnabled() { 1346 waitForReady(); 1347 return doGetVolumeShared(Environment.getExternalStorageDirectory().getPath(), "ums"); 1348 } 1349 1350 /** 1351 * @return state of the volume at the specified mount point 1352 */ 1353 public String getVolumeState(String mountPoint) { 1354 synchronized (mVolumeStates) { 1355 String state = mVolumeStates.get(mountPoint); 1356 if (state == null) { 1357 Slog.w(TAG, "getVolumeState(" + mountPoint + "): Unknown volume"); 1358 if (SystemProperties.get("vold.encrypt_progress").length() != 0) { 1359 state = Environment.MEDIA_REMOVED; 1360 } else { 1361 throw new IllegalArgumentException(); 1362 } 1363 } 1364 1365 return state; 1366 } 1367 } 1368 1369 public boolean isExternalStorageEmulated() { 1370 return mEmulateExternalStorage; 1371 } 1372 1373 public int mountVolume(String path) { 1374 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); 1375 1376 waitForReady(); 1377 return doMountVolume(path); 1378 } 1379 1380 public void unmountVolume(String path, boolean force, boolean removeEncryption) { 1381 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); 1382 waitForReady(); 1383 1384 String volState = getVolumeState(path); 1385 if (DEBUG_UNMOUNT) { 1386 Slog.i(TAG, "Unmounting " + path 1387 + " force = " + force 1388 + " removeEncryption = " + removeEncryption); 1389 } 1390 if (Environment.MEDIA_UNMOUNTED.equals(volState) || 1391 Environment.MEDIA_REMOVED.equals(volState) || 1392 Environment.MEDIA_SHARED.equals(volState) || 1393 Environment.MEDIA_UNMOUNTABLE.equals(volState)) { 1394 // Media already unmounted or cannot be unmounted. 1395 // TODO return valid return code when adding observer call back. 1396 return; 1397 } 1398 UnmountCallBack ucb = new UnmountCallBack(path, force, removeEncryption); 1399 mHandler.sendMessage(mHandler.obtainMessage(H_UNMOUNT_PM_UPDATE, ucb)); 1400 } 1401 1402 public int formatVolume(String path) { 1403 validatePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); 1404 waitForReady(); 1405 1406 return doFormatVolume(path); 1407 } 1408 1409 public int[] getStorageUsers(String path) { 1410 validatePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); 1411 waitForReady(); 1412 try { 1413 String[] r = mConnector.doListCommand( 1414 String.format("storage users %s", path), 1415 VoldResponseCode.StorageUsersListResult); 1416 // FMT: <pid> <process name> 1417 int[] data = new int[r.length]; 1418 for (int i = 0; i < r.length; i++) { 1419 String []tok = r[i].split(" "); 1420 try { 1421 data[i] = Integer.parseInt(tok[0]); 1422 } catch (NumberFormatException nfe) { 1423 Slog.e(TAG, String.format("Error parsing pid %s", tok[0])); 1424 return new int[0]; 1425 } 1426 } 1427 return data; 1428 } catch (NativeDaemonConnectorException e) { 1429 Slog.e(TAG, "Failed to retrieve storage users list", e); 1430 return new int[0]; 1431 } 1432 } 1433 1434 private void warnOnNotMounted() { 1435 if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { 1436 Slog.w(TAG, "getSecureContainerList() called when storage not mounted"); 1437 } 1438 } 1439 1440 public String[] getSecureContainerList() { 1441 validatePermission(android.Manifest.permission.ASEC_ACCESS); 1442 waitForReady(); 1443 warnOnNotMounted(); 1444 1445 try { 1446 return mConnector.doListCommand("asec list", VoldResponseCode.AsecListResult); 1447 } catch (NativeDaemonConnectorException e) { 1448 return new String[0]; 1449 } 1450 } 1451 1452 public int createSecureContainer(String id, int sizeMb, String fstype, 1453 String key, int ownerUid) { 1454 validatePermission(android.Manifest.permission.ASEC_CREATE); 1455 waitForReady(); 1456 warnOnNotMounted(); 1457 1458 int rc = StorageResultCode.OperationSucceeded; 1459 String cmd = String.format("asec create %s %d %s %s %d", id, sizeMb, fstype, key, ownerUid); 1460 try { 1461 mConnector.doCommand(cmd); 1462 } catch (NativeDaemonConnectorException e) { 1463 rc = StorageResultCode.OperationFailedInternalError; 1464 } 1465 1466 if (rc == StorageResultCode.OperationSucceeded) { 1467 synchronized (mAsecMountSet) { 1468 mAsecMountSet.add(id); 1469 } 1470 } 1471 return rc; 1472 } 1473 1474 public int finalizeSecureContainer(String id) { 1475 validatePermission(android.Manifest.permission.ASEC_CREATE); 1476 warnOnNotMounted(); 1477 1478 int rc = StorageResultCode.OperationSucceeded; 1479 try { 1480 mConnector.doCommand(String.format("asec finalize %s", id)); 1481 /* 1482 * Finalization does a remount, so no need 1483 * to update mAsecMountSet 1484 */ 1485 } catch (NativeDaemonConnectorException e) { 1486 rc = StorageResultCode.OperationFailedInternalError; 1487 } 1488 return rc; 1489 } 1490 1491 public int destroySecureContainer(String id, boolean force) { 1492 validatePermission(android.Manifest.permission.ASEC_DESTROY); 1493 waitForReady(); 1494 warnOnNotMounted(); 1495 1496 /* 1497 * Force a GC to make sure AssetManagers in other threads of the 1498 * system_server are cleaned up. We have to do this since AssetManager 1499 * instances are kept as a WeakReference and it's possible we have files 1500 * open on the external storage. 1501 */ 1502 Runtime.getRuntime().gc(); 1503 1504 int rc = StorageResultCode.OperationSucceeded; 1505 try { 1506 mConnector.doCommand(String.format("asec destroy %s%s", id, (force ? " force" : ""))); 1507 } catch (NativeDaemonConnectorException e) { 1508 int code = e.getCode(); 1509 if (code == VoldResponseCode.OpFailedStorageBusy) { 1510 rc = StorageResultCode.OperationFailedStorageBusy; 1511 } else { 1512 rc = StorageResultCode.OperationFailedInternalError; 1513 } 1514 } 1515 1516 if (rc == StorageResultCode.OperationSucceeded) { 1517 synchronized (mAsecMountSet) { 1518 if (mAsecMountSet.contains(id)) { 1519 mAsecMountSet.remove(id); 1520 } 1521 } 1522 } 1523 1524 return rc; 1525 } 1526 1527 public int mountSecureContainer(String id, String key, int ownerUid) { 1528 validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT); 1529 waitForReady(); 1530 warnOnNotMounted(); 1531 1532 synchronized (mAsecMountSet) { 1533 if (mAsecMountSet.contains(id)) { 1534 return StorageResultCode.OperationFailedStorageMounted; 1535 } 1536 } 1537 1538 int rc = StorageResultCode.OperationSucceeded; 1539 String cmd = String.format("asec mount %s %s %d", id, key, ownerUid); 1540 try { 1541 mConnector.doCommand(cmd); 1542 } catch (NativeDaemonConnectorException e) { 1543 int code = e.getCode(); 1544 if (code != VoldResponseCode.OpFailedStorageBusy) { 1545 rc = StorageResultCode.OperationFailedInternalError; 1546 } 1547 } 1548 1549 if (rc == StorageResultCode.OperationSucceeded) { 1550 synchronized (mAsecMountSet) { 1551 mAsecMountSet.add(id); 1552 } 1553 } 1554 return rc; 1555 } 1556 1557 public int unmountSecureContainer(String id, boolean force) { 1558 validatePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT); 1559 waitForReady(); 1560 warnOnNotMounted(); 1561 1562 synchronized (mAsecMountSet) { 1563 if (!mAsecMountSet.contains(id)) { 1564 return StorageResultCode.OperationFailedStorageNotMounted; 1565 } 1566 } 1567 1568 /* 1569 * Force a GC to make sure AssetManagers in other threads of the 1570 * system_server are cleaned up. We have to do this since AssetManager 1571 * instances are kept as a WeakReference and it's possible we have files 1572 * open on the external storage. 1573 */ 1574 Runtime.getRuntime().gc(); 1575 1576 int rc = StorageResultCode.OperationSucceeded; 1577 String cmd = String.format("asec unmount %s%s", id, (force ? " force" : "")); 1578 try { 1579 mConnector.doCommand(cmd); 1580 } catch (NativeDaemonConnectorException e) { 1581 int code = e.getCode(); 1582 if (code == VoldResponseCode.OpFailedStorageBusy) { 1583 rc = StorageResultCode.OperationFailedStorageBusy; 1584 } else { 1585 rc = StorageResultCode.OperationFailedInternalError; 1586 } 1587 } 1588 1589 if (rc == StorageResultCode.OperationSucceeded) { 1590 synchronized (mAsecMountSet) { 1591 mAsecMountSet.remove(id); 1592 } 1593 } 1594 return rc; 1595 } 1596 1597 public boolean isSecureContainerMounted(String id) { 1598 validatePermission(android.Manifest.permission.ASEC_ACCESS); 1599 waitForReady(); 1600 warnOnNotMounted(); 1601 1602 synchronized (mAsecMountSet) { 1603 return mAsecMountSet.contains(id); 1604 } 1605 } 1606 1607 public int renameSecureContainer(String oldId, String newId) { 1608 validatePermission(android.Manifest.permission.ASEC_RENAME); 1609 waitForReady(); 1610 warnOnNotMounted(); 1611 1612 synchronized (mAsecMountSet) { 1613 /* 1614 * Because a mounted container has active internal state which cannot be 1615 * changed while active, we must ensure both ids are not currently mounted. 1616 */ 1617 if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) { 1618 return StorageResultCode.OperationFailedStorageMounted; 1619 } 1620 } 1621 1622 int rc = StorageResultCode.OperationSucceeded; 1623 String cmd = String.format("asec rename %s %s", oldId, newId); 1624 try { 1625 mConnector.doCommand(cmd); 1626 } catch (NativeDaemonConnectorException e) { 1627 rc = StorageResultCode.OperationFailedInternalError; 1628 } 1629 1630 return rc; 1631 } 1632 1633 public String getSecureContainerPath(String id) { 1634 validatePermission(android.Manifest.permission.ASEC_ACCESS); 1635 waitForReady(); 1636 warnOnNotMounted(); 1637 1638 try { 1639 ArrayList<String> rsp = mConnector.doCommand(String.format("asec path %s", id)); 1640 String []tok = rsp.get(0).split(" "); 1641 int code = Integer.parseInt(tok[0]); 1642 if (code != VoldResponseCode.AsecPathResult) { 1643 throw new IllegalStateException(String.format("Unexpected response code %d", code)); 1644 } 1645 return tok[1]; 1646 } catch (NativeDaemonConnectorException e) { 1647 int code = e.getCode(); 1648 if (code == VoldResponseCode.OpFailedStorageNotFound) { 1649 Slog.i(TAG, String.format("Container '%s' not found", id)); 1650 return null; 1651 } else { 1652 throw new IllegalStateException(String.format("Unexpected response code %d", code)); 1653 } 1654 } 1655 } 1656 1657 public String getSecureContainerFilesystemPath(String id) { 1658 validatePermission(android.Manifest.permission.ASEC_ACCESS); 1659 waitForReady(); 1660 warnOnNotMounted(); 1661 1662 try { 1663 ArrayList<String> rsp = mConnector.doCommand(String.format("asec fspath %s", id)); 1664 String []tok = rsp.get(0).split(" "); 1665 int code = Integer.parseInt(tok[0]); 1666 if (code != VoldResponseCode.AsecPathResult) { 1667 throw new IllegalStateException(String.format("Unexpected response code %d", code)); 1668 } 1669 return tok[1]; 1670 } catch (NativeDaemonConnectorException e) { 1671 int code = e.getCode(); 1672 if (code == VoldResponseCode.OpFailedStorageNotFound) { 1673 Slog.i(TAG, String.format("Container '%s' not found", id)); 1674 return null; 1675 } else { 1676 throw new IllegalStateException(String.format("Unexpected response code %d", code)); 1677 } 1678 } 1679 } 1680 1681 public void finishMediaUpdate() { 1682 mHandler.sendEmptyMessage(H_UNMOUNT_PM_DONE); 1683 } 1684 1685 private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) { 1686 if (callerUid == android.os.Process.SYSTEM_UID) { 1687 return true; 1688 } 1689 1690 if (packageName == null) { 1691 return false; 1692 } 1693 1694 final int packageUid = mPms.getPackageUid(packageName); 1695 1696 if (DEBUG_OBB) { 1697 Slog.d(TAG, "packageName = " + packageName + ", packageUid = " + 1698 packageUid + ", callerUid = " + callerUid); 1699 } 1700 1701 return callerUid == packageUid; 1702 } 1703 1704 public String getMountedObbPath(String filename) { 1705 if (filename == null) { 1706 throw new IllegalArgumentException("filename cannot be null"); 1707 } 1708 1709 waitForReady(); 1710 warnOnNotMounted(); 1711 1712 try { 1713 ArrayList<String> rsp = mConnector.doCommand(String.format("obb path %s", filename)); 1714 String []tok = rsp.get(0).split(" "); 1715 int code = Integer.parseInt(tok[0]); 1716 if (code != VoldResponseCode.AsecPathResult) { 1717 throw new IllegalStateException(String.format("Unexpected response code %d", code)); 1718 } 1719 return tok[1]; 1720 } catch (NativeDaemonConnectorException e) { 1721 int code = e.getCode(); 1722 if (code == VoldResponseCode.OpFailedStorageNotFound) { 1723 return null; 1724 } else { 1725 throw new IllegalStateException(String.format("Unexpected response code %d", code)); 1726 } 1727 } 1728 } 1729 1730 public boolean isObbMounted(String filename) { 1731 if (filename == null) { 1732 throw new IllegalArgumentException("filename cannot be null"); 1733 } 1734 1735 synchronized (mObbMounts) { 1736 return mObbPathToStateMap.containsKey(filename); 1737 } 1738 } 1739 1740 public void mountObb(String filename, String key, IObbActionListener token, int nonce) 1741 throws RemoteException { 1742 if (filename == null) { 1743 throw new IllegalArgumentException("filename cannot be null"); 1744 } 1745 1746 if (token == null) { 1747 throw new IllegalArgumentException("token cannot be null"); 1748 } 1749 1750 final int callerUid = Binder.getCallingUid(); 1751 final ObbState obbState = new ObbState(filename, callerUid, token, nonce); 1752 final ObbAction action = new MountObbAction(obbState, key); 1753 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action)); 1754 1755 if (DEBUG_OBB) 1756 Slog.i(TAG, "Send to OBB handler: " + action.toString()); 1757 } 1758 1759 public void unmountObb(String filename, boolean force, IObbActionListener token, int nonce) 1760 throws RemoteException { 1761 if (filename == null) { 1762 throw new IllegalArgumentException("filename cannot be null"); 1763 } 1764 1765 final int callerUid = Binder.getCallingUid(); 1766 final ObbState obbState = new ObbState(filename, callerUid, token, nonce); 1767 final ObbAction action = new UnmountObbAction(obbState, force); 1768 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action)); 1769 1770 if (DEBUG_OBB) 1771 Slog.i(TAG, "Send to OBB handler: " + action.toString()); 1772 } 1773 1774 @Override 1775 public int getEncryptionState() { 1776 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, 1777 "no permission to access the crypt keeper"); 1778 1779 waitForReady(); 1780 1781 try { 1782 ArrayList<String> rsp = mConnector.doCommand("cryptfs cryptocomplete"); 1783 String[] tokens = rsp.get(0).split(" "); 1784 1785 if (tokens == null || tokens.length != 2) { 1786 // Unexpected. 1787 Slog.w(TAG, "Unexpected result from cryptfs cryptocomplete"); 1788 return ENCRYPTION_STATE_ERROR_UNKNOWN; 1789 } 1790 1791 return Integer.parseInt(tokens[1]); 1792 1793 } catch (NumberFormatException e) { 1794 // Bad result - unexpected. 1795 Slog.w(TAG, "Unable to parse result from cryptfs cryptocomplete"); 1796 return ENCRYPTION_STATE_ERROR_UNKNOWN; 1797 } catch (NativeDaemonConnectorException e) { 1798 // Something bad happened. 1799 Slog.w(TAG, "Error in communicating with cryptfs in validating"); 1800 return ENCRYPTION_STATE_ERROR_UNKNOWN; 1801 } 1802 } 1803 1804 @Override 1805 public int decryptStorage(String password) { 1806 if (TextUtils.isEmpty(password)) { 1807 throw new IllegalArgumentException("password cannot be empty"); 1808 } 1809 1810 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, 1811 "no permission to access the crypt keeper"); 1812 1813 waitForReady(); 1814 1815 if (DEBUG_EVENTS) { 1816 Slog.i(TAG, "decrypting storage..."); 1817 } 1818 1819 try { 1820 ArrayList<String> rsp = mConnector.doCommand("cryptfs checkpw " + password); 1821 String[] tokens = rsp.get(0).split(" "); 1822 1823 if (tokens == null || tokens.length != 2) { 1824 return -1; 1825 } 1826 1827 int code = Integer.parseInt(tokens[1]); 1828 1829 if (code == 0) { 1830 // Decrypt was successful. Post a delayed message before restarting in order 1831 // to let the UI to clear itself 1832 mHandler.postDelayed(new Runnable() { 1833 public void run() { 1834 mConnector.doCommand(String.format("cryptfs restart")); 1835 } 1836 }, 1000); // 1 second 1837 } 1838 1839 return code; 1840 } catch (NativeDaemonConnectorException e) { 1841 // Decryption failed 1842 return e.getCode(); 1843 } 1844 } 1845 1846 public int encryptStorage(String password) { 1847 if (TextUtils.isEmpty(password)) { 1848 throw new IllegalArgumentException("password cannot be empty"); 1849 } 1850 1851 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, 1852 "no permission to access the crypt keeper"); 1853 1854 waitForReady(); 1855 1856 if (DEBUG_EVENTS) { 1857 Slog.i(TAG, "encrypting storage..."); 1858 } 1859 1860 try { 1861 mConnector.doCommand(String.format("cryptfs enablecrypto inplace %s", password)); 1862 } catch (NativeDaemonConnectorException e) { 1863 // Encryption failed 1864 return e.getCode(); 1865 } 1866 1867 return 0; 1868 } 1869 1870 public int changeEncryptionPassword(String password) { 1871 if (TextUtils.isEmpty(password)) { 1872 throw new IllegalArgumentException("password cannot be empty"); 1873 } 1874 1875 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, 1876 "no permission to access the crypt keeper"); 1877 1878 waitForReady(); 1879 1880 if (DEBUG_EVENTS) { 1881 Slog.i(TAG, "changing encryption password..."); 1882 } 1883 1884 try { 1885 ArrayList<String> response = mConnector.doCommand("cryptfs changepw " + password); 1886 1887 String[] tokens = response.get(0).split(" "); 1888 1889 if (tokens == null || tokens.length != 2) { 1890 return -1; 1891 } 1892 1893 return Integer.parseInt(tokens[1]); 1894 } catch (NativeDaemonConnectorException e) { 1895 // Encryption failed 1896 return e.getCode(); 1897 } 1898 } 1899 1900 /** 1901 * Validate a user-supplied password string with cryptfs 1902 */ 1903 @Override 1904 public int verifyEncryptionPassword(String password) throws RemoteException { 1905 // Only the system process is permitted to validate passwords 1906 if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) { 1907 throw new SecurityException("no permission to access the crypt keeper"); 1908 } 1909 1910 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, 1911 "no permission to access the crypt keeper"); 1912 1913 if (TextUtils.isEmpty(password)) { 1914 throw new IllegalArgumentException("password cannot be empty"); 1915 } 1916 1917 waitForReady(); 1918 1919 if (DEBUG_EVENTS) { 1920 Slog.i(TAG, "validating encryption password..."); 1921 } 1922 1923 try { 1924 ArrayList<String> response = mConnector.doCommand("cryptfs verifypw " + password); 1925 String[] tokens = response.get(0).split(" "); 1926 1927 if (tokens == null || tokens.length != 2) { 1928 String msg = "Unexpected result from cryptfs verifypw: {"; 1929 if (tokens == null) msg += "null"; 1930 else for (int i = 0; i < tokens.length; i++) { 1931 if (i != 0) msg += ','; 1932 msg += tokens[i]; 1933 } 1934 msg += '}'; 1935 Slog.e(TAG, msg); 1936 return -1; 1937 } 1938 1939 Slog.i(TAG, "cryptfs verifypw => " + tokens[1]); 1940 return Integer.parseInt(tokens[1]); 1941 } catch (NativeDaemonConnectorException e) { 1942 // Encryption failed 1943 return e.getCode(); 1944 } 1945 } 1946 1947 public Parcelable[] getVolumeList() { 1948 synchronized(mVolumes) { 1949 int size = mVolumes.size(); 1950 Parcelable[] result = new Parcelable[size]; 1951 for (int i = 0; i < size; i++) { 1952 result[i] = mVolumes.get(i); 1953 } 1954 return result; 1955 } 1956 } 1957 1958 private void addObbStateLocked(ObbState obbState) throws RemoteException { 1959 final IBinder binder = obbState.getBinder(); 1960 List<ObbState> obbStates = mObbMounts.get(binder); 1961 1962 if (obbStates == null) { 1963 obbStates = new ArrayList<ObbState>(); 1964 mObbMounts.put(binder, obbStates); 1965 } else { 1966 for (final ObbState o : obbStates) { 1967 if (o.filename.equals(obbState.filename)) { 1968 throw new IllegalStateException("Attempt to add ObbState twice. " 1969 + "This indicates an error in the MountService logic."); 1970 } 1971 } 1972 } 1973 1974 obbStates.add(obbState); 1975 try { 1976 obbState.link(); 1977 } catch (RemoteException e) { 1978 /* 1979 * The binder died before we could link it, so clean up our state 1980 * and return failure. 1981 */ 1982 obbStates.remove(obbState); 1983 if (obbStates.isEmpty()) { 1984 mObbMounts.remove(binder); 1985 } 1986 1987 // Rethrow the error so mountObb can get it 1988 throw e; 1989 } 1990 1991 mObbPathToStateMap.put(obbState.filename, obbState); 1992 } 1993 1994 private void removeObbStateLocked(ObbState obbState) { 1995 final IBinder binder = obbState.getBinder(); 1996 final List<ObbState> obbStates = mObbMounts.get(binder); 1997 if (obbStates != null) { 1998 if (obbStates.remove(obbState)) { 1999 obbState.unlink(); 2000 } 2001 if (obbStates.isEmpty()) { 2002 mObbMounts.remove(binder); 2003 } 2004 } 2005 2006 mObbPathToStateMap.remove(obbState.filename); 2007 } 2008 2009 private class ObbActionHandler extends Handler { 2010 private boolean mBound = false; 2011 private final List<ObbAction> mActions = new LinkedList<ObbAction>(); 2012 2013 ObbActionHandler(Looper l) { 2014 super(l); 2015 } 2016 2017 @Override 2018 public void handleMessage(Message msg) { 2019 switch (msg.what) { 2020 case OBB_RUN_ACTION: { 2021 final ObbAction action = (ObbAction) msg.obj; 2022 2023 if (DEBUG_OBB) 2024 Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString()); 2025 2026 // If a bind was already initiated we don't really 2027 // need to do anything. The pending install 2028 // will be processed later on. 2029 if (!mBound) { 2030 // If this is the only one pending we might 2031 // have to bind to the service again. 2032 if (!connectToService()) { 2033 Slog.e(TAG, "Failed to bind to media container service"); 2034 action.handleError(); 2035 return; 2036 } 2037 } 2038 2039 mActions.add(action); 2040 break; 2041 } 2042 case OBB_MCS_BOUND: { 2043 if (DEBUG_OBB) 2044 Slog.i(TAG, "OBB_MCS_BOUND"); 2045 if (msg.obj != null) { 2046 mContainerService = (IMediaContainerService) msg.obj; 2047 } 2048 if (mContainerService == null) { 2049 // Something seriously wrong. Bail out 2050 Slog.e(TAG, "Cannot bind to media container service"); 2051 for (ObbAction action : mActions) { 2052 // Indicate service bind error 2053 action.handleError(); 2054 } 2055 mActions.clear(); 2056 } else if (mActions.size() > 0) { 2057 final ObbAction action = mActions.get(0); 2058 if (action != null) { 2059 action.execute(this); 2060 } 2061 } else { 2062 // Should never happen ideally. 2063 Slog.w(TAG, "Empty queue"); 2064 } 2065 break; 2066 } 2067 case OBB_MCS_RECONNECT: { 2068 if (DEBUG_OBB) 2069 Slog.i(TAG, "OBB_MCS_RECONNECT"); 2070 if (mActions.size() > 0) { 2071 if (mBound) { 2072 disconnectService(); 2073 } 2074 if (!connectToService()) { 2075 Slog.e(TAG, "Failed to bind to media container service"); 2076 for (ObbAction action : mActions) { 2077 // Indicate service bind error 2078 action.handleError(); 2079 } 2080 mActions.clear(); 2081 } 2082 } 2083 break; 2084 } 2085 case OBB_MCS_UNBIND: { 2086 if (DEBUG_OBB) 2087 Slog.i(TAG, "OBB_MCS_UNBIND"); 2088 2089 // Delete pending install 2090 if (mActions.size() > 0) { 2091 mActions.remove(0); 2092 } 2093 if (mActions.size() == 0) { 2094 if (mBound) { 2095 disconnectService(); 2096 } 2097 } else { 2098 // There are more pending requests in queue. 2099 // Just post MCS_BOUND message to trigger processing 2100 // of next pending install. 2101 mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND); 2102 } 2103 break; 2104 } 2105 case OBB_FLUSH_MOUNT_STATE: { 2106 final String path = (String) msg.obj; 2107 2108 if (DEBUG_OBB) 2109 Slog.i(TAG, "Flushing all OBB state for path " + path); 2110 2111 synchronized (mObbMounts) { 2112 final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>(); 2113 2114 final Iterator<Entry<String, ObbState>> i = 2115 mObbPathToStateMap.entrySet().iterator(); 2116 while (i.hasNext()) { 2117 final Entry<String, ObbState> obbEntry = i.next(); 2118 2119 /* 2120 * If this entry's source file is in the volume path 2121 * that got unmounted, remove it because it's no 2122 * longer valid. 2123 */ 2124 if (obbEntry.getKey().startsWith(path)) { 2125 obbStatesToRemove.add(obbEntry.getValue()); 2126 } 2127 } 2128 2129 for (final ObbState obbState : obbStatesToRemove) { 2130 if (DEBUG_OBB) 2131 Slog.i(TAG, "Removing state for " + obbState.filename); 2132 2133 removeObbStateLocked(obbState); 2134 2135 try { 2136 obbState.token.onObbResult(obbState.filename, obbState.nonce, 2137 OnObbStateChangeListener.UNMOUNTED); 2138 } catch (RemoteException e) { 2139 Slog.i(TAG, "Couldn't send unmount notification for OBB: " 2140 + obbState.filename); 2141 } 2142 } 2143 } 2144 break; 2145 } 2146 } 2147 } 2148 2149 private boolean connectToService() { 2150 if (DEBUG_OBB) 2151 Slog.i(TAG, "Trying to bind to DefaultContainerService"); 2152 2153 Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT); 2154 if (mContext.bindService(service, mDefContainerConn, Context.BIND_AUTO_CREATE)) { 2155 mBound = true; 2156 return true; 2157 } 2158 return false; 2159 } 2160 2161 private void disconnectService() { 2162 mContainerService = null; 2163 mBound = false; 2164 mContext.unbindService(mDefContainerConn); 2165 } 2166 } 2167 2168 abstract class ObbAction { 2169 private static final int MAX_RETRIES = 3; 2170 private int mRetries; 2171 2172 ObbState mObbState; 2173 2174 ObbAction(ObbState obbState) { 2175 mObbState = obbState; 2176 } 2177 2178 public void execute(ObbActionHandler handler) { 2179 try { 2180 if (DEBUG_OBB) 2181 Slog.i(TAG, "Starting to execute action: " + toString()); 2182 mRetries++; 2183 if (mRetries > MAX_RETRIES) { 2184 Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up"); 2185 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND); 2186 handleError(); 2187 return; 2188 } else { 2189 handleExecute(); 2190 if (DEBUG_OBB) 2191 Slog.i(TAG, "Posting install MCS_UNBIND"); 2192 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND); 2193 } 2194 } catch (RemoteException e) { 2195 if (DEBUG_OBB) 2196 Slog.i(TAG, "Posting install MCS_RECONNECT"); 2197 mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT); 2198 } catch (Exception e) { 2199 if (DEBUG_OBB) 2200 Slog.d(TAG, "Error handling OBB action", e); 2201 handleError(); 2202 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND); 2203 } 2204 } 2205 2206 abstract void handleExecute() throws RemoteException, IOException; 2207 abstract void handleError(); 2208 2209 protected ObbInfo getObbInfo() throws IOException { 2210 ObbInfo obbInfo; 2211 try { 2212 obbInfo = mContainerService.getObbInfo(mObbState.filename); 2213 } catch (RemoteException e) { 2214 Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for " 2215 + mObbState.filename); 2216 obbInfo = null; 2217 } 2218 if (obbInfo == null) { 2219 throw new IOException("Couldn't read OBB file: " + mObbState.filename); 2220 } 2221 return obbInfo; 2222 } 2223 2224 protected void sendNewStatusOrIgnore(int status) { 2225 if (mObbState == null || mObbState.token == null) { 2226 return; 2227 } 2228 2229 try { 2230 mObbState.token.onObbResult(mObbState.filename, mObbState.nonce, status); 2231 } catch (RemoteException e) { 2232 Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged"); 2233 } 2234 } 2235 } 2236 2237 class MountObbAction extends ObbAction { 2238 private final String mKey; 2239 2240 MountObbAction(ObbState obbState, String key) { 2241 super(obbState); 2242 mKey = key; 2243 } 2244 2245 @Override 2246 public void handleExecute() throws IOException, RemoteException { 2247 waitForReady(); 2248 warnOnNotMounted(); 2249 2250 final ObbInfo obbInfo = getObbInfo(); 2251 2252 if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mObbState.callerUid)) { 2253 Slog.w(TAG, "Denied attempt to mount OBB " + obbInfo.filename 2254 + " which is owned by " + obbInfo.packageName); 2255 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED); 2256 return; 2257 } 2258 2259 final boolean isMounted; 2260 synchronized (mObbMounts) { 2261 isMounted = mObbPathToStateMap.containsKey(obbInfo.filename); 2262 } 2263 if (isMounted) { 2264 Slog.w(TAG, "Attempt to mount OBB which is already mounted: " + obbInfo.filename); 2265 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_ALREADY_MOUNTED); 2266 return; 2267 } 2268 2269 /* 2270 * The filename passed in might not be the canonical name, so just 2271 * set the filename to the canonicalized version. 2272 */ 2273 mObbState.filename = obbInfo.filename; 2274 2275 final String hashedKey; 2276 if (mKey == null) { 2277 hashedKey = "none"; 2278 } else { 2279 try { 2280 SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); 2281 2282 KeySpec ks = new PBEKeySpec(mKey.toCharArray(), obbInfo.salt, 2283 PBKDF2_HASH_ROUNDS, CRYPTO_ALGORITHM_KEY_SIZE); 2284 SecretKey key = factory.generateSecret(ks); 2285 BigInteger bi = new BigInteger(key.getEncoded()); 2286 hashedKey = bi.toString(16); 2287 } catch (NoSuchAlgorithmException e) { 2288 Slog.e(TAG, "Could not load PBKDF2 algorithm", e); 2289 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL); 2290 return; 2291 } catch (InvalidKeySpecException e) { 2292 Slog.e(TAG, "Invalid key spec when loading PBKDF2 algorithm", e); 2293 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL); 2294 return; 2295 } 2296 } 2297 2298 int rc = StorageResultCode.OperationSucceeded; 2299 String cmd = String.format("obb mount %s %s %d", mObbState.filename, hashedKey, 2300 mObbState.callerUid); 2301 try { 2302 mConnector.doCommand(cmd); 2303 } catch (NativeDaemonConnectorException e) { 2304 int code = e.getCode(); 2305 if (code != VoldResponseCode.OpFailedStorageBusy) { 2306 rc = StorageResultCode.OperationFailedInternalError; 2307 } 2308 } 2309 2310 if (rc == StorageResultCode.OperationSucceeded) { 2311 if (DEBUG_OBB) 2312 Slog.d(TAG, "Successfully mounted OBB " + mObbState.filename); 2313 2314 synchronized (mObbMounts) { 2315 addObbStateLocked(mObbState); 2316 } 2317 2318 sendNewStatusOrIgnore(OnObbStateChangeListener.MOUNTED); 2319 } else { 2320 Slog.e(TAG, "Couldn't mount OBB file: " + rc); 2321 2322 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT); 2323 } 2324 } 2325 2326 @Override 2327 public void handleError() { 2328 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL); 2329 } 2330 2331 @Override 2332 public String toString() { 2333 StringBuilder sb = new StringBuilder(); 2334 sb.append("MountObbAction{"); 2335 sb.append("filename="); 2336 sb.append(mObbState.filename); 2337 sb.append(",callerUid="); 2338 sb.append(mObbState.callerUid); 2339 sb.append(",token="); 2340 sb.append(mObbState.token != null ? mObbState.token.toString() : "NULL"); 2341 sb.append(",binder="); 2342 sb.append(mObbState.token != null ? mObbState.getBinder().toString() : "null"); 2343 sb.append('}'); 2344 return sb.toString(); 2345 } 2346 } 2347 2348 class UnmountObbAction extends ObbAction { 2349 private final boolean mForceUnmount; 2350 2351 UnmountObbAction(ObbState obbState, boolean force) { 2352 super(obbState); 2353 mForceUnmount = force; 2354 } 2355 2356 @Override 2357 public void handleExecute() throws IOException { 2358 waitForReady(); 2359 warnOnNotMounted(); 2360 2361 final ObbInfo obbInfo = getObbInfo(); 2362 2363 final ObbState obbState; 2364 synchronized (mObbMounts) { 2365 obbState = mObbPathToStateMap.get(obbInfo.filename); 2366 } 2367 2368 if (obbState == null) { 2369 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_NOT_MOUNTED); 2370 return; 2371 } 2372 2373 if (obbState.callerUid != mObbState.callerUid) { 2374 Slog.w(TAG, "Permission denied attempting to unmount OBB " + obbInfo.filename 2375 + " (owned by " + obbInfo.packageName + ")"); 2376 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED); 2377 return; 2378 } 2379 2380 mObbState.filename = obbInfo.filename; 2381 2382 int rc = StorageResultCode.OperationSucceeded; 2383 String cmd = String.format("obb unmount %s%s", mObbState.filename, 2384 (mForceUnmount ? " force" : "")); 2385 try { 2386 mConnector.doCommand(cmd); 2387 } catch (NativeDaemonConnectorException e) { 2388 int code = e.getCode(); 2389 if (code == VoldResponseCode.OpFailedStorageBusy) { 2390 rc = StorageResultCode.OperationFailedStorageBusy; 2391 } else if (code == VoldResponseCode.OpFailedStorageNotFound) { 2392 // If it's not mounted then we've already won. 2393 rc = StorageResultCode.OperationSucceeded; 2394 } else { 2395 rc = StorageResultCode.OperationFailedInternalError; 2396 } 2397 } 2398 2399 if (rc == StorageResultCode.OperationSucceeded) { 2400 synchronized (mObbMounts) { 2401 removeObbStateLocked(obbState); 2402 } 2403 2404 sendNewStatusOrIgnore(OnObbStateChangeListener.UNMOUNTED); 2405 } else { 2406 Slog.w(TAG, "Could not mount OBB: " + mObbState.filename); 2407 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT); 2408 } 2409 } 2410 2411 @Override 2412 public void handleError() { 2413 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL); 2414 } 2415 2416 @Override 2417 public String toString() { 2418 StringBuilder sb = new StringBuilder(); 2419 sb.append("UnmountObbAction{"); 2420 sb.append("filename="); 2421 sb.append(mObbState.filename != null ? mObbState.filename : "null"); 2422 sb.append(",force="); 2423 sb.append(mForceUnmount); 2424 sb.append(",callerUid="); 2425 sb.append(mObbState.callerUid); 2426 sb.append(",token="); 2427 sb.append(mObbState.token != null ? mObbState.token.toString() : "null"); 2428 sb.append(",binder="); 2429 sb.append(mObbState.token != null ? mObbState.getBinder().toString() : "null"); 2430 sb.append('}'); 2431 return sb.toString(); 2432 } 2433 } 2434 2435 @Override 2436 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2437 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) != PackageManager.PERMISSION_GRANTED) { 2438 pw.println("Permission Denial: can't dump ActivityManager from from pid=" 2439 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() 2440 + " without permission " + android.Manifest.permission.DUMP); 2441 return; 2442 } 2443 2444 synchronized (mObbMounts) { 2445 pw.println(" mObbMounts:"); 2446 2447 final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet().iterator(); 2448 while (binders.hasNext()) { 2449 Entry<IBinder, List<ObbState>> e = binders.next(); 2450 pw.print(" Key="); pw.println(e.getKey().toString()); 2451 final List<ObbState> obbStates = e.getValue(); 2452 for (final ObbState obbState : obbStates) { 2453 pw.print(" "); pw.println(obbState.toString()); 2454 } 2455 } 2456 2457 pw.println(""); 2458 pw.println(" mObbPathToStateMap:"); 2459 final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator(); 2460 while (maps.hasNext()) { 2461 final Entry<String, ObbState> e = maps.next(); 2462 pw.print(" "); pw.print(e.getKey()); 2463 pw.print(" -> "); pw.println(e.getValue().toString()); 2464 } 2465 } 2466 2467 pw.println(""); 2468 2469 synchronized (mVolumes) { 2470 pw.println(" mVolumes:"); 2471 2472 final int N = mVolumes.size(); 2473 for (int i = 0; i < N; i++) { 2474 final StorageVolume v = mVolumes.get(i); 2475 pw.print(" "); 2476 pw.println(v.toString()); 2477 } 2478 } 2479 } 2480 2481 /** {@inheritDoc} */ 2482 public void monitor() { 2483 if (mConnector != null) { 2484 mConnector.monitor(); 2485 } 2486 } 2487 } 2488