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