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