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