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