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