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