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