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