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