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