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