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