1 /* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server; 18 19 import static com.android.internal.util.XmlUtils.readBooleanAttribute; 20 import static com.android.internal.util.XmlUtils.readIntAttribute; 21 import static com.android.internal.util.XmlUtils.readLongAttribute; 22 import static com.android.internal.util.XmlUtils.readStringAttribute; 23 import static com.android.internal.util.XmlUtils.writeBooleanAttribute; 24 import static com.android.internal.util.XmlUtils.writeIntAttribute; 25 import static com.android.internal.util.XmlUtils.writeLongAttribute; 26 import static com.android.internal.util.XmlUtils.writeStringAttribute; 27 28 import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; 29 import static org.xmlpull.v1.XmlPullParser.START_TAG; 30 31 import android.Manifest; 32 import android.annotation.Nullable; 33 import android.app.ActivityManager; 34 import android.app.ActivityManagerNative; 35 import android.app.AppOpsManager; 36 import android.app.IActivityManager; 37 import android.content.BroadcastReceiver; 38 import android.content.ComponentName; 39 import android.content.Context; 40 import android.content.Intent; 41 import android.content.IntentFilter; 42 import android.content.ServiceConnection; 43 import android.content.pm.IPackageMoveObserver; 44 import android.content.pm.PackageManager; 45 import android.content.pm.ProviderInfo; 46 import android.content.pm.UserInfo; 47 import android.content.res.Configuration; 48 import android.content.res.ObbInfo; 49 import android.net.Uri; 50 import android.os.Binder; 51 import android.os.DropBoxManager; 52 import android.os.Environment; 53 import android.os.Environment.UserEnvironment; 54 import android.os.FileUtils; 55 import android.os.Handler; 56 import android.os.HandlerThread; 57 import android.os.IBinder; 58 import android.os.Looper; 59 import android.os.Message; 60 import android.os.ParcelFileDescriptor; 61 import android.os.PowerManager; 62 import android.os.Process; 63 import android.os.RemoteCallbackList; 64 import android.os.RemoteException; 65 import android.os.ServiceManager; 66 import android.os.SystemClock; 67 import android.os.SystemProperties; 68 import android.os.UserHandle; 69 import android.os.UserManager; 70 import android.os.storage.DiskInfo; 71 import android.os.storage.IMountService; 72 import android.os.storage.IMountServiceListener; 73 import android.os.storage.IMountShutdownObserver; 74 import android.os.storage.IObbActionListener; 75 import android.os.storage.MountServiceInternal; 76 import android.os.storage.OnObbStateChangeListener; 77 import android.os.storage.StorageManager; 78 import android.os.storage.StorageResultCode; 79 import android.os.storage.StorageVolume; 80 import android.os.storage.VolumeInfo; 81 import android.os.storage.VolumeRecord; 82 import android.provider.MediaStore; 83 import android.provider.Settings; 84 import android.text.TextUtils; 85 import android.text.format.DateUtils; 86 import android.util.ArrayMap; 87 import android.util.AtomicFile; 88 import android.util.Log; 89 import android.util.Slog; 90 import android.util.TimeUtils; 91 import android.util.Xml; 92 93 import com.android.internal.annotations.GuardedBy; 94 import com.android.internal.app.IMediaContainerService; 95 import com.android.internal.os.SomeArgs; 96 import com.android.internal.os.Zygote; 97 import com.android.internal.util.ArrayUtils; 98 import com.android.internal.util.FastXmlSerializer; 99 import com.android.internal.util.HexDump; 100 import com.android.internal.util.IndentingPrintWriter; 101 import com.android.internal.util.Preconditions; 102 import com.android.internal.widget.LockPatternUtils; 103 import com.android.server.NativeDaemonConnector.Command; 104 import com.android.server.NativeDaemonConnector.SensitiveArg; 105 import com.android.server.pm.PackageManagerService; 106 107 import libcore.io.IoUtils; 108 import libcore.util.EmptyArray; 109 110 import org.xmlpull.v1.XmlPullParser; 111 import org.xmlpull.v1.XmlPullParserException; 112 import org.xmlpull.v1.XmlSerializer; 113 114 import java.io.File; 115 import java.io.FileDescriptor; 116 import java.io.FileInputStream; 117 import java.io.FileNotFoundException; 118 import java.io.FileOutputStream; 119 import java.io.IOException; 120 import java.io.PrintWriter; 121 import java.math.BigInteger; 122 import java.nio.charset.StandardCharsets; 123 import java.security.NoSuchAlgorithmException; 124 import java.security.spec.InvalidKeySpecException; 125 import java.security.spec.KeySpec; 126 import java.util.ArrayList; 127 import java.util.Arrays; 128 import java.util.HashMap; 129 import java.util.HashSet; 130 import java.util.Iterator; 131 import java.util.LinkedList; 132 import java.util.List; 133 import java.util.Locale; 134 import java.util.Map; 135 import java.util.Map.Entry; 136 import java.util.Objects; 137 import java.util.concurrent.CopyOnWriteArrayList; 138 import java.util.concurrent.CountDownLatch; 139 import java.util.concurrent.TimeUnit; 140 import java.util.concurrent.TimeoutException; 141 142 import javax.crypto.SecretKey; 143 import javax.crypto.SecretKeyFactory; 144 import javax.crypto.spec.PBEKeySpec; 145 146 /** 147 * Service responsible for various storage media. Connects to {@code vold} to 148 * watch for and manage dynamically added storage, such as SD cards and USB mass 149 * storage. Also decides how storage should be presented to users on the device. 150 */ 151 class MountService extends IMountService.Stub 152 implements INativeDaemonConnectorCallbacks, Watchdog.Monitor { 153 154 // Static direct instance pointer for the tightly-coupled idle service to use 155 static MountService sSelf = null; 156 157 public static class Lifecycle extends SystemService { 158 private MountService mMountService; 159 160 public Lifecycle(Context context) { 161 super(context); 162 } 163 164 @Override 165 public void onStart() { 166 mMountService = new MountService(getContext()); 167 publishBinderService("mount", mMountService); 168 mMountService.start(); 169 } 170 171 @Override 172 public void onBootPhase(int phase) { 173 if (phase == SystemService.PHASE_ACTIVITY_MANAGER_READY) { 174 mMountService.systemReady(); 175 } else if (phase == SystemService.PHASE_BOOT_COMPLETED) { 176 mMountService.bootCompleted(); 177 } 178 } 179 180 @Override 181 public void onSwitchUser(int userHandle) { 182 mMountService.mCurrentUserId = userHandle; 183 } 184 185 @Override 186 public void onUnlockUser(int userHandle) { 187 mMountService.onUnlockUser(userHandle); 188 } 189 190 @Override 191 public void onCleanupUser(int userHandle) { 192 mMountService.onCleanupUser(userHandle); 193 } 194 } 195 196 private static final boolean DEBUG_EVENTS = false; 197 private static final boolean DEBUG_OBB = false; 198 199 // Disable this since it messes up long-running cryptfs operations. 200 private static final boolean WATCHDOG_ENABLE = false; 201 202 private static final String TAG = "MountService"; 203 204 private static final String TAG_STORAGE_BENCHMARK = "storage_benchmark"; 205 private static final String TAG_STORAGE_TRIM = "storage_trim"; 206 207 private static final String VOLD_TAG = "VoldConnector"; 208 private static final String CRYPTD_TAG = "CryptdConnector"; 209 210 /** Maximum number of ASEC containers allowed to be mounted. */ 211 private static final int MAX_CONTAINERS = 250; 212 213 /** Magic value sent by MoveTask.cpp */ 214 private static final int MOVE_STATUS_COPY_FINISHED = 82; 215 216 /* 217 * Internal vold response code constants 218 */ 219 class VoldResponseCode { 220 /* 221 * 100 series - Requestion action was initiated; expect another reply 222 * before proceeding with a new command. 223 */ 224 public static final int VolumeListResult = 110; 225 public static final int AsecListResult = 111; 226 public static final int StorageUsersListResult = 112; 227 public static final int CryptfsGetfieldResult = 113; 228 229 /* 230 * 200 series - Requestion action has been successfully completed. 231 */ 232 public static final int ShareStatusResult = 210; 233 public static final int AsecPathResult = 211; 234 public static final int ShareEnabledResult = 212; 235 236 /* 237 * 400 series - Command was accepted, but the requested action 238 * did not take place. 239 */ 240 public static final int OpFailedNoMedia = 401; 241 public static final int OpFailedMediaBlank = 402; 242 public static final int OpFailedMediaCorrupt = 403; 243 public static final int OpFailedVolNotMounted = 404; 244 public static final int OpFailedStorageBusy = 405; 245 public static final int OpFailedStorageNotFound = 406; 246 247 /* 248 * 600 series - Unsolicited broadcasts. 249 */ 250 public static final int DISK_CREATED = 640; 251 public static final int DISK_SIZE_CHANGED = 641; 252 public static final int DISK_LABEL_CHANGED = 642; 253 public static final int DISK_SCANNED = 643; 254 public static final int DISK_SYS_PATH_CHANGED = 644; 255 public static final int DISK_DESTROYED = 649; 256 257 public static final int VOLUME_CREATED = 650; 258 public static final int VOLUME_STATE_CHANGED = 651; 259 public static final int VOLUME_FS_TYPE_CHANGED = 652; 260 public static final int VOLUME_FS_UUID_CHANGED = 653; 261 public static final int VOLUME_FS_LABEL_CHANGED = 654; 262 public static final int VOLUME_PATH_CHANGED = 655; 263 public static final int VOLUME_INTERNAL_PATH_CHANGED = 656; 264 public static final int VOLUME_DESTROYED = 659; 265 266 public static final int MOVE_STATUS = 660; 267 public static final int BENCHMARK_RESULT = 661; 268 public static final int TRIM_RESULT = 662; 269 } 270 271 private static final int VERSION_INIT = 1; 272 private static final int VERSION_ADD_PRIMARY = 2; 273 private static final int VERSION_FIX_PRIMARY = 3; 274 275 private static final String TAG_VOLUMES = "volumes"; 276 private static final String ATTR_VERSION = "version"; 277 private static final String ATTR_PRIMARY_STORAGE_UUID = "primaryStorageUuid"; 278 private static final String ATTR_FORCE_ADOPTABLE = "forceAdoptable"; 279 private static final String TAG_VOLUME = "volume"; 280 private static final String ATTR_TYPE = "type"; 281 private static final String ATTR_FS_UUID = "fsUuid"; 282 private static final String ATTR_PART_GUID = "partGuid"; 283 private static final String ATTR_NICKNAME = "nickname"; 284 private static final String ATTR_USER_FLAGS = "userFlags"; 285 private static final String ATTR_CREATED_MILLIS = "createdMillis"; 286 private static final String ATTR_LAST_TRIM_MILLIS = "lastTrimMillis"; 287 private static final String ATTR_LAST_BENCH_MILLIS = "lastBenchMillis"; 288 289 private final AtomicFile mSettingsFile; 290 291 /** 292 * <em>Never</em> hold the lock while performing downcalls into vold, since 293 * unsolicited events can suddenly appear to update data structures. 294 */ 295 private final Object mLock = new Object(); 296 297 /** Set of users that we know are unlocked. */ 298 @GuardedBy("mLock") 299 private int[] mLocalUnlockedUsers = EmptyArray.INT; 300 /** Set of users that system knows are unlocked. */ 301 @GuardedBy("mLock") 302 private int[] mSystemUnlockedUsers = EmptyArray.INT; 303 304 /** Map from disk ID to disk */ 305 @GuardedBy("mLock") 306 private ArrayMap<String, DiskInfo> mDisks = new ArrayMap<>(); 307 /** Map from volume ID to disk */ 308 @GuardedBy("mLock") 309 private final ArrayMap<String, VolumeInfo> mVolumes = new ArrayMap<>(); 310 311 /** Map from UUID to record */ 312 @GuardedBy("mLock") 313 private ArrayMap<String, VolumeRecord> mRecords = new ArrayMap<>(); 314 @GuardedBy("mLock") 315 private String mPrimaryStorageUuid; 316 @GuardedBy("mLock") 317 private boolean mForceAdoptable; 318 319 /** Map from disk ID to latches */ 320 @GuardedBy("mLock") 321 private ArrayMap<String, CountDownLatch> mDiskScanLatches = new ArrayMap<>(); 322 323 @GuardedBy("mLock") 324 private IPackageMoveObserver mMoveCallback; 325 @GuardedBy("mLock") 326 private String mMoveTargetUuid; 327 328 private volatile int mCurrentUserId = UserHandle.USER_SYSTEM; 329 330 private VolumeInfo findVolumeByIdOrThrow(String id) { 331 synchronized (mLock) { 332 final VolumeInfo vol = mVolumes.get(id); 333 if (vol != null) { 334 return vol; 335 } 336 } 337 throw new IllegalArgumentException("No volume found for ID " + id); 338 } 339 340 private String findVolumeIdForPathOrThrow(String path) { 341 synchronized (mLock) { 342 for (int i = 0; i < mVolumes.size(); i++) { 343 final VolumeInfo vol = mVolumes.valueAt(i); 344 if (vol.path != null && path.startsWith(vol.path)) { 345 return vol.id; 346 } 347 } 348 } 349 throw new IllegalArgumentException("No volume found for path " + path); 350 } 351 352 private VolumeRecord findRecordForPath(String path) { 353 synchronized (mLock) { 354 for (int i = 0; i < mVolumes.size(); i++) { 355 final VolumeInfo vol = mVolumes.valueAt(i); 356 if (vol.path != null && path.startsWith(vol.path)) { 357 return mRecords.get(vol.fsUuid); 358 } 359 } 360 } 361 return null; 362 } 363 364 private String scrubPath(String path) { 365 if (path.startsWith(Environment.getDataDirectory().getAbsolutePath())) { 366 return "internal"; 367 } 368 final VolumeRecord rec = findRecordForPath(path); 369 if (rec == null || rec.createdMillis == 0) { 370 return "unknown"; 371 } else { 372 return "ext:" + (int) ((System.currentTimeMillis() - rec.createdMillis) 373 / DateUtils.WEEK_IN_MILLIS) + "w"; 374 } 375 } 376 377 private @Nullable VolumeInfo findStorageForUuid(String volumeUuid) { 378 final StorageManager storage = mContext.getSystemService(StorageManager.class); 379 if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) { 380 return storage.findVolumeById(VolumeInfo.ID_EMULATED_INTERNAL); 381 } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) { 382 return storage.getPrimaryPhysicalVolume(); 383 } else { 384 return storage.findEmulatedForPrivate(storage.findVolumeByUuid(volumeUuid)); 385 } 386 } 387 388 private boolean shouldBenchmark() { 389 final long benchInterval = Settings.Global.getLong(mContext.getContentResolver(), 390 Settings.Global.STORAGE_BENCHMARK_INTERVAL, DateUtils.WEEK_IN_MILLIS); 391 if (benchInterval == -1) { 392 return false; 393 } else if (benchInterval == 0) { 394 return true; 395 } 396 397 synchronized (mLock) { 398 for (int i = 0; i < mVolumes.size(); i++) { 399 final VolumeInfo vol = mVolumes.valueAt(i); 400 final VolumeRecord rec = mRecords.get(vol.fsUuid); 401 if (vol.isMountedWritable() && rec != null) { 402 final long benchAge = System.currentTimeMillis() - rec.lastBenchMillis; 403 if (benchAge >= benchInterval) { 404 return true; 405 } 406 } 407 } 408 return false; 409 } 410 } 411 412 private CountDownLatch findOrCreateDiskScanLatch(String diskId) { 413 synchronized (mLock) { 414 CountDownLatch latch = mDiskScanLatches.get(diskId); 415 if (latch == null) { 416 latch = new CountDownLatch(1); 417 mDiskScanLatches.put(diskId, latch); 418 } 419 return latch; 420 } 421 } 422 423 private static String escapeNull(String arg) { 424 if (TextUtils.isEmpty(arg)) { 425 return "!"; 426 } else { 427 if (arg.indexOf('\0') != -1 || arg.indexOf(' ') != -1) { 428 throw new IllegalArgumentException(arg); 429 } 430 return arg; 431 } 432 } 433 434 /** List of crypto types. 435 * These must match CRYPT_TYPE_XXX in cryptfs.h AND their 436 * corresponding commands in CommandListener.cpp */ 437 public static final String[] CRYPTO_TYPES 438 = { "password", "default", "pattern", "pin" }; 439 440 private final Context mContext; 441 442 private final NativeDaemonConnector mConnector; 443 private final NativeDaemonConnector mCryptConnector; 444 445 private final Thread mConnectorThread; 446 private final Thread mCryptConnectorThread; 447 448 private volatile boolean mSystemReady = false; 449 private volatile boolean mBootCompleted = false; 450 private volatile boolean mDaemonConnected = false; 451 452 private PackageManagerService mPms; 453 454 private final Callbacks mCallbacks; 455 private final LockPatternUtils mLockPatternUtils; 456 457 // Two connectors - mConnector & mCryptConnector 458 private final CountDownLatch mConnectedSignal = new CountDownLatch(2); 459 private final CountDownLatch mAsecsScanned = new CountDownLatch(1); 460 461 private final Object mUnmountLock = new Object(); 462 @GuardedBy("mUnmountLock") 463 private CountDownLatch mUnmountSignal; 464 465 /** 466 * Private hash of currently mounted secure containers. 467 * Used as a lock in methods to manipulate secure containers. 468 */ 469 final private HashSet<String> mAsecMountSet = new HashSet<String>(); 470 471 /** 472 * The size of the crypto algorithm key in bits for OBB files. Currently 473 * Twofish is used which takes 128-bit keys. 474 */ 475 private static final int CRYPTO_ALGORITHM_KEY_SIZE = 128; 476 477 /** 478 * The number of times to run SHA1 in the PBKDF2 function for OBB files. 479 * 1024 is reasonably secure and not too slow. 480 */ 481 private static final int PBKDF2_HASH_ROUNDS = 1024; 482 483 /** 484 * Mounted OBB tracking information. Used to track the current state of all 485 * OBBs. 486 */ 487 final private Map<IBinder, List<ObbState>> mObbMounts = new HashMap<IBinder, List<ObbState>>(); 488 489 /** Map from raw paths to {@link ObbState}. */ 490 final private Map<String, ObbState> mObbPathToStateMap = new HashMap<String, ObbState>(); 491 492 // Not guarded by a lock. 493 private final MountServiceInternalImpl mMountServiceInternal = new MountServiceInternalImpl(); 494 495 class ObbState implements IBinder.DeathRecipient { 496 public ObbState(String rawPath, String canonicalPath, int callingUid, 497 IObbActionListener token, int nonce) { 498 this.rawPath = rawPath; 499 this.canonicalPath = canonicalPath; 500 501 this.ownerGid = UserHandle.getSharedAppGid(callingUid); 502 this.token = token; 503 this.nonce = nonce; 504 } 505 506 final String rawPath; 507 final String canonicalPath; 508 509 final int ownerGid; 510 511 // Token of remote Binder caller 512 final IObbActionListener token; 513 514 // Identifier to pass back to the token 515 final int nonce; 516 517 public IBinder getBinder() { 518 return token.asBinder(); 519 } 520 521 @Override 522 public void binderDied() { 523 ObbAction action = new UnmountObbAction(this, true); 524 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action)); 525 } 526 527 public void link() throws RemoteException { 528 getBinder().linkToDeath(this, 0); 529 } 530 531 public void unlink() { 532 getBinder().unlinkToDeath(this, 0); 533 } 534 535 @Override 536 public String toString() { 537 StringBuilder sb = new StringBuilder("ObbState{"); 538 sb.append("rawPath=").append(rawPath); 539 sb.append(",canonicalPath=").append(canonicalPath); 540 sb.append(",ownerGid=").append(ownerGid); 541 sb.append(",token=").append(token); 542 sb.append(",binder=").append(getBinder()); 543 sb.append('}'); 544 return sb.toString(); 545 } 546 } 547 548 // OBB Action Handler 549 final private ObbActionHandler mObbActionHandler; 550 551 // OBB action handler messages 552 private static final int OBB_RUN_ACTION = 1; 553 private static final int OBB_MCS_BOUND = 2; 554 private static final int OBB_MCS_UNBIND = 3; 555 private static final int OBB_MCS_RECONNECT = 4; 556 private static final int OBB_FLUSH_MOUNT_STATE = 5; 557 558 /* 559 * Default Container Service information 560 */ 561 static final ComponentName DEFAULT_CONTAINER_COMPONENT = new ComponentName( 562 "com.android.defcontainer", "com.android.defcontainer.DefaultContainerService"); 563 564 final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection(); 565 566 class DefaultContainerConnection implements ServiceConnection { 567 @Override 568 public void onServiceConnected(ComponentName name, IBinder service) { 569 if (DEBUG_OBB) 570 Slog.i(TAG, "onServiceConnected"); 571 IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service); 572 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_MCS_BOUND, imcs)); 573 } 574 575 @Override 576 public void onServiceDisconnected(ComponentName name) { 577 if (DEBUG_OBB) 578 Slog.i(TAG, "onServiceDisconnected"); 579 } 580 }; 581 582 // Used in the ObbActionHandler 583 private IMediaContainerService mContainerService = null; 584 585 // Last fstrim operation tracking 586 private static final String LAST_FSTRIM_FILE = "last-fstrim"; 587 private final File mLastMaintenanceFile; 588 private long mLastMaintenance; 589 590 // Handler messages 591 private static final int H_SYSTEM_READY = 1; 592 private static final int H_DAEMON_CONNECTED = 2; 593 private static final int H_SHUTDOWN = 3; 594 private static final int H_FSTRIM = 4; 595 private static final int H_VOLUME_MOUNT = 5; 596 private static final int H_VOLUME_BROADCAST = 6; 597 private static final int H_INTERNAL_BROADCAST = 7; 598 private static final int H_VOLUME_UNMOUNT = 8; 599 private static final int H_PARTITION_FORGET = 9; 600 private static final int H_RESET = 10; 601 602 class MountServiceHandler extends Handler { 603 public MountServiceHandler(Looper looper) { 604 super(looper); 605 } 606 607 @Override 608 public void handleMessage(Message msg) { 609 switch (msg.what) { 610 case H_SYSTEM_READY: { 611 handleSystemReady(); 612 break; 613 } 614 case H_DAEMON_CONNECTED: { 615 handleDaemonConnected(); 616 break; 617 } 618 case H_FSTRIM: { 619 if (!isReady()) { 620 Slog.i(TAG, "fstrim requested, but no daemon connection yet; trying again"); 621 sendMessageDelayed(obtainMessage(H_FSTRIM, msg.obj), 622 DateUtils.SECOND_IN_MILLIS); 623 break; 624 } 625 626 Slog.i(TAG, "Running fstrim idle maintenance"); 627 628 // Remember when we kicked it off 629 try { 630 mLastMaintenance = System.currentTimeMillis(); 631 mLastMaintenanceFile.setLastModified(mLastMaintenance); 632 } catch (Exception e) { 633 Slog.e(TAG, "Unable to record last fstrim!"); 634 } 635 636 final boolean shouldBenchmark = shouldBenchmark(); 637 try { 638 // This method must be run on the main (handler) thread, 639 // so it is safe to directly call into vold. 640 mConnector.execute("fstrim", shouldBenchmark ? "dotrimbench" : "dotrim"); 641 } catch (NativeDaemonConnectorException ndce) { 642 Slog.e(TAG, "Failed to run fstrim!"); 643 } 644 645 // invoke the completion callback, if any 646 // TODO: fstrim is non-blocking, so remove this useless callback 647 Runnable callback = (Runnable) msg.obj; 648 if (callback != null) { 649 callback.run(); 650 } 651 break; 652 } 653 case H_SHUTDOWN: { 654 final IMountShutdownObserver obs = (IMountShutdownObserver) msg.obj; 655 boolean success = false; 656 try { 657 success = mConnector.execute("volume", "shutdown").isClassOk(); 658 } catch (NativeDaemonConnectorException ignored) { 659 } 660 if (obs != null) { 661 try { 662 obs.onShutDownComplete(success ? 0 : -1); 663 } catch (RemoteException ignored) { 664 } 665 } 666 break; 667 } 668 case H_VOLUME_MOUNT: { 669 final VolumeInfo vol = (VolumeInfo) msg.obj; 670 if (isMountDisallowed(vol)) { 671 Slog.i(TAG, "Ignoring mount " + vol.getId() + " due to policy"); 672 break; 673 } 674 try { 675 mConnector.execute("volume", "mount", vol.id, vol.mountFlags, 676 vol.mountUserId); 677 } catch (NativeDaemonConnectorException ignored) { 678 } 679 break; 680 } 681 case H_VOLUME_UNMOUNT: { 682 final VolumeInfo vol = (VolumeInfo) msg.obj; 683 unmount(vol.getId()); 684 break; 685 } 686 case H_VOLUME_BROADCAST: { 687 final StorageVolume userVol = (StorageVolume) msg.obj; 688 final String envState = userVol.getState(); 689 Slog.d(TAG, "Volume " + userVol.getId() + " broadcasting " + envState + " to " 690 + userVol.getOwner()); 691 692 final String action = VolumeInfo.getBroadcastForEnvironment(envState); 693 if (action != null) { 694 final Intent intent = new Intent(action, 695 Uri.fromFile(userVol.getPathFile())); 696 intent.putExtra(StorageVolume.EXTRA_STORAGE_VOLUME, userVol); 697 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT); 698 mContext.sendBroadcastAsUser(intent, userVol.getOwner()); 699 } 700 break; 701 } 702 case H_INTERNAL_BROADCAST: { 703 // Internal broadcasts aimed at system components, not for 704 // third-party apps. 705 final Intent intent = (Intent) msg.obj; 706 mContext.sendBroadcastAsUser(intent, UserHandle.ALL, 707 android.Manifest.permission.WRITE_MEDIA_STORAGE); 708 break; 709 } 710 case H_PARTITION_FORGET: { 711 final String partGuid = (String) msg.obj; 712 forgetPartition(partGuid); 713 break; 714 } 715 case H_RESET: { 716 resetIfReadyAndConnected(); 717 break; 718 } 719 } 720 } 721 } 722 723 private final Handler mHandler; 724 725 private BroadcastReceiver mUserReceiver = new BroadcastReceiver() { 726 @Override 727 public void onReceive(Context context, Intent intent) { 728 final String action = intent.getAction(); 729 final int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 730 Preconditions.checkArgument(userId >= 0); 731 732 try { 733 if (Intent.ACTION_USER_ADDED.equals(action)) { 734 final UserManager um = mContext.getSystemService(UserManager.class); 735 final int userSerialNumber = um.getUserSerialNumber(userId); 736 mConnector.execute("volume", "user_added", userId, userSerialNumber); 737 } else if (Intent.ACTION_USER_REMOVED.equals(action)) { 738 synchronized (mVolumes) { 739 final int size = mVolumes.size(); 740 for (int i = 0; i < size; i++) { 741 final VolumeInfo vol = mVolumes.valueAt(i); 742 if (vol.mountUserId == userId) { 743 vol.mountUserId = UserHandle.USER_NULL; 744 mHandler.obtainMessage(H_VOLUME_UNMOUNT, vol).sendToTarget(); 745 } 746 } 747 } 748 mConnector.execute("volume", "user_removed", userId); 749 } 750 } catch (NativeDaemonConnectorException e) { 751 Slog.w(TAG, "Failed to send user details to vold", e); 752 } 753 } 754 }; 755 756 @Override 757 public void waitForAsecScan() { 758 waitForLatch(mAsecsScanned, "mAsecsScanned"); 759 } 760 761 private void waitForReady() { 762 waitForLatch(mConnectedSignal, "mConnectedSignal"); 763 } 764 765 private void waitForLatch(CountDownLatch latch, String condition) { 766 try { 767 waitForLatch(latch, condition, -1); 768 } catch (TimeoutException ignored) { 769 } 770 } 771 772 private void waitForLatch(CountDownLatch latch, String condition, long timeoutMillis) 773 throws TimeoutException { 774 final long startMillis = SystemClock.elapsedRealtime(); 775 while (true) { 776 try { 777 if (latch.await(5000, TimeUnit.MILLISECONDS)) { 778 return; 779 } else { 780 Slog.w(TAG, "Thread " + Thread.currentThread().getName() 781 + " still waiting for " + condition + "..."); 782 } 783 } catch (InterruptedException e) { 784 Slog.w(TAG, "Interrupt while waiting for " + condition); 785 } 786 if (timeoutMillis > 0 && SystemClock.elapsedRealtime() > startMillis + timeoutMillis) { 787 throw new TimeoutException("Thread " + Thread.currentThread().getName() 788 + " gave up waiting for " + condition + " after " + timeoutMillis + "ms"); 789 } 790 } 791 } 792 793 private boolean isReady() { 794 try { 795 return mConnectedSignal.await(0, TimeUnit.MILLISECONDS); 796 } catch (InterruptedException e) { 797 return false; 798 } 799 } 800 801 private void handleSystemReady() { 802 initIfReadyAndConnected(); 803 resetIfReadyAndConnected(); 804 805 // Start scheduling nominally-daily fstrim operations 806 MountServiceIdler.scheduleIdlePass(mContext); 807 } 808 809 /** 810 * MediaProvider has a ton of code that makes assumptions about storage 811 * paths never changing, so we outright kill them to pick up new state. 812 */ 813 @Deprecated 814 private void killMediaProvider(List<UserInfo> users) { 815 if (users == null) return; 816 817 final long token = Binder.clearCallingIdentity(); 818 try { 819 for (UserInfo user : users) { 820 // System user does not have media provider, so skip. 821 if (user.isSystemOnly()) continue; 822 823 final ProviderInfo provider = mPms.resolveContentProvider(MediaStore.AUTHORITY, 824 PackageManager.MATCH_DIRECT_BOOT_AWARE 825 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 826 user.id); 827 if (provider != null) { 828 final IActivityManager am = ActivityManagerNative.getDefault(); 829 try { 830 am.killApplication(provider.applicationInfo.packageName, 831 UserHandle.getAppId(provider.applicationInfo.uid), 832 UserHandle.USER_ALL, "vold reset"); 833 // We only need to run this once. It will kill all users' media processes. 834 break; 835 } catch (RemoteException e) { 836 } 837 } 838 } 839 } finally { 840 Binder.restoreCallingIdentity(token); 841 } 842 } 843 844 private void addInternalVolumeLocked() { 845 // Create a stub volume that represents internal storage 846 final VolumeInfo internal = new VolumeInfo(VolumeInfo.ID_PRIVATE_INTERNAL, 847 VolumeInfo.TYPE_PRIVATE, null, null); 848 internal.state = VolumeInfo.STATE_MOUNTED; 849 internal.path = Environment.getDataDirectory().getAbsolutePath(); 850 mVolumes.put(internal.id, internal); 851 } 852 853 private void initIfReadyAndConnected() { 854 Slog.d(TAG, "Thinking about init, mSystemReady=" + mSystemReady 855 + ", mDaemonConnected=" + mDaemonConnected); 856 if (mSystemReady && mDaemonConnected 857 && !StorageManager.isFileEncryptedNativeOnly()) { 858 // When booting a device without native support, make sure that our 859 // user directories are locked or unlocked based on the current 860 // emulation status. 861 final boolean initLocked = StorageManager.isFileEncryptedEmulatedOnly(); 862 Slog.d(TAG, "Setting up emulation state, initlocked=" + initLocked); 863 final List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers(); 864 for (UserInfo user : users) { 865 try { 866 if (initLocked) { 867 mCryptConnector.execute("cryptfs", "lock_user_key", user.id); 868 } else { 869 mCryptConnector.execute("cryptfs", "unlock_user_key", user.id, 870 user.serialNumber, "!", "!"); 871 } 872 } catch (NativeDaemonConnectorException e) { 873 Slog.w(TAG, "Failed to init vold", e); 874 } 875 } 876 } 877 } 878 879 private void resetIfReadyAndConnected() { 880 Slog.d(TAG, "Thinking about reset, mSystemReady=" + mSystemReady 881 + ", mDaemonConnected=" + mDaemonConnected); 882 if (mSystemReady && mDaemonConnected) { 883 final List<UserInfo> users = mContext.getSystemService(UserManager.class).getUsers(); 884 killMediaProvider(users); 885 886 final int[] systemUnlockedUsers; 887 synchronized (mLock) { 888 systemUnlockedUsers = mSystemUnlockedUsers; 889 890 mDisks.clear(); 891 mVolumes.clear(); 892 893 addInternalVolumeLocked(); 894 } 895 896 try { 897 mConnector.execute("volume", "reset"); 898 899 // Tell vold about all existing and started users 900 for (UserInfo user : users) { 901 mConnector.execute("volume", "user_added", user.id, user.serialNumber); 902 } 903 for (int userId : systemUnlockedUsers) { 904 mConnector.execute("volume", "user_started", userId); 905 } 906 } catch (NativeDaemonConnectorException e) { 907 Slog.w(TAG, "Failed to reset vold", e); 908 } 909 } 910 } 911 912 private void onUnlockUser(int userId) { 913 Slog.d(TAG, "onUnlockUser " + userId); 914 915 // We purposefully block here to make sure that user-specific 916 // staging area is ready so it's ready for zygote-forked apps to 917 // bind mount against. 918 try { 919 mConnector.execute("volume", "user_started", userId); 920 } catch (NativeDaemonConnectorException ignored) { 921 } 922 923 // Record user as started so newly mounted volumes kick off events 924 // correctly, then synthesize events for any already-mounted volumes. 925 synchronized (mVolumes) { 926 for (int i = 0; i < mVolumes.size(); i++) { 927 final VolumeInfo vol = mVolumes.valueAt(i); 928 if (vol.isVisibleForRead(userId) && vol.isMountedReadable()) { 929 final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, false); 930 mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget(); 931 932 final String envState = VolumeInfo.getEnvironmentForState(vol.getState()); 933 mCallbacks.notifyStorageStateChanged(userVol.getPath(), envState, envState); 934 } 935 } 936 mSystemUnlockedUsers = ArrayUtils.appendInt(mSystemUnlockedUsers, userId); 937 } 938 } 939 940 private void onCleanupUser(int userId) { 941 Slog.d(TAG, "onCleanupUser " + userId); 942 943 try { 944 mConnector.execute("volume", "user_stopped", userId); 945 } catch (NativeDaemonConnectorException ignored) { 946 } 947 948 synchronized (mVolumes) { 949 mSystemUnlockedUsers = ArrayUtils.removeInt(mSystemUnlockedUsers, userId); 950 } 951 } 952 953 void runIdleMaintenance(Runnable callback) { 954 mHandler.sendMessage(mHandler.obtainMessage(H_FSTRIM, callback)); 955 } 956 957 // Binder entry point for kicking off an immediate fstrim 958 @Override 959 public void runMaintenance() { 960 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); 961 runIdleMaintenance(null); 962 } 963 964 @Override 965 public long lastMaintenance() { 966 return mLastMaintenance; 967 } 968 969 /** 970 * Callback from NativeDaemonConnector 971 */ 972 @Override 973 public void onDaemonConnected() { 974 mDaemonConnected = true; 975 mHandler.obtainMessage(H_DAEMON_CONNECTED).sendToTarget(); 976 } 977 978 private void handleDaemonConnected() { 979 initIfReadyAndConnected(); 980 resetIfReadyAndConnected(); 981 982 /* 983 * Now that we've done our initialization, release 984 * the hounds! 985 */ 986 mConnectedSignal.countDown(); 987 if (mConnectedSignal.getCount() != 0) { 988 // More daemons need to connect 989 return; 990 } 991 992 // On an encrypted device we can't see system properties yet, so pull 993 // the system locale out of the mount service. 994 if ("".equals(SystemProperties.get("vold.encrypt_progress"))) { 995 copyLocaleFromMountService(); 996 } 997 998 // Let package manager load internal ASECs. 999 mPms.scanAvailableAsecs(); 1000 1001 // Notify people waiting for ASECs to be scanned that it's done. 1002 mAsecsScanned.countDown(); 1003 } 1004 1005 private void copyLocaleFromMountService() { 1006 String systemLocale; 1007 try { 1008 systemLocale = getField(StorageManager.SYSTEM_LOCALE_KEY); 1009 } catch (RemoteException e) { 1010 return; 1011 } 1012 if (TextUtils.isEmpty(systemLocale)) { 1013 return; 1014 } 1015 1016 Slog.d(TAG, "Got locale " + systemLocale + " from mount service"); 1017 Locale locale = Locale.forLanguageTag(systemLocale); 1018 Configuration config = new Configuration(); 1019 config.setLocale(locale); 1020 try { 1021 ActivityManagerNative.getDefault().updatePersistentConfiguration(config); 1022 } catch (RemoteException e) { 1023 Slog.e(TAG, "Error setting system locale from mount service", e); 1024 } 1025 1026 // Temporary workaround for http://b/17945169. 1027 Slog.d(TAG, "Setting system properties to " + systemLocale + " from mount service"); 1028 SystemProperties.set("persist.sys.locale", locale.toLanguageTag()); 1029 } 1030 1031 /** 1032 * Callback from NativeDaemonConnector 1033 */ 1034 @Override 1035 public boolean onCheckHoldWakeLock(int code) { 1036 return false; 1037 } 1038 1039 /** 1040 * Callback from NativeDaemonConnector 1041 */ 1042 @Override 1043 public boolean onEvent(int code, String raw, String[] cooked) { 1044 synchronized (mLock) { 1045 return onEventLocked(code, raw, cooked); 1046 } 1047 } 1048 1049 private boolean onEventLocked(int code, String raw, String[] cooked) { 1050 switch (code) { 1051 case VoldResponseCode.DISK_CREATED: { 1052 if (cooked.length != 3) break; 1053 final String id = cooked[1]; 1054 int flags = Integer.parseInt(cooked[2]); 1055 if (SystemProperties.getBoolean(StorageManager.PROP_FORCE_ADOPTABLE, false) 1056 || mForceAdoptable) { 1057 flags |= DiskInfo.FLAG_ADOPTABLE; 1058 } 1059 mDisks.put(id, new DiskInfo(id, flags)); 1060 break; 1061 } 1062 case VoldResponseCode.DISK_SIZE_CHANGED: { 1063 if (cooked.length != 3) break; 1064 final DiskInfo disk = mDisks.get(cooked[1]); 1065 if (disk != null) { 1066 disk.size = Long.parseLong(cooked[2]); 1067 } 1068 break; 1069 } 1070 case VoldResponseCode.DISK_LABEL_CHANGED: { 1071 final DiskInfo disk = mDisks.get(cooked[1]); 1072 if (disk != null) { 1073 final StringBuilder builder = new StringBuilder(); 1074 for (int i = 2; i < cooked.length; i++) { 1075 builder.append(cooked[i]).append(' '); 1076 } 1077 disk.label = builder.toString().trim(); 1078 } 1079 break; 1080 } 1081 case VoldResponseCode.DISK_SCANNED: { 1082 if (cooked.length != 2) break; 1083 final DiskInfo disk = mDisks.get(cooked[1]); 1084 if (disk != null) { 1085 onDiskScannedLocked(disk); 1086 } 1087 break; 1088 } 1089 case VoldResponseCode.DISK_SYS_PATH_CHANGED: { 1090 if (cooked.length != 3) break; 1091 final DiskInfo disk = mDisks.get(cooked[1]); 1092 if (disk != null) { 1093 disk.sysPath = cooked[2]; 1094 } 1095 break; 1096 } 1097 case VoldResponseCode.DISK_DESTROYED: { 1098 if (cooked.length != 2) break; 1099 final DiskInfo disk = mDisks.remove(cooked[1]); 1100 if (disk != null) { 1101 mCallbacks.notifyDiskDestroyed(disk); 1102 } 1103 break; 1104 } 1105 1106 case VoldResponseCode.VOLUME_CREATED: { 1107 final String id = cooked[1]; 1108 final int type = Integer.parseInt(cooked[2]); 1109 final String diskId = TextUtils.nullIfEmpty(cooked[3]); 1110 final String partGuid = TextUtils.nullIfEmpty(cooked[4]); 1111 1112 final DiskInfo disk = mDisks.get(diskId); 1113 final VolumeInfo vol = new VolumeInfo(id, type, disk, partGuid); 1114 mVolumes.put(id, vol); 1115 onVolumeCreatedLocked(vol); 1116 break; 1117 } 1118 case VoldResponseCode.VOLUME_STATE_CHANGED: { 1119 if (cooked.length != 3) break; 1120 final VolumeInfo vol = mVolumes.get(cooked[1]); 1121 if (vol != null) { 1122 final int oldState = vol.state; 1123 final int newState = Integer.parseInt(cooked[2]); 1124 vol.state = newState; 1125 onVolumeStateChangedLocked(vol, oldState, newState); 1126 } 1127 break; 1128 } 1129 case VoldResponseCode.VOLUME_FS_TYPE_CHANGED: { 1130 if (cooked.length != 3) break; 1131 final VolumeInfo vol = mVolumes.get(cooked[1]); 1132 if (vol != null) { 1133 vol.fsType = cooked[2]; 1134 } 1135 break; 1136 } 1137 case VoldResponseCode.VOLUME_FS_UUID_CHANGED: { 1138 if (cooked.length != 3) break; 1139 final VolumeInfo vol = mVolumes.get(cooked[1]); 1140 if (vol != null) { 1141 vol.fsUuid = cooked[2]; 1142 } 1143 break; 1144 } 1145 case VoldResponseCode.VOLUME_FS_LABEL_CHANGED: { 1146 final VolumeInfo vol = mVolumes.get(cooked[1]); 1147 if (vol != null) { 1148 final StringBuilder builder = new StringBuilder(); 1149 for (int i = 2; i < cooked.length; i++) { 1150 builder.append(cooked[i]).append(' '); 1151 } 1152 vol.fsLabel = builder.toString().trim(); 1153 } 1154 // TODO: notify listeners that label changed 1155 break; 1156 } 1157 case VoldResponseCode.VOLUME_PATH_CHANGED: { 1158 if (cooked.length != 3) break; 1159 final VolumeInfo vol = mVolumes.get(cooked[1]); 1160 if (vol != null) { 1161 vol.path = cooked[2]; 1162 } 1163 break; 1164 } 1165 case VoldResponseCode.VOLUME_INTERNAL_PATH_CHANGED: { 1166 if (cooked.length != 3) break; 1167 final VolumeInfo vol = mVolumes.get(cooked[1]); 1168 if (vol != null) { 1169 vol.internalPath = cooked[2]; 1170 } 1171 break; 1172 } 1173 case VoldResponseCode.VOLUME_DESTROYED: { 1174 if (cooked.length != 2) break; 1175 mVolumes.remove(cooked[1]); 1176 break; 1177 } 1178 1179 case VoldResponseCode.MOVE_STATUS: { 1180 final int status = Integer.parseInt(cooked[1]); 1181 onMoveStatusLocked(status); 1182 break; 1183 } 1184 case VoldResponseCode.BENCHMARK_RESULT: { 1185 if (cooked.length != 7) break; 1186 final String path = cooked[1]; 1187 final String ident = cooked[2]; 1188 final long create = Long.parseLong(cooked[3]); 1189 final long drop = Long.parseLong(cooked[4]); 1190 final long run = Long.parseLong(cooked[5]); 1191 final long destroy = Long.parseLong(cooked[6]); 1192 1193 final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class); 1194 dropBox.addText(TAG_STORAGE_BENCHMARK, scrubPath(path) 1195 + " " + ident + " " + create + " " + run + " " + destroy); 1196 1197 final VolumeRecord rec = findRecordForPath(path); 1198 if (rec != null) { 1199 rec.lastBenchMillis = System.currentTimeMillis(); 1200 writeSettingsLocked(); 1201 } 1202 1203 break; 1204 } 1205 case VoldResponseCode.TRIM_RESULT: { 1206 if (cooked.length != 4) break; 1207 final String path = cooked[1]; 1208 final long bytes = Long.parseLong(cooked[2]); 1209 final long time = Long.parseLong(cooked[3]); 1210 1211 final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class); 1212 dropBox.addText(TAG_STORAGE_TRIM, scrubPath(path) 1213 + " " + bytes + " " + time); 1214 1215 final VolumeRecord rec = findRecordForPath(path); 1216 if (rec != null) { 1217 rec.lastTrimMillis = System.currentTimeMillis(); 1218 writeSettingsLocked(); 1219 } 1220 1221 break; 1222 } 1223 1224 default: { 1225 Slog.d(TAG, "Unhandled vold event " + code); 1226 } 1227 } 1228 1229 return true; 1230 } 1231 1232 private void onDiskScannedLocked(DiskInfo disk) { 1233 int volumeCount = 0; 1234 for (int i = 0; i < mVolumes.size(); i++) { 1235 final VolumeInfo vol = mVolumes.valueAt(i); 1236 if (Objects.equals(disk.id, vol.getDiskId())) { 1237 volumeCount++; 1238 } 1239 } 1240 1241 final Intent intent = new Intent(DiskInfo.ACTION_DISK_SCANNED); 1242 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 1243 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 1244 intent.putExtra(DiskInfo.EXTRA_DISK_ID, disk.id); 1245 intent.putExtra(DiskInfo.EXTRA_VOLUME_COUNT, volumeCount); 1246 mHandler.obtainMessage(H_INTERNAL_BROADCAST, intent).sendToTarget(); 1247 1248 final CountDownLatch latch = mDiskScanLatches.remove(disk.id); 1249 if (latch != null) { 1250 latch.countDown(); 1251 } 1252 1253 disk.volumeCount = volumeCount; 1254 mCallbacks.notifyDiskScanned(disk, volumeCount); 1255 } 1256 1257 private void onVolumeCreatedLocked(VolumeInfo vol) { 1258 if (mPms.isOnlyCoreApps()) { 1259 Slog.d(TAG, "System booted in core-only mode; ignoring volume " + vol.getId()); 1260 return; 1261 } 1262 1263 if (vol.type == VolumeInfo.TYPE_EMULATED) { 1264 final StorageManager storage = mContext.getSystemService(StorageManager.class); 1265 final VolumeInfo privateVol = storage.findPrivateForEmulated(vol); 1266 1267 if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid) 1268 && VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.id)) { 1269 Slog.v(TAG, "Found primary storage at " + vol); 1270 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY; 1271 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE; 1272 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget(); 1273 1274 } else if (Objects.equals(privateVol.fsUuid, mPrimaryStorageUuid)) { 1275 Slog.v(TAG, "Found primary storage at " + vol); 1276 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY; 1277 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE; 1278 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget(); 1279 } 1280 1281 } else if (vol.type == VolumeInfo.TYPE_PUBLIC) { 1282 // TODO: only look at first public partition 1283 if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, mPrimaryStorageUuid) 1284 && vol.disk.isDefaultPrimary()) { 1285 Slog.v(TAG, "Found primary storage at " + vol); 1286 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY; 1287 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE; 1288 } 1289 1290 // Adoptable public disks are visible to apps, since they meet 1291 // public API requirement of being in a stable location. 1292 if (vol.disk.isAdoptable()) { 1293 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE; 1294 } 1295 1296 vol.mountUserId = mCurrentUserId; 1297 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget(); 1298 1299 } else if (vol.type == VolumeInfo.TYPE_PRIVATE) { 1300 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget(); 1301 1302 } else { 1303 Slog.d(TAG, "Skipping automatic mounting of " + vol); 1304 } 1305 } 1306 1307 private boolean isBroadcastWorthy(VolumeInfo vol) { 1308 switch (vol.getType()) { 1309 case VolumeInfo.TYPE_PRIVATE: 1310 case VolumeInfo.TYPE_PUBLIC: 1311 case VolumeInfo.TYPE_EMULATED: 1312 break; 1313 default: 1314 return false; 1315 } 1316 1317 switch (vol.getState()) { 1318 case VolumeInfo.STATE_MOUNTED: 1319 case VolumeInfo.STATE_MOUNTED_READ_ONLY: 1320 case VolumeInfo.STATE_EJECTING: 1321 case VolumeInfo.STATE_UNMOUNTED: 1322 case VolumeInfo.STATE_UNMOUNTABLE: 1323 case VolumeInfo.STATE_BAD_REMOVAL: 1324 break; 1325 default: 1326 return false; 1327 } 1328 1329 return true; 1330 } 1331 1332 private void onVolumeStateChangedLocked(VolumeInfo vol, int oldState, int newState) { 1333 // Remember that we saw this volume so we're ready to accept user 1334 // metadata, or so we can annoy them when a private volume is ejected 1335 if (vol.isMountedReadable() && !TextUtils.isEmpty(vol.fsUuid)) { 1336 VolumeRecord rec = mRecords.get(vol.fsUuid); 1337 if (rec == null) { 1338 rec = new VolumeRecord(vol.type, vol.fsUuid); 1339 rec.partGuid = vol.partGuid; 1340 rec.createdMillis = System.currentTimeMillis(); 1341 if (vol.type == VolumeInfo.TYPE_PRIVATE) { 1342 rec.nickname = vol.disk.getDescription(); 1343 } 1344 mRecords.put(rec.fsUuid, rec); 1345 writeSettingsLocked(); 1346 } else { 1347 // Handle upgrade case where we didn't store partition GUID 1348 if (TextUtils.isEmpty(rec.partGuid)) { 1349 rec.partGuid = vol.partGuid; 1350 writeSettingsLocked(); 1351 } 1352 } 1353 } 1354 1355 mCallbacks.notifyVolumeStateChanged(vol, oldState, newState); 1356 1357 // Do not broadcast before boot has completed to avoid launching the 1358 // processes that receive the intent unnecessarily. 1359 if (mBootCompleted && isBroadcastWorthy(vol)) { 1360 final Intent intent = new Intent(VolumeInfo.ACTION_VOLUME_STATE_CHANGED); 1361 intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.id); 1362 intent.putExtra(VolumeInfo.EXTRA_VOLUME_STATE, newState); 1363 intent.putExtra(VolumeRecord.EXTRA_FS_UUID, vol.fsUuid); 1364 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 1365 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 1366 mHandler.obtainMessage(H_INTERNAL_BROADCAST, intent).sendToTarget(); 1367 } 1368 1369 final String oldStateEnv = VolumeInfo.getEnvironmentForState(oldState); 1370 final String newStateEnv = VolumeInfo.getEnvironmentForState(newState); 1371 1372 if (!Objects.equals(oldStateEnv, newStateEnv)) { 1373 // Kick state changed event towards all started users. Any users 1374 // started after this point will trigger additional 1375 // user-specific broadcasts. 1376 for (int userId : mSystemUnlockedUsers) { 1377 if (vol.isVisibleForRead(userId)) { 1378 final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, false); 1379 mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget(); 1380 1381 mCallbacks.notifyStorageStateChanged(userVol.getPath(), oldStateEnv, 1382 newStateEnv); 1383 } 1384 } 1385 } 1386 1387 if (vol.type == VolumeInfo.TYPE_PUBLIC && vol.state == VolumeInfo.STATE_EJECTING) { 1388 // TODO: this should eventually be handled by new ObbVolume state changes 1389 /* 1390 * Some OBBs might have been unmounted when this volume was 1391 * unmounted, so send a message to the handler to let it know to 1392 * remove those from the list of mounted OBBS. 1393 */ 1394 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage( 1395 OBB_FLUSH_MOUNT_STATE, vol.path)); 1396 } 1397 } 1398 1399 private void onMoveStatusLocked(int status) { 1400 if (mMoveCallback == null) { 1401 Slog.w(TAG, "Odd, status but no move requested"); 1402 return; 1403 } 1404 1405 // TODO: estimate remaining time 1406 try { 1407 mMoveCallback.onStatusChanged(-1, status, -1); 1408 } catch (RemoteException ignored) { 1409 } 1410 1411 // We've finished copying and we're about to clean up old data, so 1412 // remember that move was successful if we get rebooted 1413 if (status == MOVE_STATUS_COPY_FINISHED) { 1414 Slog.d(TAG, "Move to " + mMoveTargetUuid + " copy phase finshed; persisting"); 1415 1416 mPrimaryStorageUuid = mMoveTargetUuid; 1417 writeSettingsLocked(); 1418 } 1419 1420 if (PackageManager.isMoveStatusFinished(status)) { 1421 Slog.d(TAG, "Move to " + mMoveTargetUuid + " finished with status " + status); 1422 1423 mMoveCallback = null; 1424 mMoveTargetUuid = null; 1425 } 1426 } 1427 1428 private void enforcePermission(String perm) { 1429 mContext.enforceCallingOrSelfPermission(perm, perm); 1430 } 1431 1432 /** 1433 * Decide if volume is mountable per device policies. 1434 */ 1435 private boolean isMountDisallowed(VolumeInfo vol) { 1436 if (vol.type == VolumeInfo.TYPE_PUBLIC || vol.type == VolumeInfo.TYPE_PRIVATE) { 1437 final UserManager userManager = mContext.getSystemService(UserManager.class); 1438 return userManager.hasUserRestriction(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA, 1439 Binder.getCallingUserHandle()); 1440 } else { 1441 return false; 1442 } 1443 } 1444 1445 private void enforceAdminUser() { 1446 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 1447 final int callingUserId = UserHandle.getCallingUserId(); 1448 boolean isAdmin; 1449 long token = Binder.clearCallingIdentity(); 1450 try { 1451 isAdmin = um.getUserInfo(callingUserId).isAdmin(); 1452 } finally { 1453 Binder.restoreCallingIdentity(token); 1454 } 1455 if (!isAdmin) { 1456 throw new SecurityException("Only admin users can adopt sd cards"); 1457 } 1458 } 1459 1460 /** 1461 * Constructs a new MountService instance 1462 * 1463 * @param context Binder context for this service 1464 */ 1465 public MountService(Context context) { 1466 sSelf = this; 1467 1468 mContext = context; 1469 mCallbacks = new Callbacks(FgThread.get().getLooper()); 1470 mLockPatternUtils = new LockPatternUtils(mContext); 1471 1472 // XXX: This will go away soon in favor of IMountServiceObserver 1473 mPms = (PackageManagerService) ServiceManager.getService("package"); 1474 1475 HandlerThread hthread = new HandlerThread(TAG); 1476 hthread.start(); 1477 mHandler = new MountServiceHandler(hthread.getLooper()); 1478 1479 // Add OBB Action Handler to MountService thread. 1480 mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper()); 1481 1482 // Initialize the last-fstrim tracking if necessary 1483 File dataDir = Environment.getDataDirectory(); 1484 File systemDir = new File(dataDir, "system"); 1485 mLastMaintenanceFile = new File(systemDir, LAST_FSTRIM_FILE); 1486 if (!mLastMaintenanceFile.exists()) { 1487 // Not setting mLastMaintenance here means that we will force an 1488 // fstrim during reboot following the OTA that installs this code. 1489 try { 1490 (new FileOutputStream(mLastMaintenanceFile)).close(); 1491 } catch (IOException e) { 1492 Slog.e(TAG, "Unable to create fstrim record " + mLastMaintenanceFile.getPath()); 1493 } 1494 } else { 1495 mLastMaintenance = mLastMaintenanceFile.lastModified(); 1496 } 1497 1498 mSettingsFile = new AtomicFile( 1499 new File(Environment.getDataSystemDirectory(), "storage.xml")); 1500 1501 synchronized (mLock) { 1502 readSettingsLocked(); 1503 } 1504 1505 LocalServices.addService(MountServiceInternal.class, mMountServiceInternal); 1506 1507 /* 1508 * Create the connection to vold with a maximum queue of twice the 1509 * amount of containers we'd ever expect to have. This keeps an 1510 * "asec list" from blocking a thread repeatedly. 1511 */ 1512 1513 mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25, 1514 null); 1515 mConnector.setDebug(true); 1516 mConnector.setWarnIfHeld(mLock); 1517 mConnectorThread = new Thread(mConnector, VOLD_TAG); 1518 1519 // Reuse parameters from first connector since they are tested and safe 1520 mCryptConnector = new NativeDaemonConnector(this, "cryptd", 1521 MAX_CONTAINERS * 2, CRYPTD_TAG, 25, null); 1522 mCryptConnector.setDebug(true); 1523 mCryptConnectorThread = new Thread(mCryptConnector, CRYPTD_TAG); 1524 1525 final IntentFilter userFilter = new IntentFilter(); 1526 userFilter.addAction(Intent.ACTION_USER_ADDED); 1527 userFilter.addAction(Intent.ACTION_USER_REMOVED); 1528 mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler); 1529 1530 synchronized (mLock) { 1531 addInternalVolumeLocked(); 1532 } 1533 1534 // Add ourself to the Watchdog monitors if enabled. 1535 if (WATCHDOG_ENABLE) { 1536 Watchdog.getInstance().addMonitor(this); 1537 } 1538 } 1539 1540 private void start() { 1541 mConnectorThread.start(); 1542 mCryptConnectorThread.start(); 1543 } 1544 1545 private void systemReady() { 1546 mSystemReady = true; 1547 mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget(); 1548 } 1549 1550 private void bootCompleted() { 1551 mBootCompleted = true; 1552 } 1553 1554 private String getDefaultPrimaryStorageUuid() { 1555 if (SystemProperties.getBoolean(StorageManager.PROP_PRIMARY_PHYSICAL, false)) { 1556 return StorageManager.UUID_PRIMARY_PHYSICAL; 1557 } else { 1558 return StorageManager.UUID_PRIVATE_INTERNAL; 1559 } 1560 } 1561 1562 private void readSettingsLocked() { 1563 mRecords.clear(); 1564 mPrimaryStorageUuid = getDefaultPrimaryStorageUuid(); 1565 mForceAdoptable = false; 1566 1567 FileInputStream fis = null; 1568 try { 1569 fis = mSettingsFile.openRead(); 1570 final XmlPullParser in = Xml.newPullParser(); 1571 in.setInput(fis, StandardCharsets.UTF_8.name()); 1572 1573 int type; 1574 while ((type = in.next()) != END_DOCUMENT) { 1575 if (type == START_TAG) { 1576 final String tag = in.getName(); 1577 if (TAG_VOLUMES.equals(tag)) { 1578 final int version = readIntAttribute(in, ATTR_VERSION, VERSION_INIT); 1579 final boolean primaryPhysical = SystemProperties.getBoolean( 1580 StorageManager.PROP_PRIMARY_PHYSICAL, false); 1581 final boolean validAttr = (version >= VERSION_FIX_PRIMARY) 1582 || (version >= VERSION_ADD_PRIMARY && !primaryPhysical); 1583 if (validAttr) { 1584 mPrimaryStorageUuid = readStringAttribute(in, 1585 ATTR_PRIMARY_STORAGE_UUID); 1586 } 1587 mForceAdoptable = readBooleanAttribute(in, ATTR_FORCE_ADOPTABLE, false); 1588 1589 } else if (TAG_VOLUME.equals(tag)) { 1590 final VolumeRecord rec = readVolumeRecord(in); 1591 mRecords.put(rec.fsUuid, rec); 1592 } 1593 } 1594 } 1595 } catch (FileNotFoundException e) { 1596 // Missing metadata is okay, probably first boot 1597 } catch (IOException e) { 1598 Slog.wtf(TAG, "Failed reading metadata", e); 1599 } catch (XmlPullParserException e) { 1600 Slog.wtf(TAG, "Failed reading metadata", e); 1601 } finally { 1602 IoUtils.closeQuietly(fis); 1603 } 1604 } 1605 1606 private void writeSettingsLocked() { 1607 FileOutputStream fos = null; 1608 try { 1609 fos = mSettingsFile.startWrite(); 1610 1611 XmlSerializer out = new FastXmlSerializer(); 1612 out.setOutput(fos, StandardCharsets.UTF_8.name()); 1613 out.startDocument(null, true); 1614 out.startTag(null, TAG_VOLUMES); 1615 writeIntAttribute(out, ATTR_VERSION, VERSION_FIX_PRIMARY); 1616 writeStringAttribute(out, ATTR_PRIMARY_STORAGE_UUID, mPrimaryStorageUuid); 1617 writeBooleanAttribute(out, ATTR_FORCE_ADOPTABLE, mForceAdoptable); 1618 final int size = mRecords.size(); 1619 for (int i = 0; i < size; i++) { 1620 final VolumeRecord rec = mRecords.valueAt(i); 1621 writeVolumeRecord(out, rec); 1622 } 1623 out.endTag(null, TAG_VOLUMES); 1624 out.endDocument(); 1625 1626 mSettingsFile.finishWrite(fos); 1627 } catch (IOException e) { 1628 if (fos != null) { 1629 mSettingsFile.failWrite(fos); 1630 } 1631 } 1632 } 1633 1634 public static VolumeRecord readVolumeRecord(XmlPullParser in) throws IOException { 1635 final int type = readIntAttribute(in, ATTR_TYPE); 1636 final String fsUuid = readStringAttribute(in, ATTR_FS_UUID); 1637 final VolumeRecord meta = new VolumeRecord(type, fsUuid); 1638 meta.partGuid = readStringAttribute(in, ATTR_PART_GUID); 1639 meta.nickname = readStringAttribute(in, ATTR_NICKNAME); 1640 meta.userFlags = readIntAttribute(in, ATTR_USER_FLAGS); 1641 meta.createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS); 1642 meta.lastTrimMillis = readLongAttribute(in, ATTR_LAST_TRIM_MILLIS); 1643 meta.lastBenchMillis = readLongAttribute(in, ATTR_LAST_BENCH_MILLIS); 1644 return meta; 1645 } 1646 1647 public static void writeVolumeRecord(XmlSerializer out, VolumeRecord rec) throws IOException { 1648 out.startTag(null, TAG_VOLUME); 1649 writeIntAttribute(out, ATTR_TYPE, rec.type); 1650 writeStringAttribute(out, ATTR_FS_UUID, rec.fsUuid); 1651 writeStringAttribute(out, ATTR_PART_GUID, rec.partGuid); 1652 writeStringAttribute(out, ATTR_NICKNAME, rec.nickname); 1653 writeIntAttribute(out, ATTR_USER_FLAGS, rec.userFlags); 1654 writeLongAttribute(out, ATTR_CREATED_MILLIS, rec.createdMillis); 1655 writeLongAttribute(out, ATTR_LAST_TRIM_MILLIS, rec.lastTrimMillis); 1656 writeLongAttribute(out, ATTR_LAST_BENCH_MILLIS, rec.lastBenchMillis); 1657 out.endTag(null, TAG_VOLUME); 1658 } 1659 1660 /** 1661 * Exposed API calls below here 1662 */ 1663 1664 @Override 1665 public void registerListener(IMountServiceListener listener) { 1666 mCallbacks.register(listener); 1667 } 1668 1669 @Override 1670 public void unregisterListener(IMountServiceListener listener) { 1671 mCallbacks.unregister(listener); 1672 } 1673 1674 @Override 1675 public void shutdown(final IMountShutdownObserver observer) { 1676 enforcePermission(android.Manifest.permission.SHUTDOWN); 1677 1678 Slog.i(TAG, "Shutting down"); 1679 mHandler.obtainMessage(H_SHUTDOWN, observer).sendToTarget(); 1680 } 1681 1682 @Override 1683 public boolean isUsbMassStorageConnected() { 1684 throw new UnsupportedOperationException(); 1685 } 1686 1687 @Override 1688 public void setUsbMassStorageEnabled(boolean enable) { 1689 throw new UnsupportedOperationException(); 1690 } 1691 1692 @Override 1693 public boolean isUsbMassStorageEnabled() { 1694 throw new UnsupportedOperationException(); 1695 } 1696 1697 @Override 1698 public String getVolumeState(String mountPoint) { 1699 throw new UnsupportedOperationException(); 1700 } 1701 1702 @Override 1703 public boolean isExternalStorageEmulated() { 1704 throw new UnsupportedOperationException(); 1705 } 1706 1707 @Override 1708 public int mountVolume(String path) { 1709 mount(findVolumeIdForPathOrThrow(path)); 1710 return 0; 1711 } 1712 1713 @Override 1714 public void unmountVolume(String path, boolean force, boolean removeEncryption) { 1715 unmount(findVolumeIdForPathOrThrow(path)); 1716 } 1717 1718 @Override 1719 public int formatVolume(String path) { 1720 format(findVolumeIdForPathOrThrow(path)); 1721 return 0; 1722 } 1723 1724 @Override 1725 public void mount(String volId) { 1726 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); 1727 waitForReady(); 1728 1729 final VolumeInfo vol = findVolumeByIdOrThrow(volId); 1730 if (isMountDisallowed(vol)) { 1731 throw new SecurityException("Mounting " + volId + " restricted by policy"); 1732 } 1733 try { 1734 mConnector.execute("volume", "mount", vol.id, vol.mountFlags, vol.mountUserId); 1735 } catch (NativeDaemonConnectorException e) { 1736 throw e.rethrowAsParcelableException(); 1737 } 1738 } 1739 1740 @Override 1741 public void unmount(String volId) { 1742 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); 1743 waitForReady(); 1744 1745 final VolumeInfo vol = findVolumeByIdOrThrow(volId); 1746 1747 // TODO: expand PMS to know about multiple volumes 1748 if (vol.isPrimaryPhysical()) { 1749 final long ident = Binder.clearCallingIdentity(); 1750 try { 1751 synchronized (mUnmountLock) { 1752 mUnmountSignal = new CountDownLatch(1); 1753 mPms.updateExternalMediaStatus(false, true); 1754 waitForLatch(mUnmountSignal, "mUnmountSignal"); 1755 mUnmountSignal = null; 1756 } 1757 } finally { 1758 Binder.restoreCallingIdentity(ident); 1759 } 1760 } 1761 1762 try { 1763 mConnector.execute("volume", "unmount", vol.id); 1764 } catch (NativeDaemonConnectorException e) { 1765 throw e.rethrowAsParcelableException(); 1766 } 1767 } 1768 1769 @Override 1770 public void format(String volId) { 1771 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); 1772 waitForReady(); 1773 1774 final VolumeInfo vol = findVolumeByIdOrThrow(volId); 1775 try { 1776 mConnector.execute("volume", "format", vol.id, "auto"); 1777 } catch (NativeDaemonConnectorException e) { 1778 throw e.rethrowAsParcelableException(); 1779 } 1780 } 1781 1782 @Override 1783 public long benchmark(String volId) { 1784 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); 1785 waitForReady(); 1786 1787 try { 1788 // TODO: make benchmark async so we don't block other commands 1789 final NativeDaemonEvent res = mConnector.execute(3 * DateUtils.MINUTE_IN_MILLIS, 1790 "volume", "benchmark", volId); 1791 return Long.parseLong(res.getMessage()); 1792 } catch (NativeDaemonTimeoutException e) { 1793 return Long.MAX_VALUE; 1794 } catch (NativeDaemonConnectorException e) { 1795 throw e.rethrowAsParcelableException(); 1796 } 1797 } 1798 1799 @Override 1800 public void partitionPublic(String diskId) { 1801 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); 1802 waitForReady(); 1803 1804 final CountDownLatch latch = findOrCreateDiskScanLatch(diskId); 1805 try { 1806 mConnector.execute("volume", "partition", diskId, "public"); 1807 waitForLatch(latch, "partitionPublic", 3 * DateUtils.MINUTE_IN_MILLIS); 1808 } catch (NativeDaemonConnectorException e) { 1809 throw e.rethrowAsParcelableException(); 1810 } catch (TimeoutException e) { 1811 throw new IllegalStateException(e); 1812 } 1813 } 1814 1815 @Override 1816 public void partitionPrivate(String diskId) { 1817 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); 1818 enforceAdminUser(); 1819 waitForReady(); 1820 1821 final CountDownLatch latch = findOrCreateDiskScanLatch(diskId); 1822 try { 1823 mConnector.execute("volume", "partition", diskId, "private"); 1824 waitForLatch(latch, "partitionPrivate", 3 * DateUtils.MINUTE_IN_MILLIS); 1825 } catch (NativeDaemonConnectorException e) { 1826 throw e.rethrowAsParcelableException(); 1827 } catch (TimeoutException e) { 1828 throw new IllegalStateException(e); 1829 } 1830 } 1831 1832 @Override 1833 public void partitionMixed(String diskId, int ratio) { 1834 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); 1835 enforceAdminUser(); 1836 waitForReady(); 1837 1838 final CountDownLatch latch = findOrCreateDiskScanLatch(diskId); 1839 try { 1840 mConnector.execute("volume", "partition", diskId, "mixed", ratio); 1841 waitForLatch(latch, "partitionMixed", 3 * DateUtils.MINUTE_IN_MILLIS); 1842 } catch (NativeDaemonConnectorException e) { 1843 throw e.rethrowAsParcelableException(); 1844 } catch (TimeoutException e) { 1845 throw new IllegalStateException(e); 1846 } 1847 } 1848 1849 @Override 1850 public void setVolumeNickname(String fsUuid, String nickname) { 1851 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); 1852 waitForReady(); 1853 1854 Preconditions.checkNotNull(fsUuid); 1855 synchronized (mLock) { 1856 final VolumeRecord rec = mRecords.get(fsUuid); 1857 rec.nickname = nickname; 1858 mCallbacks.notifyVolumeRecordChanged(rec); 1859 writeSettingsLocked(); 1860 } 1861 } 1862 1863 @Override 1864 public void setVolumeUserFlags(String fsUuid, int flags, int mask) { 1865 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); 1866 waitForReady(); 1867 1868 Preconditions.checkNotNull(fsUuid); 1869 synchronized (mLock) { 1870 final VolumeRecord rec = mRecords.get(fsUuid); 1871 rec.userFlags = (rec.userFlags & ~mask) | (flags & mask); 1872 mCallbacks.notifyVolumeRecordChanged(rec); 1873 writeSettingsLocked(); 1874 } 1875 } 1876 1877 @Override 1878 public void forgetVolume(String fsUuid) { 1879 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); 1880 waitForReady(); 1881 1882 Preconditions.checkNotNull(fsUuid); 1883 1884 synchronized (mLock) { 1885 final VolumeRecord rec = mRecords.remove(fsUuid); 1886 if (rec != null && !TextUtils.isEmpty(rec.partGuid)) { 1887 mHandler.obtainMessage(H_PARTITION_FORGET, rec.partGuid).sendToTarget(); 1888 } 1889 mCallbacks.notifyVolumeForgotten(fsUuid); 1890 1891 // If this had been primary storage, revert back to internal and 1892 // reset vold so we bind into new volume into place. 1893 if (Objects.equals(mPrimaryStorageUuid, fsUuid)) { 1894 mPrimaryStorageUuid = getDefaultPrimaryStorageUuid(); 1895 mHandler.obtainMessage(H_RESET).sendToTarget(); 1896 } 1897 1898 writeSettingsLocked(); 1899 } 1900 } 1901 1902 @Override 1903 public void forgetAllVolumes() { 1904 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); 1905 waitForReady(); 1906 1907 synchronized (mLock) { 1908 for (int i = 0; i < mRecords.size(); i++) { 1909 final String fsUuid = mRecords.keyAt(i); 1910 final VolumeRecord rec = mRecords.valueAt(i); 1911 if (!TextUtils.isEmpty(rec.partGuid)) { 1912 mHandler.obtainMessage(H_PARTITION_FORGET, rec.partGuid).sendToTarget(); 1913 } 1914 mCallbacks.notifyVolumeForgotten(fsUuid); 1915 } 1916 mRecords.clear(); 1917 1918 if (!Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)) { 1919 mPrimaryStorageUuid = getDefaultPrimaryStorageUuid(); 1920 } 1921 1922 writeSettingsLocked(); 1923 mHandler.obtainMessage(H_RESET).sendToTarget(); 1924 } 1925 } 1926 1927 private void forgetPartition(String partGuid) { 1928 try { 1929 mConnector.execute("volume", "forget_partition", partGuid); 1930 } catch (NativeDaemonConnectorException e) { 1931 Slog.w(TAG, "Failed to forget key for " + partGuid + ": " + e); 1932 } 1933 } 1934 1935 private void remountUidExternalStorage(int uid, int mode) { 1936 waitForReady(); 1937 1938 String modeName = "none"; 1939 switch (mode) { 1940 case Zygote.MOUNT_EXTERNAL_DEFAULT: { 1941 modeName = "default"; 1942 } break; 1943 1944 case Zygote.MOUNT_EXTERNAL_READ: { 1945 modeName = "read"; 1946 } break; 1947 1948 case Zygote.MOUNT_EXTERNAL_WRITE: { 1949 modeName = "write"; 1950 } break; 1951 } 1952 1953 try { 1954 mConnector.execute("volume", "remount_uid", uid, modeName); 1955 } catch (NativeDaemonConnectorException e) { 1956 Slog.w(TAG, "Failed to remount UID " + uid + " as " + modeName + ": " + e); 1957 } 1958 } 1959 1960 @Override 1961 public void setDebugFlags(int flags, int mask) { 1962 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); 1963 waitForReady(); 1964 1965 if ((mask & StorageManager.DEBUG_EMULATE_FBE) != 0) { 1966 if (StorageManager.isFileEncryptedNativeOnly()) { 1967 throw new IllegalStateException( 1968 "Emulation not available on device with native FBE"); 1969 } 1970 if (mLockPatternUtils.isCredentialRequiredToDecrypt(false)) { 1971 throw new IllegalStateException( 1972 "Emulation requires disabling 'Secure start-up' in Settings > Security"); 1973 } 1974 1975 final long token = Binder.clearCallingIdentity(); 1976 try { 1977 final boolean emulateFbe = (flags & StorageManager.DEBUG_EMULATE_FBE) != 0; 1978 SystemProperties.set(StorageManager.PROP_EMULATE_FBE, Boolean.toString(emulateFbe)); 1979 1980 // Perform hard reboot to kick policy into place 1981 mContext.getSystemService(PowerManager.class).reboot(null); 1982 } finally { 1983 Binder.restoreCallingIdentity(token); 1984 } 1985 } 1986 1987 if ((mask & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0) { 1988 synchronized (mLock) { 1989 mForceAdoptable = (flags & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0; 1990 1991 writeSettingsLocked(); 1992 mHandler.obtainMessage(H_RESET).sendToTarget(); 1993 } 1994 } 1995 1996 if ((mask & (StorageManager.DEBUG_SDCARDFS_FORCE_ON 1997 | StorageManager.DEBUG_SDCARDFS_FORCE_OFF)) != 0) { 1998 final String value; 1999 if ((flags & StorageManager.DEBUG_SDCARDFS_FORCE_ON) != 0) { 2000 value = "force_on"; 2001 } else if ((flags & StorageManager.DEBUG_SDCARDFS_FORCE_OFF) != 0) { 2002 value = "force_off"; 2003 } else { 2004 value = ""; 2005 } 2006 2007 final long token = Binder.clearCallingIdentity(); 2008 try { 2009 SystemProperties.set(StorageManager.PROP_SDCARDFS, value); 2010 2011 // Reset storage to kick new setting into place 2012 mHandler.obtainMessage(H_RESET).sendToTarget(); 2013 } finally { 2014 Binder.restoreCallingIdentity(token); 2015 } 2016 } 2017 } 2018 2019 @Override 2020 public String getPrimaryStorageUuid() { 2021 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); 2022 waitForReady(); 2023 2024 synchronized (mLock) { 2025 return mPrimaryStorageUuid; 2026 } 2027 } 2028 2029 @Override 2030 public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) { 2031 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); 2032 waitForReady(); 2033 2034 final VolumeInfo from; 2035 final VolumeInfo to; 2036 2037 synchronized (mLock) { 2038 if (Objects.equals(mPrimaryStorageUuid, volumeUuid)) { 2039 throw new IllegalArgumentException("Primary storage already at " + volumeUuid); 2040 } 2041 2042 if (mMoveCallback != null) { 2043 throw new IllegalStateException("Move already in progress"); 2044 } 2045 mMoveCallback = callback; 2046 mMoveTargetUuid = volumeUuid; 2047 2048 // When moving to/from primary physical volume, we probably just nuked 2049 // the current storage location, so we have nothing to move. 2050 if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, mPrimaryStorageUuid) 2051 || Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) { 2052 Slog.d(TAG, "Skipping move to/from primary physical"); 2053 onMoveStatusLocked(MOVE_STATUS_COPY_FINISHED); 2054 onMoveStatusLocked(PackageManager.MOVE_SUCCEEDED); 2055 mHandler.obtainMessage(H_RESET).sendToTarget(); 2056 return; 2057 2058 } else { 2059 from = findStorageForUuid(mPrimaryStorageUuid); 2060 to = findStorageForUuid(volumeUuid); 2061 2062 if (from == null) { 2063 Slog.w(TAG, "Failing move due to missing from volume " + mPrimaryStorageUuid); 2064 onMoveStatusLocked(PackageManager.MOVE_FAILED_INTERNAL_ERROR); 2065 return; 2066 } else if (to == null) { 2067 Slog.w(TAG, "Failing move due to missing to volume " + volumeUuid); 2068 onMoveStatusLocked(PackageManager.MOVE_FAILED_INTERNAL_ERROR); 2069 return; 2070 } 2071 } 2072 } 2073 2074 try { 2075 mConnector.execute("volume", "move_storage", from.id, to.id); 2076 } catch (NativeDaemonConnectorException e) { 2077 throw e.rethrowAsParcelableException(); 2078 } 2079 } 2080 2081 @Override 2082 public int[] getStorageUsers(String path) { 2083 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); 2084 waitForReady(); 2085 try { 2086 final String[] r = NativeDaemonEvent.filterMessageList( 2087 mConnector.executeForList("storage", "users", path), 2088 VoldResponseCode.StorageUsersListResult); 2089 2090 // FMT: <pid> <process name> 2091 int[] data = new int[r.length]; 2092 for (int i = 0; i < r.length; i++) { 2093 String[] tok = r[i].split(" "); 2094 try { 2095 data[i] = Integer.parseInt(tok[0]); 2096 } catch (NumberFormatException nfe) { 2097 Slog.e(TAG, String.format("Error parsing pid %s", tok[0])); 2098 return new int[0]; 2099 } 2100 } 2101 return data; 2102 } catch (NativeDaemonConnectorException e) { 2103 Slog.e(TAG, "Failed to retrieve storage users list", e); 2104 return new int[0]; 2105 } 2106 } 2107 2108 private void warnOnNotMounted() { 2109 synchronized (mLock) { 2110 for (int i = 0; i < mVolumes.size(); i++) { 2111 final VolumeInfo vol = mVolumes.valueAt(i); 2112 if (vol.isPrimary() && vol.isMountedWritable()) { 2113 // Cool beans, we have a mounted primary volume 2114 return; 2115 } 2116 } 2117 } 2118 2119 Slog.w(TAG, "No primary storage mounted!"); 2120 } 2121 2122 public String[] getSecureContainerList() { 2123 enforcePermission(android.Manifest.permission.ASEC_ACCESS); 2124 waitForReady(); 2125 warnOnNotMounted(); 2126 2127 try { 2128 return NativeDaemonEvent.filterMessageList( 2129 mConnector.executeForList("asec", "list"), VoldResponseCode.AsecListResult); 2130 } catch (NativeDaemonConnectorException e) { 2131 return new String[0]; 2132 } 2133 } 2134 2135 public int createSecureContainer(String id, int sizeMb, String fstype, String key, 2136 int ownerUid, boolean external) { 2137 enforcePermission(android.Manifest.permission.ASEC_CREATE); 2138 waitForReady(); 2139 warnOnNotMounted(); 2140 2141 int rc = StorageResultCode.OperationSucceeded; 2142 try { 2143 mConnector.execute("asec", "create", id, sizeMb, fstype, new SensitiveArg(key), 2144 ownerUid, external ? "1" : "0"); 2145 } catch (NativeDaemonConnectorException e) { 2146 rc = StorageResultCode.OperationFailedInternalError; 2147 } 2148 2149 if (rc == StorageResultCode.OperationSucceeded) { 2150 synchronized (mAsecMountSet) { 2151 mAsecMountSet.add(id); 2152 } 2153 } 2154 return rc; 2155 } 2156 2157 @Override 2158 public int resizeSecureContainer(String id, int sizeMb, String key) { 2159 enforcePermission(android.Manifest.permission.ASEC_CREATE); 2160 waitForReady(); 2161 warnOnNotMounted(); 2162 2163 int rc = StorageResultCode.OperationSucceeded; 2164 try { 2165 mConnector.execute("asec", "resize", id, sizeMb, new SensitiveArg(key)); 2166 } catch (NativeDaemonConnectorException e) { 2167 rc = StorageResultCode.OperationFailedInternalError; 2168 } 2169 return rc; 2170 } 2171 2172 public int finalizeSecureContainer(String id) { 2173 enforcePermission(android.Manifest.permission.ASEC_CREATE); 2174 warnOnNotMounted(); 2175 2176 int rc = StorageResultCode.OperationSucceeded; 2177 try { 2178 mConnector.execute("asec", "finalize", id); 2179 /* 2180 * Finalization does a remount, so no need 2181 * to update mAsecMountSet 2182 */ 2183 } catch (NativeDaemonConnectorException e) { 2184 rc = StorageResultCode.OperationFailedInternalError; 2185 } 2186 return rc; 2187 } 2188 2189 public int fixPermissionsSecureContainer(String id, int gid, String filename) { 2190 enforcePermission(android.Manifest.permission.ASEC_CREATE); 2191 warnOnNotMounted(); 2192 2193 int rc = StorageResultCode.OperationSucceeded; 2194 try { 2195 mConnector.execute("asec", "fixperms", id, gid, filename); 2196 /* 2197 * Fix permissions does a remount, so no need to update 2198 * mAsecMountSet 2199 */ 2200 } catch (NativeDaemonConnectorException e) { 2201 rc = StorageResultCode.OperationFailedInternalError; 2202 } 2203 return rc; 2204 } 2205 2206 public int destroySecureContainer(String id, boolean force) { 2207 enforcePermission(android.Manifest.permission.ASEC_DESTROY); 2208 waitForReady(); 2209 warnOnNotMounted(); 2210 2211 /* 2212 * Force a GC to make sure AssetManagers in other threads of the 2213 * system_server are cleaned up. We have to do this since AssetManager 2214 * instances are kept as a WeakReference and it's possible we have files 2215 * open on the external storage. 2216 */ 2217 Runtime.getRuntime().gc(); 2218 2219 int rc = StorageResultCode.OperationSucceeded; 2220 try { 2221 final Command cmd = new Command("asec", "destroy", id); 2222 if (force) { 2223 cmd.appendArg("force"); 2224 } 2225 mConnector.execute(cmd); 2226 } catch (NativeDaemonConnectorException e) { 2227 int code = e.getCode(); 2228 if (code == VoldResponseCode.OpFailedStorageBusy) { 2229 rc = StorageResultCode.OperationFailedStorageBusy; 2230 } else { 2231 rc = StorageResultCode.OperationFailedInternalError; 2232 } 2233 } 2234 2235 if (rc == StorageResultCode.OperationSucceeded) { 2236 synchronized (mAsecMountSet) { 2237 if (mAsecMountSet.contains(id)) { 2238 mAsecMountSet.remove(id); 2239 } 2240 } 2241 } 2242 2243 return rc; 2244 } 2245 2246 public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly) { 2247 enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT); 2248 waitForReady(); 2249 warnOnNotMounted(); 2250 2251 synchronized (mAsecMountSet) { 2252 if (mAsecMountSet.contains(id)) { 2253 return StorageResultCode.OperationFailedStorageMounted; 2254 } 2255 } 2256 2257 int rc = StorageResultCode.OperationSucceeded; 2258 try { 2259 mConnector.execute("asec", "mount", id, new SensitiveArg(key), ownerUid, 2260 readOnly ? "ro" : "rw"); 2261 } catch (NativeDaemonConnectorException e) { 2262 int code = e.getCode(); 2263 if (code != VoldResponseCode.OpFailedStorageBusy) { 2264 rc = StorageResultCode.OperationFailedInternalError; 2265 } 2266 } 2267 2268 if (rc == StorageResultCode.OperationSucceeded) { 2269 synchronized (mAsecMountSet) { 2270 mAsecMountSet.add(id); 2271 } 2272 } 2273 return rc; 2274 } 2275 2276 public int unmountSecureContainer(String id, boolean force) { 2277 enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT); 2278 waitForReady(); 2279 warnOnNotMounted(); 2280 2281 synchronized (mAsecMountSet) { 2282 if (!mAsecMountSet.contains(id)) { 2283 return StorageResultCode.OperationFailedStorageNotMounted; 2284 } 2285 } 2286 2287 /* 2288 * Force a GC to make sure AssetManagers in other threads of the 2289 * system_server are cleaned up. We have to do this since AssetManager 2290 * instances are kept as a WeakReference and it's possible we have files 2291 * open on the external storage. 2292 */ 2293 Runtime.getRuntime().gc(); 2294 2295 int rc = StorageResultCode.OperationSucceeded; 2296 try { 2297 final Command cmd = new Command("asec", "unmount", id); 2298 if (force) { 2299 cmd.appendArg("force"); 2300 } 2301 mConnector.execute(cmd); 2302 } catch (NativeDaemonConnectorException e) { 2303 int code = e.getCode(); 2304 if (code == VoldResponseCode.OpFailedStorageBusy) { 2305 rc = StorageResultCode.OperationFailedStorageBusy; 2306 } else { 2307 rc = StorageResultCode.OperationFailedInternalError; 2308 } 2309 } 2310 2311 if (rc == StorageResultCode.OperationSucceeded) { 2312 synchronized (mAsecMountSet) { 2313 mAsecMountSet.remove(id); 2314 } 2315 } 2316 return rc; 2317 } 2318 2319 public boolean isSecureContainerMounted(String id) { 2320 enforcePermission(android.Manifest.permission.ASEC_ACCESS); 2321 waitForReady(); 2322 warnOnNotMounted(); 2323 2324 synchronized (mAsecMountSet) { 2325 return mAsecMountSet.contains(id); 2326 } 2327 } 2328 2329 public int renameSecureContainer(String oldId, String newId) { 2330 enforcePermission(android.Manifest.permission.ASEC_RENAME); 2331 waitForReady(); 2332 warnOnNotMounted(); 2333 2334 synchronized (mAsecMountSet) { 2335 /* 2336 * Because a mounted container has active internal state which cannot be 2337 * changed while active, we must ensure both ids are not currently mounted. 2338 */ 2339 if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) { 2340 return StorageResultCode.OperationFailedStorageMounted; 2341 } 2342 } 2343 2344 int rc = StorageResultCode.OperationSucceeded; 2345 try { 2346 mConnector.execute("asec", "rename", oldId, newId); 2347 } catch (NativeDaemonConnectorException e) { 2348 rc = StorageResultCode.OperationFailedInternalError; 2349 } 2350 2351 return rc; 2352 } 2353 2354 public String getSecureContainerPath(String id) { 2355 enforcePermission(android.Manifest.permission.ASEC_ACCESS); 2356 waitForReady(); 2357 warnOnNotMounted(); 2358 2359 final NativeDaemonEvent event; 2360 try { 2361 event = mConnector.execute("asec", "path", id); 2362 event.checkCode(VoldResponseCode.AsecPathResult); 2363 return event.getMessage(); 2364 } catch (NativeDaemonConnectorException e) { 2365 int code = e.getCode(); 2366 if (code == VoldResponseCode.OpFailedStorageNotFound) { 2367 Slog.i(TAG, String.format("Container '%s' not found", id)); 2368 return null; 2369 } else { 2370 throw new IllegalStateException(String.format("Unexpected response code %d", code)); 2371 } 2372 } 2373 } 2374 2375 public String getSecureContainerFilesystemPath(String id) { 2376 enforcePermission(android.Manifest.permission.ASEC_ACCESS); 2377 waitForReady(); 2378 warnOnNotMounted(); 2379 2380 final NativeDaemonEvent event; 2381 try { 2382 event = mConnector.execute("asec", "fspath", id); 2383 event.checkCode(VoldResponseCode.AsecPathResult); 2384 return event.getMessage(); 2385 } catch (NativeDaemonConnectorException e) { 2386 int code = e.getCode(); 2387 if (code == VoldResponseCode.OpFailedStorageNotFound) { 2388 Slog.i(TAG, String.format("Container '%s' not found", id)); 2389 return null; 2390 } else { 2391 throw new IllegalStateException(String.format("Unexpected response code %d", code)); 2392 } 2393 } 2394 } 2395 2396 @Override 2397 public void finishMediaUpdate() { 2398 if (Binder.getCallingUid() != Process.SYSTEM_UID) { 2399 throw new SecurityException("no permission to call finishMediaUpdate()"); 2400 } 2401 if (mUnmountSignal != null) { 2402 mUnmountSignal.countDown(); 2403 } else { 2404 Slog.w(TAG, "Odd, nobody asked to unmount?"); 2405 } 2406 } 2407 2408 private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) { 2409 if (callerUid == android.os.Process.SYSTEM_UID) { 2410 return true; 2411 } 2412 2413 if (packageName == null) { 2414 return false; 2415 } 2416 2417 final int packageUid = mPms.getPackageUid(packageName, 2418 PackageManager.MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getUserId(callerUid)); 2419 2420 if (DEBUG_OBB) { 2421 Slog.d(TAG, "packageName = " + packageName + ", packageUid = " + 2422 packageUid + ", callerUid = " + callerUid); 2423 } 2424 2425 return callerUid == packageUid; 2426 } 2427 2428 public String getMountedObbPath(String rawPath) { 2429 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 2430 2431 waitForReady(); 2432 warnOnNotMounted(); 2433 2434 final ObbState state; 2435 synchronized (mObbMounts) { 2436 state = mObbPathToStateMap.get(rawPath); 2437 } 2438 if (state == null) { 2439 Slog.w(TAG, "Failed to find OBB mounted at " + rawPath); 2440 return null; 2441 } 2442 2443 final NativeDaemonEvent event; 2444 try { 2445 event = mConnector.execute("obb", "path", state.canonicalPath); 2446 event.checkCode(VoldResponseCode.AsecPathResult); 2447 return event.getMessage(); 2448 } catch (NativeDaemonConnectorException e) { 2449 int code = e.getCode(); 2450 if (code == VoldResponseCode.OpFailedStorageNotFound) { 2451 return null; 2452 } else { 2453 throw new IllegalStateException(String.format("Unexpected response code %d", code)); 2454 } 2455 } 2456 } 2457 2458 @Override 2459 public boolean isObbMounted(String rawPath) { 2460 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 2461 synchronized (mObbMounts) { 2462 return mObbPathToStateMap.containsKey(rawPath); 2463 } 2464 } 2465 2466 @Override 2467 public void mountObb( 2468 String rawPath, String canonicalPath, String key, IObbActionListener token, int nonce) { 2469 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 2470 Preconditions.checkNotNull(canonicalPath, "canonicalPath cannot be null"); 2471 Preconditions.checkNotNull(token, "token cannot be null"); 2472 2473 final int callingUid = Binder.getCallingUid(); 2474 final ObbState obbState = new ObbState(rawPath, canonicalPath, callingUid, token, nonce); 2475 final ObbAction action = new MountObbAction(obbState, key, callingUid); 2476 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action)); 2477 2478 if (DEBUG_OBB) 2479 Slog.i(TAG, "Send to OBB handler: " + action.toString()); 2480 } 2481 2482 @Override 2483 public void unmountObb(String rawPath, boolean force, IObbActionListener token, int nonce) { 2484 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 2485 2486 final ObbState existingState; 2487 synchronized (mObbMounts) { 2488 existingState = mObbPathToStateMap.get(rawPath); 2489 } 2490 2491 if (existingState != null) { 2492 // TODO: separate state object from request data 2493 final int callingUid = Binder.getCallingUid(); 2494 final ObbState newState = new ObbState( 2495 rawPath, existingState.canonicalPath, callingUid, token, nonce); 2496 final ObbAction action = new UnmountObbAction(newState, force); 2497 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action)); 2498 2499 if (DEBUG_OBB) 2500 Slog.i(TAG, "Send to OBB handler: " + action.toString()); 2501 } else { 2502 Slog.w(TAG, "Unknown OBB mount at " + rawPath); 2503 } 2504 } 2505 2506 @Override 2507 public int getEncryptionState() { 2508 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, 2509 "no permission to access the crypt keeper"); 2510 2511 waitForReady(); 2512 2513 final NativeDaemonEvent event; 2514 try { 2515 event = mCryptConnector.execute("cryptfs", "cryptocomplete"); 2516 return Integer.parseInt(event.getMessage()); 2517 } catch (NumberFormatException e) { 2518 // Bad result - unexpected. 2519 Slog.w(TAG, "Unable to parse result from cryptfs cryptocomplete"); 2520 return ENCRYPTION_STATE_ERROR_UNKNOWN; 2521 } catch (NativeDaemonConnectorException e) { 2522 // Something bad happened. 2523 Slog.w(TAG, "Error in communicating with cryptfs in validating"); 2524 return ENCRYPTION_STATE_ERROR_UNKNOWN; 2525 } 2526 } 2527 2528 @Override 2529 public int decryptStorage(String password) { 2530 if (TextUtils.isEmpty(password)) { 2531 throw new IllegalArgumentException("password cannot be empty"); 2532 } 2533 2534 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, 2535 "no permission to access the crypt keeper"); 2536 2537 waitForReady(); 2538 2539 if (DEBUG_EVENTS) { 2540 Slog.i(TAG, "decrypting storage..."); 2541 } 2542 2543 final NativeDaemonEvent event; 2544 try { 2545 event = mCryptConnector.execute("cryptfs", "checkpw", new SensitiveArg(password)); 2546 2547 final int code = Integer.parseInt(event.getMessage()); 2548 if (code == 0) { 2549 // Decrypt was successful. Post a delayed message before restarting in order 2550 // to let the UI to clear itself 2551 mHandler.postDelayed(new Runnable() { 2552 public void run() { 2553 try { 2554 mCryptConnector.execute("cryptfs", "restart"); 2555 } catch (NativeDaemonConnectorException e) { 2556 Slog.e(TAG, "problem executing in background", e); 2557 } 2558 } 2559 }, 1000); // 1 second 2560 } 2561 2562 return code; 2563 } catch (NativeDaemonConnectorException e) { 2564 // Decryption failed 2565 return e.getCode(); 2566 } 2567 } 2568 2569 public int encryptStorage(int type, String password) { 2570 if (TextUtils.isEmpty(password) && type != StorageManager.CRYPT_TYPE_DEFAULT) { 2571 throw new IllegalArgumentException("password cannot be empty"); 2572 } 2573 2574 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, 2575 "no permission to access the crypt keeper"); 2576 2577 waitForReady(); 2578 2579 if (DEBUG_EVENTS) { 2580 Slog.i(TAG, "encrypting storage..."); 2581 } 2582 2583 try { 2584 if (type == StorageManager.CRYPT_TYPE_DEFAULT) { 2585 mCryptConnector.execute("cryptfs", "enablecrypto", "inplace", 2586 CRYPTO_TYPES[type]); 2587 } else { 2588 mCryptConnector.execute("cryptfs", "enablecrypto", "inplace", 2589 CRYPTO_TYPES[type], new SensitiveArg(password)); 2590 } 2591 } catch (NativeDaemonConnectorException e) { 2592 // Encryption failed 2593 return e.getCode(); 2594 } 2595 2596 return 0; 2597 } 2598 2599 /** Set the password for encrypting the master key. 2600 * @param type One of the CRYPTO_TYPE_XXX consts defined in StorageManager. 2601 * @param password The password to set. 2602 */ 2603 public int changeEncryptionPassword(int type, String password) { 2604 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, 2605 "no permission to access the crypt keeper"); 2606 2607 waitForReady(); 2608 2609 if (DEBUG_EVENTS) { 2610 Slog.i(TAG, "changing encryption password..."); 2611 } 2612 2613 try { 2614 NativeDaemonEvent event = mCryptConnector.execute("cryptfs", "changepw", CRYPTO_TYPES[type], 2615 new SensitiveArg(password)); 2616 return Integer.parseInt(event.getMessage()); 2617 } catch (NativeDaemonConnectorException e) { 2618 // Encryption failed 2619 return e.getCode(); 2620 } 2621 } 2622 2623 /** 2624 * Validate a user-supplied password string with cryptfs 2625 */ 2626 @Override 2627 public int verifyEncryptionPassword(String password) throws RemoteException { 2628 // Only the system process is permitted to validate passwords 2629 if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) { 2630 throw new SecurityException("no permission to access the crypt keeper"); 2631 } 2632 2633 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, 2634 "no permission to access the crypt keeper"); 2635 2636 if (TextUtils.isEmpty(password)) { 2637 throw new IllegalArgumentException("password cannot be empty"); 2638 } 2639 2640 waitForReady(); 2641 2642 if (DEBUG_EVENTS) { 2643 Slog.i(TAG, "validating encryption password..."); 2644 } 2645 2646 final NativeDaemonEvent event; 2647 try { 2648 event = mCryptConnector.execute("cryptfs", "verifypw", new SensitiveArg(password)); 2649 Slog.i(TAG, "cryptfs verifypw => " + event.getMessage()); 2650 return Integer.parseInt(event.getMessage()); 2651 } catch (NativeDaemonConnectorException e) { 2652 // Encryption failed 2653 return e.getCode(); 2654 } 2655 } 2656 2657 /** 2658 * Get the type of encryption used to encrypt the master key. 2659 * @return The type, one of the CRYPT_TYPE_XXX consts from StorageManager. 2660 */ 2661 @Override 2662 public int getPasswordType() { 2663 mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL, 2664 "no permission to access the crypt keeper"); 2665 2666 waitForReady(); 2667 2668 final NativeDaemonEvent event; 2669 try { 2670 event = mCryptConnector.execute("cryptfs", "getpwtype"); 2671 for (int i = 0; i < CRYPTO_TYPES.length; ++i) { 2672 if (CRYPTO_TYPES[i].equals(event.getMessage())) 2673 return i; 2674 } 2675 2676 throw new IllegalStateException("unexpected return from cryptfs"); 2677 } catch (NativeDaemonConnectorException e) { 2678 throw e.rethrowAsParcelableException(); 2679 } 2680 } 2681 2682 /** 2683 * Set a field in the crypto header. 2684 * @param field field to set 2685 * @param contents contents to set in field 2686 */ 2687 @Override 2688 public void setField(String field, String contents) throws RemoteException { 2689 mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL, 2690 "no permission to access the crypt keeper"); 2691 2692 waitForReady(); 2693 2694 final NativeDaemonEvent event; 2695 try { 2696 event = mCryptConnector.execute("cryptfs", "setfield", field, contents); 2697 } catch (NativeDaemonConnectorException e) { 2698 throw e.rethrowAsParcelableException(); 2699 } 2700 } 2701 2702 /** 2703 * Gets a field from the crypto header. 2704 * @param field field to get 2705 * @return contents of field 2706 */ 2707 @Override 2708 public String getField(String field) throws RemoteException { 2709 mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL, 2710 "no permission to access the crypt keeper"); 2711 2712 waitForReady(); 2713 2714 final NativeDaemonEvent event; 2715 try { 2716 final String[] contents = NativeDaemonEvent.filterMessageList( 2717 mCryptConnector.executeForList("cryptfs", "getfield", field), 2718 VoldResponseCode.CryptfsGetfieldResult); 2719 String result = new String(); 2720 for (String content : contents) { 2721 result += content; 2722 } 2723 return result; 2724 } catch (NativeDaemonConnectorException e) { 2725 throw e.rethrowAsParcelableException(); 2726 } 2727 } 2728 2729 /** 2730 * Is userdata convertible to file based encryption? 2731 * @return non zero for convertible 2732 */ 2733 @Override 2734 public boolean isConvertibleToFBE() throws RemoteException { 2735 mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL, 2736 "no permission to access the crypt keeper"); 2737 2738 waitForReady(); 2739 2740 final NativeDaemonEvent event; 2741 try { 2742 event = mCryptConnector.execute("cryptfs", "isConvertibleToFBE"); 2743 return Integer.parseInt(event.getMessage()) != 0; 2744 } catch (NativeDaemonConnectorException e) { 2745 throw e.rethrowAsParcelableException(); 2746 } 2747 } 2748 2749 @Override 2750 public String getPassword() throws RemoteException { 2751 mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL, 2752 "only keyguard can retrieve password"); 2753 2754 if (!isReady()) { 2755 return new String(); 2756 } 2757 2758 final NativeDaemonEvent event; 2759 try { 2760 event = mCryptConnector.execute("cryptfs", "getpw"); 2761 if ("-1".equals(event.getMessage())) { 2762 // -1 equals no password 2763 return null; 2764 } 2765 return event.getMessage(); 2766 } catch (NativeDaemonConnectorException e) { 2767 throw e.rethrowAsParcelableException(); 2768 } catch (IllegalArgumentException e) { 2769 Slog.e(TAG, "Invalid response to getPassword"); 2770 return null; 2771 } 2772 } 2773 2774 @Override 2775 public void clearPassword() throws RemoteException { 2776 mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL, 2777 "only keyguard can clear password"); 2778 2779 if (!isReady()) { 2780 return; 2781 } 2782 2783 final NativeDaemonEvent event; 2784 try { 2785 event = mCryptConnector.execute("cryptfs", "clearpw"); 2786 } catch (NativeDaemonConnectorException e) { 2787 throw e.rethrowAsParcelableException(); 2788 } 2789 } 2790 2791 @Override 2792 public void createUserKey(int userId, int serialNumber, boolean ephemeral) { 2793 enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); 2794 waitForReady(); 2795 2796 try { 2797 mCryptConnector.execute("cryptfs", "create_user_key", userId, serialNumber, 2798 ephemeral ? 1 : 0); 2799 } catch (NativeDaemonConnectorException e) { 2800 throw e.rethrowAsParcelableException(); 2801 } 2802 } 2803 2804 @Override 2805 public void destroyUserKey(int userId) { 2806 enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); 2807 waitForReady(); 2808 2809 try { 2810 mCryptConnector.execute("cryptfs", "destroy_user_key", userId); 2811 } catch (NativeDaemonConnectorException e) { 2812 throw e.rethrowAsParcelableException(); 2813 } 2814 } 2815 2816 private SensitiveArg encodeBytes(byte[] bytes) { 2817 if (ArrayUtils.isEmpty(bytes)) { 2818 return new SensitiveArg("!"); 2819 } else { 2820 return new SensitiveArg(HexDump.toHexString(bytes)); 2821 } 2822 } 2823 2824 /* 2825 * Add this token/secret pair to the set of ways we can recover a disk encryption key. 2826 * Changing the token/secret for a disk encryption key is done in two phases: first, adding 2827 * a new token/secret pair with this call, then delting all other pairs with 2828 * fixateNewestUserKeyAuth. This allows other places where a credential is used, such as 2829 * Gatekeeper, to be updated between the two calls. 2830 */ 2831 @Override 2832 public void addUserKeyAuth(int userId, int serialNumber, byte[] token, byte[] secret) { 2833 enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); 2834 waitForReady(); 2835 2836 try { 2837 mCryptConnector.execute("cryptfs", "add_user_key_auth", userId, serialNumber, 2838 encodeBytes(token), encodeBytes(secret)); 2839 } catch (NativeDaemonConnectorException e) { 2840 throw e.rethrowAsParcelableException(); 2841 } 2842 } 2843 2844 /* 2845 * Delete all disk encryption token/secret pairs except the most recently added one 2846 */ 2847 @Override 2848 public void fixateNewestUserKeyAuth(int userId) { 2849 enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); 2850 waitForReady(); 2851 2852 try { 2853 mCryptConnector.execute("cryptfs", "fixate_newest_user_key_auth", userId); 2854 } catch (NativeDaemonConnectorException e) { 2855 throw e.rethrowAsParcelableException(); 2856 } 2857 } 2858 2859 @Override 2860 public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) { 2861 enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); 2862 waitForReady(); 2863 2864 if (StorageManager.isFileEncryptedNativeOrEmulated()) { 2865 // When a user has secure lock screen, require a challenge token to 2866 // actually unlock. This check is mostly in place for emulation mode. 2867 if (mLockPatternUtils.isSecure(userId) && ArrayUtils.isEmpty(token)) { 2868 throw new IllegalStateException("Token required to unlock secure user " + userId); 2869 } 2870 2871 try { 2872 mCryptConnector.execute("cryptfs", "unlock_user_key", userId, serialNumber, 2873 encodeBytes(token), encodeBytes(secret)); 2874 } catch (NativeDaemonConnectorException e) { 2875 throw e.rethrowAsParcelableException(); 2876 } 2877 } 2878 2879 synchronized (mLock) { 2880 mLocalUnlockedUsers = ArrayUtils.appendInt(mLocalUnlockedUsers, userId); 2881 } 2882 } 2883 2884 @Override 2885 public void lockUserKey(int userId) { 2886 enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); 2887 waitForReady(); 2888 2889 try { 2890 mCryptConnector.execute("cryptfs", "lock_user_key", userId); 2891 } catch (NativeDaemonConnectorException e) { 2892 throw e.rethrowAsParcelableException(); 2893 } 2894 2895 synchronized (mLock) { 2896 mLocalUnlockedUsers = ArrayUtils.removeInt(mLocalUnlockedUsers, userId); 2897 } 2898 } 2899 2900 @Override 2901 public boolean isUserKeyUnlocked(int userId) { 2902 synchronized (mLock) { 2903 return ArrayUtils.contains(mLocalUnlockedUsers, userId); 2904 } 2905 } 2906 2907 @Override 2908 public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) { 2909 enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); 2910 waitForReady(); 2911 2912 try { 2913 mCryptConnector.execute("cryptfs", "prepare_user_storage", escapeNull(volumeUuid), 2914 userId, serialNumber, flags); 2915 } catch (NativeDaemonConnectorException e) { 2916 throw e.rethrowAsParcelableException(); 2917 } 2918 } 2919 2920 @Override 2921 public void destroyUserStorage(String volumeUuid, int userId, int flags) { 2922 enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); 2923 waitForReady(); 2924 2925 try { 2926 mCryptConnector.execute("cryptfs", "destroy_user_storage", escapeNull(volumeUuid), 2927 userId, flags); 2928 } catch (NativeDaemonConnectorException e) { 2929 throw e.rethrowAsParcelableException(); 2930 } 2931 } 2932 2933 @Override 2934 public ParcelFileDescriptor mountAppFuse(final String name) throws RemoteException { 2935 try { 2936 final int uid = Binder.getCallingUid(); 2937 final int pid = Binder.getCallingPid(); 2938 final NativeDaemonEvent event = 2939 mConnector.execute("appfuse", "mount", uid, pid, name); 2940 if (event.getFileDescriptors() == null) { 2941 throw new RemoteException("AppFuse FD from vold is null."); 2942 } 2943 return ParcelFileDescriptor.fromFd( 2944 event.getFileDescriptors()[0], 2945 mHandler, 2946 new ParcelFileDescriptor.OnCloseListener() { 2947 @Override 2948 public void onClose(IOException e) { 2949 try { 2950 final NativeDaemonEvent event = mConnector.execute( 2951 "appfuse", "unmount", uid, pid, name); 2952 } catch (NativeDaemonConnectorException unmountException) { 2953 Log.e(TAG, "Failed to unmount appfuse."); 2954 } 2955 } 2956 }); 2957 } catch (NativeDaemonConnectorException e) { 2958 throw e.rethrowAsParcelableException(); 2959 } catch (IOException e) { 2960 throw new RemoteException(e.getMessage()); 2961 } 2962 } 2963 2964 @Override 2965 public int mkdirs(String callingPkg, String appPath) { 2966 final int userId = UserHandle.getUserId(Binder.getCallingUid()); 2967 final UserEnvironment userEnv = new UserEnvironment(userId); 2968 2969 // Validate that reported package name belongs to caller 2970 final AppOpsManager appOps = (AppOpsManager) mContext.getSystemService( 2971 Context.APP_OPS_SERVICE); 2972 appOps.checkPackage(Binder.getCallingUid(), callingPkg); 2973 2974 File appFile = null; 2975 try { 2976 appFile = new File(appPath).getCanonicalFile(); 2977 } catch (IOException e) { 2978 Slog.e(TAG, "Failed to resolve " + appPath + ": " + e); 2979 return -1; 2980 } 2981 2982 // Try translating the app path into a vold path, but require that it 2983 // belong to the calling package. 2984 if (FileUtils.contains(userEnv.buildExternalStorageAppDataDirs(callingPkg), appFile) || 2985 FileUtils.contains(userEnv.buildExternalStorageAppObbDirs(callingPkg), appFile) || 2986 FileUtils.contains(userEnv.buildExternalStorageAppMediaDirs(callingPkg), appFile)) { 2987 appPath = appFile.getAbsolutePath(); 2988 if (!appPath.endsWith("/")) { 2989 appPath = appPath + "/"; 2990 } 2991 2992 try { 2993 mConnector.execute("volume", "mkdirs", appPath); 2994 return 0; 2995 } catch (NativeDaemonConnectorException e) { 2996 return e.getCode(); 2997 } 2998 } 2999 3000 throw new SecurityException("Invalid mkdirs path: " + appFile); 3001 } 3002 3003 @Override 3004 public StorageVolume[] getVolumeList(int uid, String packageName, int flags) { 3005 final int userId = UserHandle.getUserId(uid); 3006 3007 final boolean forWrite = (flags & StorageManager.FLAG_FOR_WRITE) != 0; 3008 final boolean realState = (flags & StorageManager.FLAG_REAL_STATE) != 0; 3009 final boolean includeInvisible = (flags & StorageManager.FLAG_INCLUDE_INVISIBLE) != 0; 3010 3011 final boolean userKeyUnlocked; 3012 final boolean storagePermission; 3013 final long token = Binder.clearCallingIdentity(); 3014 try { 3015 userKeyUnlocked = isUserKeyUnlocked(userId); 3016 storagePermission = mMountServiceInternal.hasExternalStorage(uid, packageName); 3017 } finally { 3018 Binder.restoreCallingIdentity(token); 3019 } 3020 3021 boolean foundPrimary = false; 3022 3023 final ArrayList<StorageVolume> res = new ArrayList<>(); 3024 synchronized (mLock) { 3025 for (int i = 0; i < mVolumes.size(); i++) { 3026 final VolumeInfo vol = mVolumes.valueAt(i); 3027 switch (vol.getType()) { 3028 case VolumeInfo.TYPE_PUBLIC: 3029 case VolumeInfo.TYPE_EMULATED: 3030 break; 3031 default: 3032 continue; 3033 } 3034 3035 boolean match = false; 3036 if (forWrite) { 3037 match = vol.isVisibleForWrite(userId); 3038 } else { 3039 match = vol.isVisibleForRead(userId) 3040 || (includeInvisible && vol.getPath() != null); 3041 } 3042 if (!match) continue; 3043 3044 boolean reportUnmounted = false; 3045 if ((vol.getType() == VolumeInfo.TYPE_EMULATED) && !userKeyUnlocked) { 3046 reportUnmounted = true; 3047 } else if (!storagePermission && !realState) { 3048 reportUnmounted = true; 3049 } 3050 3051 final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, 3052 reportUnmounted); 3053 if (vol.isPrimary()) { 3054 res.add(0, userVol); 3055 foundPrimary = true; 3056 } else { 3057 res.add(userVol); 3058 } 3059 } 3060 } 3061 3062 if (!foundPrimary) { 3063 Log.w(TAG, "No primary storage defined yet; hacking together a stub"); 3064 3065 final boolean primaryPhysical = SystemProperties.getBoolean( 3066 StorageManager.PROP_PRIMARY_PHYSICAL, false); 3067 3068 final String id = "stub_primary"; 3069 final File path = Environment.getLegacyExternalStorageDirectory(); 3070 final String description = mContext.getString(android.R.string.unknownName); 3071 final boolean primary = true; 3072 final boolean removable = primaryPhysical; 3073 final boolean emulated = !primaryPhysical; 3074 final long mtpReserveSize = 0L; 3075 final boolean allowMassStorage = false; 3076 final long maxFileSize = 0L; 3077 final UserHandle owner = new UserHandle(userId); 3078 final String uuid = null; 3079 final String state = Environment.MEDIA_REMOVED; 3080 3081 res.add(0, new StorageVolume(id, StorageVolume.STORAGE_ID_INVALID, path, 3082 description, primary, removable, emulated, mtpReserveSize, 3083 allowMassStorage, maxFileSize, owner, uuid, state)); 3084 } 3085 3086 return res.toArray(new StorageVolume[res.size()]); 3087 } 3088 3089 @Override 3090 public DiskInfo[] getDisks() { 3091 synchronized (mLock) { 3092 final DiskInfo[] res = new DiskInfo[mDisks.size()]; 3093 for (int i = 0; i < mDisks.size(); i++) { 3094 res[i] = mDisks.valueAt(i); 3095 } 3096 return res; 3097 } 3098 } 3099 3100 @Override 3101 public VolumeInfo[] getVolumes(int flags) { 3102 synchronized (mLock) { 3103 final VolumeInfo[] res = new VolumeInfo[mVolumes.size()]; 3104 for (int i = 0; i < mVolumes.size(); i++) { 3105 res[i] = mVolumes.valueAt(i); 3106 } 3107 return res; 3108 } 3109 } 3110 3111 @Override 3112 public VolumeRecord[] getVolumeRecords(int flags) { 3113 synchronized (mLock) { 3114 final VolumeRecord[] res = new VolumeRecord[mRecords.size()]; 3115 for (int i = 0; i < mRecords.size(); i++) { 3116 res[i] = mRecords.valueAt(i); 3117 } 3118 return res; 3119 } 3120 } 3121 3122 private void addObbStateLocked(ObbState obbState) throws RemoteException { 3123 final IBinder binder = obbState.getBinder(); 3124 List<ObbState> obbStates = mObbMounts.get(binder); 3125 3126 if (obbStates == null) { 3127 obbStates = new ArrayList<ObbState>(); 3128 mObbMounts.put(binder, obbStates); 3129 } else { 3130 for (final ObbState o : obbStates) { 3131 if (o.rawPath.equals(obbState.rawPath)) { 3132 throw new IllegalStateException("Attempt to add ObbState twice. " 3133 + "This indicates an error in the MountService logic."); 3134 } 3135 } 3136 } 3137 3138 obbStates.add(obbState); 3139 try { 3140 obbState.link(); 3141 } catch (RemoteException e) { 3142 /* 3143 * The binder died before we could link it, so clean up our state 3144 * and return failure. 3145 */ 3146 obbStates.remove(obbState); 3147 if (obbStates.isEmpty()) { 3148 mObbMounts.remove(binder); 3149 } 3150 3151 // Rethrow the error so mountObb can get it 3152 throw e; 3153 } 3154 3155 mObbPathToStateMap.put(obbState.rawPath, obbState); 3156 } 3157 3158 private void removeObbStateLocked(ObbState obbState) { 3159 final IBinder binder = obbState.getBinder(); 3160 final List<ObbState> obbStates = mObbMounts.get(binder); 3161 if (obbStates != null) { 3162 if (obbStates.remove(obbState)) { 3163 obbState.unlink(); 3164 } 3165 if (obbStates.isEmpty()) { 3166 mObbMounts.remove(binder); 3167 } 3168 } 3169 3170 mObbPathToStateMap.remove(obbState.rawPath); 3171 } 3172 3173 private class ObbActionHandler extends Handler { 3174 private boolean mBound = false; 3175 private final List<ObbAction> mActions = new LinkedList<ObbAction>(); 3176 3177 ObbActionHandler(Looper l) { 3178 super(l); 3179 } 3180 3181 @Override 3182 public void handleMessage(Message msg) { 3183 switch (msg.what) { 3184 case OBB_RUN_ACTION: { 3185 final ObbAction action = (ObbAction) msg.obj; 3186 3187 if (DEBUG_OBB) 3188 Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString()); 3189 3190 // If a bind was already initiated we don't really 3191 // need to do anything. The pending install 3192 // will be processed later on. 3193 if (!mBound) { 3194 // If this is the only one pending we might 3195 // have to bind to the service again. 3196 if (!connectToService()) { 3197 Slog.e(TAG, "Failed to bind to media container service"); 3198 action.handleError(); 3199 return; 3200 } 3201 } 3202 3203 mActions.add(action); 3204 break; 3205 } 3206 case OBB_MCS_BOUND: { 3207 if (DEBUG_OBB) 3208 Slog.i(TAG, "OBB_MCS_BOUND"); 3209 if (msg.obj != null) { 3210 mContainerService = (IMediaContainerService) msg.obj; 3211 } 3212 if (mContainerService == null) { 3213 // Something seriously wrong. Bail out 3214 Slog.e(TAG, "Cannot bind to media container service"); 3215 for (ObbAction action : mActions) { 3216 // Indicate service bind error 3217 action.handleError(); 3218 } 3219 mActions.clear(); 3220 } else if (mActions.size() > 0) { 3221 final ObbAction action = mActions.get(0); 3222 if (action != null) { 3223 action.execute(this); 3224 } 3225 } else { 3226 // Should never happen ideally. 3227 Slog.w(TAG, "Empty queue"); 3228 } 3229 break; 3230 } 3231 case OBB_MCS_RECONNECT: { 3232 if (DEBUG_OBB) 3233 Slog.i(TAG, "OBB_MCS_RECONNECT"); 3234 if (mActions.size() > 0) { 3235 if (mBound) { 3236 disconnectService(); 3237 } 3238 if (!connectToService()) { 3239 Slog.e(TAG, "Failed to bind to media container service"); 3240 for (ObbAction action : mActions) { 3241 // Indicate service bind error 3242 action.handleError(); 3243 } 3244 mActions.clear(); 3245 } 3246 } 3247 break; 3248 } 3249 case OBB_MCS_UNBIND: { 3250 if (DEBUG_OBB) 3251 Slog.i(TAG, "OBB_MCS_UNBIND"); 3252 3253 // Delete pending install 3254 if (mActions.size() > 0) { 3255 mActions.remove(0); 3256 } 3257 if (mActions.size() == 0) { 3258 if (mBound) { 3259 disconnectService(); 3260 } 3261 } else { 3262 // There are more pending requests in queue. 3263 // Just post MCS_BOUND message to trigger processing 3264 // of next pending install. 3265 mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND); 3266 } 3267 break; 3268 } 3269 case OBB_FLUSH_MOUNT_STATE: { 3270 final String path = (String) msg.obj; 3271 3272 if (DEBUG_OBB) 3273 Slog.i(TAG, "Flushing all OBB state for path " + path); 3274 3275 synchronized (mObbMounts) { 3276 final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>(); 3277 3278 final Iterator<ObbState> i = mObbPathToStateMap.values().iterator(); 3279 while (i.hasNext()) { 3280 final ObbState state = i.next(); 3281 3282 /* 3283 * If this entry's source file is in the volume path 3284 * that got unmounted, remove it because it's no 3285 * longer valid. 3286 */ 3287 if (state.canonicalPath.startsWith(path)) { 3288 obbStatesToRemove.add(state); 3289 } 3290 } 3291 3292 for (final ObbState obbState : obbStatesToRemove) { 3293 if (DEBUG_OBB) 3294 Slog.i(TAG, "Removing state for " + obbState.rawPath); 3295 3296 removeObbStateLocked(obbState); 3297 3298 try { 3299 obbState.token.onObbResult(obbState.rawPath, obbState.nonce, 3300 OnObbStateChangeListener.UNMOUNTED); 3301 } catch (RemoteException e) { 3302 Slog.i(TAG, "Couldn't send unmount notification for OBB: " 3303 + obbState.rawPath); 3304 } 3305 } 3306 } 3307 break; 3308 } 3309 } 3310 } 3311 3312 private boolean connectToService() { 3313 if (DEBUG_OBB) 3314 Slog.i(TAG, "Trying to bind to DefaultContainerService"); 3315 3316 Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT); 3317 if (mContext.bindServiceAsUser(service, mDefContainerConn, Context.BIND_AUTO_CREATE, 3318 UserHandle.SYSTEM)) { 3319 mBound = true; 3320 return true; 3321 } 3322 return false; 3323 } 3324 3325 private void disconnectService() { 3326 mContainerService = null; 3327 mBound = false; 3328 mContext.unbindService(mDefContainerConn); 3329 } 3330 } 3331 3332 abstract class ObbAction { 3333 private static final int MAX_RETRIES = 3; 3334 private int mRetries; 3335 3336 ObbState mObbState; 3337 3338 ObbAction(ObbState obbState) { 3339 mObbState = obbState; 3340 } 3341 3342 public void execute(ObbActionHandler handler) { 3343 try { 3344 if (DEBUG_OBB) 3345 Slog.i(TAG, "Starting to execute action: " + toString()); 3346 mRetries++; 3347 if (mRetries > MAX_RETRIES) { 3348 Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up"); 3349 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND); 3350 handleError(); 3351 } else { 3352 handleExecute(); 3353 if (DEBUG_OBB) 3354 Slog.i(TAG, "Posting install MCS_UNBIND"); 3355 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND); 3356 } 3357 } catch (RemoteException e) { 3358 if (DEBUG_OBB) 3359 Slog.i(TAG, "Posting install MCS_RECONNECT"); 3360 mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT); 3361 } catch (Exception e) { 3362 if (DEBUG_OBB) 3363 Slog.d(TAG, "Error handling OBB action", e); 3364 handleError(); 3365 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND); 3366 } 3367 } 3368 3369 abstract void handleExecute() throws RemoteException, IOException; 3370 abstract void handleError(); 3371 3372 protected ObbInfo getObbInfo() throws IOException { 3373 ObbInfo obbInfo; 3374 try { 3375 obbInfo = mContainerService.getObbInfo(mObbState.canonicalPath); 3376 } catch (RemoteException e) { 3377 Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for " 3378 + mObbState.canonicalPath); 3379 obbInfo = null; 3380 } 3381 if (obbInfo == null) { 3382 throw new IOException("Couldn't read OBB file: " + mObbState.canonicalPath); 3383 } 3384 return obbInfo; 3385 } 3386 3387 protected void sendNewStatusOrIgnore(int status) { 3388 if (mObbState == null || mObbState.token == null) { 3389 return; 3390 } 3391 3392 try { 3393 mObbState.token.onObbResult(mObbState.rawPath, mObbState.nonce, status); 3394 } catch (RemoteException e) { 3395 Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged"); 3396 } 3397 } 3398 } 3399 3400 class MountObbAction extends ObbAction { 3401 private final String mKey; 3402 private final int mCallingUid; 3403 3404 MountObbAction(ObbState obbState, String key, int callingUid) { 3405 super(obbState); 3406 mKey = key; 3407 mCallingUid = callingUid; 3408 } 3409 3410 @Override 3411 public void handleExecute() throws IOException, RemoteException { 3412 waitForReady(); 3413 warnOnNotMounted(); 3414 3415 final ObbInfo obbInfo = getObbInfo(); 3416 3417 if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mCallingUid)) { 3418 Slog.w(TAG, "Denied attempt to mount OBB " + obbInfo.filename 3419 + " which is owned by " + obbInfo.packageName); 3420 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED); 3421 return; 3422 } 3423 3424 final boolean isMounted; 3425 synchronized (mObbMounts) { 3426 isMounted = mObbPathToStateMap.containsKey(mObbState.rawPath); 3427 } 3428 if (isMounted) { 3429 Slog.w(TAG, "Attempt to mount OBB which is already mounted: " + obbInfo.filename); 3430 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_ALREADY_MOUNTED); 3431 return; 3432 } 3433 3434 final String hashedKey; 3435 if (mKey == null) { 3436 hashedKey = "none"; 3437 } else { 3438 try { 3439 SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); 3440 3441 KeySpec ks = new PBEKeySpec(mKey.toCharArray(), obbInfo.salt, 3442 PBKDF2_HASH_ROUNDS, CRYPTO_ALGORITHM_KEY_SIZE); 3443 SecretKey key = factory.generateSecret(ks); 3444 BigInteger bi = new BigInteger(key.getEncoded()); 3445 hashedKey = bi.toString(16); 3446 } catch (NoSuchAlgorithmException e) { 3447 Slog.e(TAG, "Could not load PBKDF2 algorithm", e); 3448 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL); 3449 return; 3450 } catch (InvalidKeySpecException e) { 3451 Slog.e(TAG, "Invalid key spec when loading PBKDF2 algorithm", e); 3452 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL); 3453 return; 3454 } 3455 } 3456 3457 int rc = StorageResultCode.OperationSucceeded; 3458 try { 3459 mConnector.execute("obb", "mount", mObbState.canonicalPath, new SensitiveArg(hashedKey), 3460 mObbState.ownerGid); 3461 } catch (NativeDaemonConnectorException e) { 3462 int code = e.getCode(); 3463 if (code != VoldResponseCode.OpFailedStorageBusy) { 3464 rc = StorageResultCode.OperationFailedInternalError; 3465 } 3466 } 3467 3468 if (rc == StorageResultCode.OperationSucceeded) { 3469 if (DEBUG_OBB) 3470 Slog.d(TAG, "Successfully mounted OBB " + mObbState.canonicalPath); 3471 3472 synchronized (mObbMounts) { 3473 addObbStateLocked(mObbState); 3474 } 3475 3476 sendNewStatusOrIgnore(OnObbStateChangeListener.MOUNTED); 3477 } else { 3478 Slog.e(TAG, "Couldn't mount OBB file: " + rc); 3479 3480 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT); 3481 } 3482 } 3483 3484 @Override 3485 public void handleError() { 3486 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL); 3487 } 3488 3489 @Override 3490 public String toString() { 3491 StringBuilder sb = new StringBuilder(); 3492 sb.append("MountObbAction{"); 3493 sb.append(mObbState); 3494 sb.append('}'); 3495 return sb.toString(); 3496 } 3497 } 3498 3499 class UnmountObbAction extends ObbAction { 3500 private final boolean mForceUnmount; 3501 3502 UnmountObbAction(ObbState obbState, boolean force) { 3503 super(obbState); 3504 mForceUnmount = force; 3505 } 3506 3507 @Override 3508 public void handleExecute() throws IOException { 3509 waitForReady(); 3510 warnOnNotMounted(); 3511 3512 final ObbState existingState; 3513 synchronized (mObbMounts) { 3514 existingState = mObbPathToStateMap.get(mObbState.rawPath); 3515 } 3516 3517 if (existingState == null) { 3518 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_NOT_MOUNTED); 3519 return; 3520 } 3521 3522 if (existingState.ownerGid != mObbState.ownerGid) { 3523 Slog.w(TAG, "Permission denied attempting to unmount OBB " + existingState.rawPath 3524 + " (owned by GID " + existingState.ownerGid + ")"); 3525 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED); 3526 return; 3527 } 3528 3529 int rc = StorageResultCode.OperationSucceeded; 3530 try { 3531 final Command cmd = new Command("obb", "unmount", mObbState.canonicalPath); 3532 if (mForceUnmount) { 3533 cmd.appendArg("force"); 3534 } 3535 mConnector.execute(cmd); 3536 } catch (NativeDaemonConnectorException e) { 3537 int code = e.getCode(); 3538 if (code == VoldResponseCode.OpFailedStorageBusy) { 3539 rc = StorageResultCode.OperationFailedStorageBusy; 3540 } else if (code == VoldResponseCode.OpFailedStorageNotFound) { 3541 // If it's not mounted then we've already won. 3542 rc = StorageResultCode.OperationSucceeded; 3543 } else { 3544 rc = StorageResultCode.OperationFailedInternalError; 3545 } 3546 } 3547 3548 if (rc == StorageResultCode.OperationSucceeded) { 3549 synchronized (mObbMounts) { 3550 removeObbStateLocked(existingState); 3551 } 3552 3553 sendNewStatusOrIgnore(OnObbStateChangeListener.UNMOUNTED); 3554 } else { 3555 Slog.w(TAG, "Could not unmount OBB: " + existingState); 3556 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT); 3557 } 3558 } 3559 3560 @Override 3561 public void handleError() { 3562 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL); 3563 } 3564 3565 @Override 3566 public String toString() { 3567 StringBuilder sb = new StringBuilder(); 3568 sb.append("UnmountObbAction{"); 3569 sb.append(mObbState); 3570 sb.append(",force="); 3571 sb.append(mForceUnmount); 3572 sb.append('}'); 3573 return sb.toString(); 3574 } 3575 } 3576 3577 private static class Callbacks extends Handler { 3578 private static final int MSG_STORAGE_STATE_CHANGED = 1; 3579 private static final int MSG_VOLUME_STATE_CHANGED = 2; 3580 private static final int MSG_VOLUME_RECORD_CHANGED = 3; 3581 private static final int MSG_VOLUME_FORGOTTEN = 4; 3582 private static final int MSG_DISK_SCANNED = 5; 3583 private static final int MSG_DISK_DESTROYED = 6; 3584 3585 private final RemoteCallbackList<IMountServiceListener> 3586 mCallbacks = new RemoteCallbackList<>(); 3587 3588 public Callbacks(Looper looper) { 3589 super(looper); 3590 } 3591 3592 public void register(IMountServiceListener callback) { 3593 mCallbacks.register(callback); 3594 } 3595 3596 public void unregister(IMountServiceListener callback) { 3597 mCallbacks.unregister(callback); 3598 } 3599 3600 @Override 3601 public void handleMessage(Message msg) { 3602 final SomeArgs args = (SomeArgs) msg.obj; 3603 final int n = mCallbacks.beginBroadcast(); 3604 for (int i = 0; i < n; i++) { 3605 final IMountServiceListener callback = mCallbacks.getBroadcastItem(i); 3606 try { 3607 invokeCallback(callback, msg.what, args); 3608 } catch (RemoteException ignored) { 3609 } 3610 } 3611 mCallbacks.finishBroadcast(); 3612 args.recycle(); 3613 } 3614 3615 private void invokeCallback(IMountServiceListener callback, int what, SomeArgs args) 3616 throws RemoteException { 3617 switch (what) { 3618 case MSG_STORAGE_STATE_CHANGED: { 3619 callback.onStorageStateChanged((String) args.arg1, (String) args.arg2, 3620 (String) args.arg3); 3621 break; 3622 } 3623 case MSG_VOLUME_STATE_CHANGED: { 3624 callback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3); 3625 break; 3626 } 3627 case MSG_VOLUME_RECORD_CHANGED: { 3628 callback.onVolumeRecordChanged((VolumeRecord) args.arg1); 3629 break; 3630 } 3631 case MSG_VOLUME_FORGOTTEN: { 3632 callback.onVolumeForgotten((String) args.arg1); 3633 break; 3634 } 3635 case MSG_DISK_SCANNED: { 3636 callback.onDiskScanned((DiskInfo) args.arg1, args.argi2); 3637 break; 3638 } 3639 case MSG_DISK_DESTROYED: { 3640 callback.onDiskDestroyed((DiskInfo) args.arg1); 3641 break; 3642 } 3643 } 3644 } 3645 3646 private void notifyStorageStateChanged(String path, String oldState, String newState) { 3647 final SomeArgs args = SomeArgs.obtain(); 3648 args.arg1 = path; 3649 args.arg2 = oldState; 3650 args.arg3 = newState; 3651 obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget(); 3652 } 3653 3654 private void notifyVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { 3655 final SomeArgs args = SomeArgs.obtain(); 3656 args.arg1 = vol.clone(); 3657 args.argi2 = oldState; 3658 args.argi3 = newState; 3659 obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget(); 3660 } 3661 3662 private void notifyVolumeRecordChanged(VolumeRecord rec) { 3663 final SomeArgs args = SomeArgs.obtain(); 3664 args.arg1 = rec.clone(); 3665 obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget(); 3666 } 3667 3668 private void notifyVolumeForgotten(String fsUuid) { 3669 final SomeArgs args = SomeArgs.obtain(); 3670 args.arg1 = fsUuid; 3671 obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget(); 3672 } 3673 3674 private void notifyDiskScanned(DiskInfo disk, int volumeCount) { 3675 final SomeArgs args = SomeArgs.obtain(); 3676 args.arg1 = disk.clone(); 3677 args.argi2 = volumeCount; 3678 obtainMessage(MSG_DISK_SCANNED, args).sendToTarget(); 3679 } 3680 3681 private void notifyDiskDestroyed(DiskInfo disk) { 3682 final SomeArgs args = SomeArgs.obtain(); 3683 args.arg1 = disk.clone(); 3684 obtainMessage(MSG_DISK_DESTROYED, args).sendToTarget(); 3685 } 3686 } 3687 3688 @Override 3689 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 3690 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); 3691 3692 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ", 160); 3693 synchronized (mLock) { 3694 pw.println("Disks:"); 3695 pw.increaseIndent(); 3696 for (int i = 0; i < mDisks.size(); i++) { 3697 final DiskInfo disk = mDisks.valueAt(i); 3698 disk.dump(pw); 3699 } 3700 pw.decreaseIndent(); 3701 3702 pw.println(); 3703 pw.println("Volumes:"); 3704 pw.increaseIndent(); 3705 for (int i = 0; i < mVolumes.size(); i++) { 3706 final VolumeInfo vol = mVolumes.valueAt(i); 3707 if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.id)) continue; 3708 vol.dump(pw); 3709 } 3710 pw.decreaseIndent(); 3711 3712 pw.println(); 3713 pw.println("Records:"); 3714 pw.increaseIndent(); 3715 for (int i = 0; i < mRecords.size(); i++) { 3716 final VolumeRecord note = mRecords.valueAt(i); 3717 note.dump(pw); 3718 } 3719 pw.decreaseIndent(); 3720 3721 pw.println(); 3722 pw.println("Primary storage UUID: " + mPrimaryStorageUuid); 3723 pw.println("Force adoptable: " + mForceAdoptable); 3724 pw.println(); 3725 pw.println("Local unlocked users: " + Arrays.toString(mLocalUnlockedUsers)); 3726 pw.println("System unlocked users: " + Arrays.toString(mSystemUnlockedUsers)); 3727 } 3728 3729 synchronized (mObbMounts) { 3730 pw.println(); 3731 pw.println("mObbMounts:"); 3732 pw.increaseIndent(); 3733 final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet() 3734 .iterator(); 3735 while (binders.hasNext()) { 3736 Entry<IBinder, List<ObbState>> e = binders.next(); 3737 pw.println(e.getKey() + ":"); 3738 pw.increaseIndent(); 3739 final List<ObbState> obbStates = e.getValue(); 3740 for (final ObbState obbState : obbStates) { 3741 pw.println(obbState); 3742 } 3743 pw.decreaseIndent(); 3744 } 3745 pw.decreaseIndent(); 3746 3747 pw.println(); 3748 pw.println("mObbPathToStateMap:"); 3749 pw.increaseIndent(); 3750 final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator(); 3751 while (maps.hasNext()) { 3752 final Entry<String, ObbState> e = maps.next(); 3753 pw.print(e.getKey()); 3754 pw.print(" -> "); 3755 pw.println(e.getValue()); 3756 } 3757 pw.decreaseIndent(); 3758 } 3759 3760 pw.println(); 3761 pw.println("mConnector:"); 3762 pw.increaseIndent(); 3763 mConnector.dump(fd, pw, args); 3764 pw.decreaseIndent(); 3765 3766 pw.println(); 3767 pw.println("mCryptConnector:"); 3768 pw.increaseIndent(); 3769 mCryptConnector.dump(fd, pw, args); 3770 pw.decreaseIndent(); 3771 3772 pw.println(); 3773 pw.print("Last maintenance: "); 3774 pw.println(TimeUtils.formatForLogging(mLastMaintenance)); 3775 } 3776 3777 /** {@inheritDoc} */ 3778 @Override 3779 public void monitor() { 3780 if (mConnector != null) { 3781 mConnector.monitor(); 3782 } 3783 if (mCryptConnector != null) { 3784 mCryptConnector.monitor(); 3785 } 3786 } 3787 3788 private final class MountServiceInternalImpl extends MountServiceInternal { 3789 // Not guarded by a lock. 3790 private final CopyOnWriteArrayList<ExternalStorageMountPolicy> mPolicies = 3791 new CopyOnWriteArrayList<>(); 3792 3793 @Override 3794 public void addExternalStoragePolicy(ExternalStorageMountPolicy policy) { 3795 // No locking - CopyOnWriteArrayList 3796 mPolicies.add(policy); 3797 } 3798 3799 @Override 3800 public void onExternalStoragePolicyChanged(int uid, String packageName) { 3801 final int mountMode = getExternalStorageMountMode(uid, packageName); 3802 remountUidExternalStorage(uid, mountMode); 3803 } 3804 3805 @Override 3806 public int getExternalStorageMountMode(int uid, String packageName) { 3807 // No locking - CopyOnWriteArrayList 3808 int mountMode = Integer.MAX_VALUE; 3809 for (ExternalStorageMountPolicy policy : mPolicies) { 3810 final int policyMode = policy.getMountMode(uid, packageName); 3811 if (policyMode == Zygote.MOUNT_EXTERNAL_NONE) { 3812 return Zygote.MOUNT_EXTERNAL_NONE; 3813 } 3814 mountMode = Math.min(mountMode, policyMode); 3815 } 3816 if (mountMode == Integer.MAX_VALUE) { 3817 return Zygote.MOUNT_EXTERNAL_NONE; 3818 } 3819 return mountMode; 3820 } 3821 3822 public boolean hasExternalStorage(int uid, String packageName) { 3823 // No need to check for system uid. This avoids a deadlock between 3824 // PackageManagerService and AppOpsService. 3825 if (uid == Process.SYSTEM_UID) { 3826 return true; 3827 } 3828 // No locking - CopyOnWriteArrayList 3829 for (ExternalStorageMountPolicy policy : mPolicies) { 3830 final boolean policyHasStorage = policy.hasExternalStorage(uid, packageName); 3831 if (!policyHasStorage) { 3832 return false; 3833 } 3834 } 3835 return true; 3836 } 3837 } 3838 } 3839