1/* 2 * Copyright (C) 2007 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server; 18 19import static com.android.internal.util.XmlUtils.readBooleanAttribute; 20import static com.android.internal.util.XmlUtils.readIntAttribute; 21import static com.android.internal.util.XmlUtils.readLongAttribute; 22import static com.android.internal.util.XmlUtils.readStringAttribute; 23import static com.android.internal.util.XmlUtils.writeBooleanAttribute; 24import static com.android.internal.util.XmlUtils.writeIntAttribute; 25import static com.android.internal.util.XmlUtils.writeLongAttribute; 26import static com.android.internal.util.XmlUtils.writeStringAttribute; 27 28import static org.xmlpull.v1.XmlPullParser.END_DOCUMENT; 29import static org.xmlpull.v1.XmlPullParser.START_TAG; 30 31import android.Manifest; 32import android.annotation.Nullable; 33import android.app.ActivityManager; 34import android.app.ActivityManagerNative; 35import android.app.AppOpsManager; 36import android.app.IActivityManager; 37import android.content.BroadcastReceiver; 38import android.content.ComponentName; 39import android.content.Context; 40import android.content.Intent; 41import android.content.IntentFilter; 42import android.content.ServiceConnection; 43import android.content.pm.IPackageMoveObserver; 44import android.content.pm.PackageManager; 45import android.content.pm.ProviderInfo; 46import android.content.pm.UserInfo; 47import android.content.res.Configuration; 48import android.content.res.ObbInfo; 49import android.net.Uri; 50import android.os.Binder; 51import android.os.DropBoxManager; 52import android.os.Environment; 53import android.os.Environment.UserEnvironment; 54import android.os.FileUtils; 55import android.os.Handler; 56import android.os.HandlerThread; 57import android.os.IBinder; 58import android.os.Looper; 59import android.os.Message; 60import android.os.ParcelFileDescriptor; 61import android.os.PowerManager; 62import android.os.Process; 63import android.os.RemoteCallbackList; 64import android.os.RemoteException; 65import android.os.ServiceManager; 66import android.os.SystemClock; 67import android.os.SystemProperties; 68import android.os.UserHandle; 69import android.os.UserManager; 70import android.os.storage.DiskInfo; 71import android.os.storage.IMountService; 72import android.os.storage.IMountServiceListener; 73import android.os.storage.IMountShutdownObserver; 74import android.os.storage.IObbActionListener; 75import android.os.storage.MountServiceInternal; 76import android.os.storage.OnObbStateChangeListener; 77import android.os.storage.StorageManager; 78import android.os.storage.StorageResultCode; 79import android.os.storage.StorageVolume; 80import android.os.storage.VolumeInfo; 81import android.os.storage.VolumeRecord; 82import android.provider.MediaStore; 83import android.provider.Settings; 84import android.text.TextUtils; 85import android.text.format.DateUtils; 86import android.util.ArrayMap; 87import android.util.AtomicFile; 88import android.util.Log; 89import android.util.Slog; 90import android.util.TimeUtils; 91import android.util.Xml; 92 93import com.android.internal.annotations.GuardedBy; 94import com.android.internal.app.IMediaContainerService; 95import com.android.internal.os.SomeArgs; 96import com.android.internal.os.Zygote; 97import com.android.internal.util.ArrayUtils; 98import com.android.internal.util.FastXmlSerializer; 99import com.android.internal.util.HexDump; 100import com.android.internal.util.IndentingPrintWriter; 101import com.android.internal.util.Preconditions; 102import com.android.internal.widget.LockPatternUtils; 103import com.android.server.NativeDaemonConnector.Command; 104import com.android.server.NativeDaemonConnector.SensitiveArg; 105import com.android.server.pm.PackageManagerService; 106 107import libcore.io.IoUtils; 108import libcore.util.EmptyArray; 109 110import org.xmlpull.v1.XmlPullParser; 111import org.xmlpull.v1.XmlPullParserException; 112import org.xmlpull.v1.XmlSerializer; 113 114import java.io.File; 115import java.io.FileDescriptor; 116import java.io.FileInputStream; 117import java.io.FileNotFoundException; 118import java.io.FileOutputStream; 119import java.io.IOException; 120import java.io.PrintWriter; 121import java.math.BigInteger; 122import java.nio.charset.StandardCharsets; 123import java.security.NoSuchAlgorithmException; 124import java.security.spec.InvalidKeySpecException; 125import java.security.spec.KeySpec; 126import java.util.ArrayList; 127import java.util.Arrays; 128import java.util.HashMap; 129import java.util.HashSet; 130import java.util.Iterator; 131import java.util.LinkedList; 132import java.util.List; 133import java.util.Locale; 134import java.util.Map; 135import java.util.Map.Entry; 136import java.util.Objects; 137import java.util.concurrent.CopyOnWriteArrayList; 138import java.util.concurrent.CountDownLatch; 139import java.util.concurrent.TimeUnit; 140import java.util.concurrent.TimeoutException; 141 142import javax.crypto.SecretKey; 143import javax.crypto.SecretKeyFactory; 144import 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 */ 151class 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 // Adoptable storage isn't currently supported on FBE devices 1060 if (StorageManager.isFileEncryptedNativeOnly()) { 1061 flags &= ~DiskInfo.FLAG_ADOPTABLE; 1062 } 1063 mDisks.put(id, new DiskInfo(id, flags)); 1064 break; 1065 } 1066 case VoldResponseCode.DISK_SIZE_CHANGED: { 1067 if (cooked.length != 3) break; 1068 final DiskInfo disk = mDisks.get(cooked[1]); 1069 if (disk != null) { 1070 disk.size = Long.parseLong(cooked[2]); 1071 } 1072 break; 1073 } 1074 case VoldResponseCode.DISK_LABEL_CHANGED: { 1075 final DiskInfo disk = mDisks.get(cooked[1]); 1076 if (disk != null) { 1077 final StringBuilder builder = new StringBuilder(); 1078 for (int i = 2; i < cooked.length; i++) { 1079 builder.append(cooked[i]).append(' '); 1080 } 1081 disk.label = builder.toString().trim(); 1082 } 1083 break; 1084 } 1085 case VoldResponseCode.DISK_SCANNED: { 1086 if (cooked.length != 2) break; 1087 final DiskInfo disk = mDisks.get(cooked[1]); 1088 if (disk != null) { 1089 onDiskScannedLocked(disk); 1090 } 1091 break; 1092 } 1093 case VoldResponseCode.DISK_SYS_PATH_CHANGED: { 1094 if (cooked.length != 3) break; 1095 final DiskInfo disk = mDisks.get(cooked[1]); 1096 if (disk != null) { 1097 disk.sysPath = cooked[2]; 1098 } 1099 break; 1100 } 1101 case VoldResponseCode.DISK_DESTROYED: { 1102 if (cooked.length != 2) break; 1103 final DiskInfo disk = mDisks.remove(cooked[1]); 1104 if (disk != null) { 1105 mCallbacks.notifyDiskDestroyed(disk); 1106 } 1107 break; 1108 } 1109 1110 case VoldResponseCode.VOLUME_CREATED: { 1111 final String id = cooked[1]; 1112 final int type = Integer.parseInt(cooked[2]); 1113 final String diskId = TextUtils.nullIfEmpty(cooked[3]); 1114 final String partGuid = TextUtils.nullIfEmpty(cooked[4]); 1115 1116 final DiskInfo disk = mDisks.get(diskId); 1117 final VolumeInfo vol = new VolumeInfo(id, type, disk, partGuid); 1118 mVolumes.put(id, vol); 1119 onVolumeCreatedLocked(vol); 1120 break; 1121 } 1122 case VoldResponseCode.VOLUME_STATE_CHANGED: { 1123 if (cooked.length != 3) break; 1124 final VolumeInfo vol = mVolumes.get(cooked[1]); 1125 if (vol != null) { 1126 final int oldState = vol.state; 1127 final int newState = Integer.parseInt(cooked[2]); 1128 vol.state = newState; 1129 onVolumeStateChangedLocked(vol, oldState, newState); 1130 } 1131 break; 1132 } 1133 case VoldResponseCode.VOLUME_FS_TYPE_CHANGED: { 1134 if (cooked.length != 3) break; 1135 final VolumeInfo vol = mVolumes.get(cooked[1]); 1136 if (vol != null) { 1137 vol.fsType = cooked[2]; 1138 } 1139 break; 1140 } 1141 case VoldResponseCode.VOLUME_FS_UUID_CHANGED: { 1142 if (cooked.length != 3) break; 1143 final VolumeInfo vol = mVolumes.get(cooked[1]); 1144 if (vol != null) { 1145 vol.fsUuid = cooked[2]; 1146 } 1147 break; 1148 } 1149 case VoldResponseCode.VOLUME_FS_LABEL_CHANGED: { 1150 final VolumeInfo vol = mVolumes.get(cooked[1]); 1151 if (vol != null) { 1152 final StringBuilder builder = new StringBuilder(); 1153 for (int i = 2; i < cooked.length; i++) { 1154 builder.append(cooked[i]).append(' '); 1155 } 1156 vol.fsLabel = builder.toString().trim(); 1157 } 1158 // TODO: notify listeners that label changed 1159 break; 1160 } 1161 case VoldResponseCode.VOLUME_PATH_CHANGED: { 1162 if (cooked.length != 3) break; 1163 final VolumeInfo vol = mVolumes.get(cooked[1]); 1164 if (vol != null) { 1165 vol.path = cooked[2]; 1166 } 1167 break; 1168 } 1169 case VoldResponseCode.VOLUME_INTERNAL_PATH_CHANGED: { 1170 if (cooked.length != 3) break; 1171 final VolumeInfo vol = mVolumes.get(cooked[1]); 1172 if (vol != null) { 1173 vol.internalPath = cooked[2]; 1174 } 1175 break; 1176 } 1177 case VoldResponseCode.VOLUME_DESTROYED: { 1178 if (cooked.length != 2) break; 1179 mVolumes.remove(cooked[1]); 1180 break; 1181 } 1182 1183 case VoldResponseCode.MOVE_STATUS: { 1184 final int status = Integer.parseInt(cooked[1]); 1185 onMoveStatusLocked(status); 1186 break; 1187 } 1188 case VoldResponseCode.BENCHMARK_RESULT: { 1189 if (cooked.length != 7) break; 1190 final String path = cooked[1]; 1191 final String ident = cooked[2]; 1192 final long create = Long.parseLong(cooked[3]); 1193 final long drop = Long.parseLong(cooked[4]); 1194 final long run = Long.parseLong(cooked[5]); 1195 final long destroy = Long.parseLong(cooked[6]); 1196 1197 final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class); 1198 dropBox.addText(TAG_STORAGE_BENCHMARK, scrubPath(path) 1199 + " " + ident + " " + create + " " + run + " " + destroy); 1200 1201 final VolumeRecord rec = findRecordForPath(path); 1202 if (rec != null) { 1203 rec.lastBenchMillis = System.currentTimeMillis(); 1204 writeSettingsLocked(); 1205 } 1206 1207 break; 1208 } 1209 case VoldResponseCode.TRIM_RESULT: { 1210 if (cooked.length != 4) break; 1211 final String path = cooked[1]; 1212 final long bytes = Long.parseLong(cooked[2]); 1213 final long time = Long.parseLong(cooked[3]); 1214 1215 final DropBoxManager dropBox = mContext.getSystemService(DropBoxManager.class); 1216 dropBox.addText(TAG_STORAGE_TRIM, scrubPath(path) 1217 + " " + bytes + " " + time); 1218 1219 final VolumeRecord rec = findRecordForPath(path); 1220 if (rec != null) { 1221 rec.lastTrimMillis = System.currentTimeMillis(); 1222 writeSettingsLocked(); 1223 } 1224 1225 break; 1226 } 1227 1228 default: { 1229 Slog.d(TAG, "Unhandled vold event " + code); 1230 } 1231 } 1232 1233 return true; 1234 } 1235 1236 private void onDiskScannedLocked(DiskInfo disk) { 1237 int volumeCount = 0; 1238 for (int i = 0; i < mVolumes.size(); i++) { 1239 final VolumeInfo vol = mVolumes.valueAt(i); 1240 if (Objects.equals(disk.id, vol.getDiskId())) { 1241 volumeCount++; 1242 } 1243 } 1244 1245 final Intent intent = new Intent(DiskInfo.ACTION_DISK_SCANNED); 1246 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 1247 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 1248 intent.putExtra(DiskInfo.EXTRA_DISK_ID, disk.id); 1249 intent.putExtra(DiskInfo.EXTRA_VOLUME_COUNT, volumeCount); 1250 mHandler.obtainMessage(H_INTERNAL_BROADCAST, intent).sendToTarget(); 1251 1252 final CountDownLatch latch = mDiskScanLatches.remove(disk.id); 1253 if (latch != null) { 1254 latch.countDown(); 1255 } 1256 1257 disk.volumeCount = volumeCount; 1258 mCallbacks.notifyDiskScanned(disk, volumeCount); 1259 } 1260 1261 private void onVolumeCreatedLocked(VolumeInfo vol) { 1262 if (mPms.isOnlyCoreApps()) { 1263 Slog.d(TAG, "System booted in core-only mode; ignoring volume " + vol.getId()); 1264 return; 1265 } 1266 1267 if (vol.type == VolumeInfo.TYPE_EMULATED) { 1268 final StorageManager storage = mContext.getSystemService(StorageManager.class); 1269 final VolumeInfo privateVol = storage.findPrivateForEmulated(vol); 1270 1271 if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid) 1272 && VolumeInfo.ID_PRIVATE_INTERNAL.equals(privateVol.id)) { 1273 Slog.v(TAG, "Found primary storage at " + vol); 1274 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY; 1275 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE; 1276 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget(); 1277 1278 } else if (Objects.equals(privateVol.fsUuid, mPrimaryStorageUuid)) { 1279 Slog.v(TAG, "Found primary storage at " + vol); 1280 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY; 1281 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE; 1282 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget(); 1283 } 1284 1285 } else if (vol.type == VolumeInfo.TYPE_PUBLIC) { 1286 // TODO: only look at first public partition 1287 if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, mPrimaryStorageUuid) 1288 && vol.disk.isDefaultPrimary()) { 1289 Slog.v(TAG, "Found primary storage at " + vol); 1290 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_PRIMARY; 1291 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE; 1292 } 1293 1294 // Adoptable public disks are visible to apps, since they meet 1295 // public API requirement of being in a stable location. 1296 if (vol.disk.isAdoptable()) { 1297 vol.mountFlags |= VolumeInfo.MOUNT_FLAG_VISIBLE; 1298 } 1299 1300 vol.mountUserId = mCurrentUserId; 1301 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget(); 1302 1303 } else if (vol.type == VolumeInfo.TYPE_PRIVATE) { 1304 mHandler.obtainMessage(H_VOLUME_MOUNT, vol).sendToTarget(); 1305 1306 } else { 1307 Slog.d(TAG, "Skipping automatic mounting of " + vol); 1308 } 1309 } 1310 1311 private boolean isBroadcastWorthy(VolumeInfo vol) { 1312 switch (vol.getType()) { 1313 case VolumeInfo.TYPE_PRIVATE: 1314 case VolumeInfo.TYPE_PUBLIC: 1315 case VolumeInfo.TYPE_EMULATED: 1316 break; 1317 default: 1318 return false; 1319 } 1320 1321 switch (vol.getState()) { 1322 case VolumeInfo.STATE_MOUNTED: 1323 case VolumeInfo.STATE_MOUNTED_READ_ONLY: 1324 case VolumeInfo.STATE_EJECTING: 1325 case VolumeInfo.STATE_UNMOUNTED: 1326 case VolumeInfo.STATE_UNMOUNTABLE: 1327 case VolumeInfo.STATE_BAD_REMOVAL: 1328 break; 1329 default: 1330 return false; 1331 } 1332 1333 return true; 1334 } 1335 1336 private void onVolumeStateChangedLocked(VolumeInfo vol, int oldState, int newState) { 1337 // Remember that we saw this volume so we're ready to accept user 1338 // metadata, or so we can annoy them when a private volume is ejected 1339 if (vol.isMountedReadable() && !TextUtils.isEmpty(vol.fsUuid)) { 1340 VolumeRecord rec = mRecords.get(vol.fsUuid); 1341 if (rec == null) { 1342 rec = new VolumeRecord(vol.type, vol.fsUuid); 1343 rec.partGuid = vol.partGuid; 1344 rec.createdMillis = System.currentTimeMillis(); 1345 if (vol.type == VolumeInfo.TYPE_PRIVATE) { 1346 rec.nickname = vol.disk.getDescription(); 1347 } 1348 mRecords.put(rec.fsUuid, rec); 1349 writeSettingsLocked(); 1350 } else { 1351 // Handle upgrade case where we didn't store partition GUID 1352 if (TextUtils.isEmpty(rec.partGuid)) { 1353 rec.partGuid = vol.partGuid; 1354 writeSettingsLocked(); 1355 } 1356 } 1357 } 1358 1359 mCallbacks.notifyVolumeStateChanged(vol, oldState, newState); 1360 1361 // Do not broadcast before boot has completed to avoid launching the 1362 // processes that receive the intent unnecessarily. 1363 if (mBootCompleted && isBroadcastWorthy(vol)) { 1364 final Intent intent = new Intent(VolumeInfo.ACTION_VOLUME_STATE_CHANGED); 1365 intent.putExtra(VolumeInfo.EXTRA_VOLUME_ID, vol.id); 1366 intent.putExtra(VolumeInfo.EXTRA_VOLUME_STATE, newState); 1367 intent.putExtra(VolumeRecord.EXTRA_FS_UUID, vol.fsUuid); 1368 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 1369 | Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND); 1370 mHandler.obtainMessage(H_INTERNAL_BROADCAST, intent).sendToTarget(); 1371 } 1372 1373 final String oldStateEnv = VolumeInfo.getEnvironmentForState(oldState); 1374 final String newStateEnv = VolumeInfo.getEnvironmentForState(newState); 1375 1376 if (!Objects.equals(oldStateEnv, newStateEnv)) { 1377 // Kick state changed event towards all started users. Any users 1378 // started after this point will trigger additional 1379 // user-specific broadcasts. 1380 for (int userId : mSystemUnlockedUsers) { 1381 if (vol.isVisibleForRead(userId)) { 1382 final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, false); 1383 mHandler.obtainMessage(H_VOLUME_BROADCAST, userVol).sendToTarget(); 1384 1385 mCallbacks.notifyStorageStateChanged(userVol.getPath(), oldStateEnv, 1386 newStateEnv); 1387 } 1388 } 1389 } 1390 1391 if (vol.type == VolumeInfo.TYPE_PUBLIC && vol.state == VolumeInfo.STATE_EJECTING) { 1392 // TODO: this should eventually be handled by new ObbVolume state changes 1393 /* 1394 * Some OBBs might have been unmounted when this volume was 1395 * unmounted, so send a message to the handler to let it know to 1396 * remove those from the list of mounted OBBS. 1397 */ 1398 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage( 1399 OBB_FLUSH_MOUNT_STATE, vol.path)); 1400 } 1401 } 1402 1403 private void onMoveStatusLocked(int status) { 1404 if (mMoveCallback == null) { 1405 Slog.w(TAG, "Odd, status but no move requested"); 1406 return; 1407 } 1408 1409 // TODO: estimate remaining time 1410 try { 1411 mMoveCallback.onStatusChanged(-1, status, -1); 1412 } catch (RemoteException ignored) { 1413 } 1414 1415 // We've finished copying and we're about to clean up old data, so 1416 // remember that move was successful if we get rebooted 1417 if (status == MOVE_STATUS_COPY_FINISHED) { 1418 Slog.d(TAG, "Move to " + mMoveTargetUuid + " copy phase finshed; persisting"); 1419 1420 mPrimaryStorageUuid = mMoveTargetUuid; 1421 writeSettingsLocked(); 1422 } 1423 1424 if (PackageManager.isMoveStatusFinished(status)) { 1425 Slog.d(TAG, "Move to " + mMoveTargetUuid + " finished with status " + status); 1426 1427 mMoveCallback = null; 1428 mMoveTargetUuid = null; 1429 } 1430 } 1431 1432 private void enforcePermission(String perm) { 1433 mContext.enforceCallingOrSelfPermission(perm, perm); 1434 } 1435 1436 /** 1437 * Decide if volume is mountable per device policies. 1438 */ 1439 private boolean isMountDisallowed(VolumeInfo vol) { 1440 UserManager userManager = mContext.getSystemService(UserManager.class); 1441 1442 boolean isUsbRestricted = false; 1443 if (vol.disk != null && vol.disk.isUsb()) { 1444 isUsbRestricted = userManager.hasUserRestriction(UserManager.DISALLOW_USB_FILE_TRANSFER, 1445 Binder.getCallingUserHandle()); 1446 } 1447 1448 boolean isTypeRestricted = false; 1449 if (vol.type == VolumeInfo.TYPE_PUBLIC || vol.type == VolumeInfo.TYPE_PRIVATE) { 1450 isTypeRestricted = userManager 1451 .hasUserRestriction(UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA, 1452 Binder.getCallingUserHandle()); 1453 } 1454 1455 return isUsbRestricted || isTypeRestricted; 1456 } 1457 1458 private void enforceAdminUser() { 1459 UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 1460 final int callingUserId = UserHandle.getCallingUserId(); 1461 boolean isAdmin; 1462 long token = Binder.clearCallingIdentity(); 1463 try { 1464 isAdmin = um.getUserInfo(callingUserId).isAdmin(); 1465 } finally { 1466 Binder.restoreCallingIdentity(token); 1467 } 1468 if (!isAdmin) { 1469 throw new SecurityException("Only admin users can adopt sd cards"); 1470 } 1471 } 1472 1473 /** 1474 * Constructs a new MountService instance 1475 * 1476 * @param context Binder context for this service 1477 */ 1478 public MountService(Context context) { 1479 sSelf = this; 1480 1481 mContext = context; 1482 mCallbacks = new Callbacks(FgThread.get().getLooper()); 1483 mLockPatternUtils = new LockPatternUtils(mContext); 1484 1485 // XXX: This will go away soon in favor of IMountServiceObserver 1486 mPms = (PackageManagerService) ServiceManager.getService("package"); 1487 1488 HandlerThread hthread = new HandlerThread(TAG); 1489 hthread.start(); 1490 mHandler = new MountServiceHandler(hthread.getLooper()); 1491 1492 // Add OBB Action Handler to MountService thread. 1493 mObbActionHandler = new ObbActionHandler(IoThread.get().getLooper()); 1494 1495 // Initialize the last-fstrim tracking if necessary 1496 File dataDir = Environment.getDataDirectory(); 1497 File systemDir = new File(dataDir, "system"); 1498 mLastMaintenanceFile = new File(systemDir, LAST_FSTRIM_FILE); 1499 if (!mLastMaintenanceFile.exists()) { 1500 // Not setting mLastMaintenance here means that we will force an 1501 // fstrim during reboot following the OTA that installs this code. 1502 try { 1503 (new FileOutputStream(mLastMaintenanceFile)).close(); 1504 } catch (IOException e) { 1505 Slog.e(TAG, "Unable to create fstrim record " + mLastMaintenanceFile.getPath()); 1506 } 1507 } else { 1508 mLastMaintenance = mLastMaintenanceFile.lastModified(); 1509 } 1510 1511 mSettingsFile = new AtomicFile( 1512 new File(Environment.getDataSystemDirectory(), "storage.xml")); 1513 1514 synchronized (mLock) { 1515 readSettingsLocked(); 1516 } 1517 1518 LocalServices.addService(MountServiceInternal.class, mMountServiceInternal); 1519 1520 /* 1521 * Create the connection to vold with a maximum queue of twice the 1522 * amount of containers we'd ever expect to have. This keeps an 1523 * "asec list" from blocking a thread repeatedly. 1524 */ 1525 1526 mConnector = new NativeDaemonConnector(this, "vold", MAX_CONTAINERS * 2, VOLD_TAG, 25, 1527 null); 1528 mConnector.setDebug(true); 1529 mConnector.setWarnIfHeld(mLock); 1530 mConnectorThread = new Thread(mConnector, VOLD_TAG); 1531 1532 // Reuse parameters from first connector since they are tested and safe 1533 mCryptConnector = new NativeDaemonConnector(this, "cryptd", 1534 MAX_CONTAINERS * 2, CRYPTD_TAG, 25, null); 1535 mCryptConnector.setDebug(true); 1536 mCryptConnectorThread = new Thread(mCryptConnector, CRYPTD_TAG); 1537 1538 final IntentFilter userFilter = new IntentFilter(); 1539 userFilter.addAction(Intent.ACTION_USER_ADDED); 1540 userFilter.addAction(Intent.ACTION_USER_REMOVED); 1541 mContext.registerReceiver(mUserReceiver, userFilter, null, mHandler); 1542 1543 synchronized (mLock) { 1544 addInternalVolumeLocked(); 1545 } 1546 1547 // Add ourself to the Watchdog monitors if enabled. 1548 if (WATCHDOG_ENABLE) { 1549 Watchdog.getInstance().addMonitor(this); 1550 } 1551 } 1552 1553 private void start() { 1554 mConnectorThread.start(); 1555 mCryptConnectorThread.start(); 1556 } 1557 1558 private void systemReady() { 1559 mSystemReady = true; 1560 mHandler.obtainMessage(H_SYSTEM_READY).sendToTarget(); 1561 } 1562 1563 private void bootCompleted() { 1564 mBootCompleted = true; 1565 } 1566 1567 private String getDefaultPrimaryStorageUuid() { 1568 if (SystemProperties.getBoolean(StorageManager.PROP_PRIMARY_PHYSICAL, false)) { 1569 return StorageManager.UUID_PRIMARY_PHYSICAL; 1570 } else { 1571 return StorageManager.UUID_PRIVATE_INTERNAL; 1572 } 1573 } 1574 1575 private void readSettingsLocked() { 1576 mRecords.clear(); 1577 mPrimaryStorageUuid = getDefaultPrimaryStorageUuid(); 1578 mForceAdoptable = false; 1579 1580 FileInputStream fis = null; 1581 try { 1582 fis = mSettingsFile.openRead(); 1583 final XmlPullParser in = Xml.newPullParser(); 1584 in.setInput(fis, StandardCharsets.UTF_8.name()); 1585 1586 int type; 1587 while ((type = in.next()) != END_DOCUMENT) { 1588 if (type == START_TAG) { 1589 final String tag = in.getName(); 1590 if (TAG_VOLUMES.equals(tag)) { 1591 final int version = readIntAttribute(in, ATTR_VERSION, VERSION_INIT); 1592 final boolean primaryPhysical = SystemProperties.getBoolean( 1593 StorageManager.PROP_PRIMARY_PHYSICAL, false); 1594 final boolean validAttr = (version >= VERSION_FIX_PRIMARY) 1595 || (version >= VERSION_ADD_PRIMARY && !primaryPhysical); 1596 if (validAttr) { 1597 mPrimaryStorageUuid = readStringAttribute(in, 1598 ATTR_PRIMARY_STORAGE_UUID); 1599 } 1600 mForceAdoptable = readBooleanAttribute(in, ATTR_FORCE_ADOPTABLE, false); 1601 1602 } else if (TAG_VOLUME.equals(tag)) { 1603 final VolumeRecord rec = readVolumeRecord(in); 1604 mRecords.put(rec.fsUuid, rec); 1605 } 1606 } 1607 } 1608 } catch (FileNotFoundException e) { 1609 // Missing metadata is okay, probably first boot 1610 } catch (IOException e) { 1611 Slog.wtf(TAG, "Failed reading metadata", e); 1612 } catch (XmlPullParserException e) { 1613 Slog.wtf(TAG, "Failed reading metadata", e); 1614 } finally { 1615 IoUtils.closeQuietly(fis); 1616 } 1617 } 1618 1619 private void writeSettingsLocked() { 1620 FileOutputStream fos = null; 1621 try { 1622 fos = mSettingsFile.startWrite(); 1623 1624 XmlSerializer out = new FastXmlSerializer(); 1625 out.setOutput(fos, StandardCharsets.UTF_8.name()); 1626 out.startDocument(null, true); 1627 out.startTag(null, TAG_VOLUMES); 1628 writeIntAttribute(out, ATTR_VERSION, VERSION_FIX_PRIMARY); 1629 writeStringAttribute(out, ATTR_PRIMARY_STORAGE_UUID, mPrimaryStorageUuid); 1630 writeBooleanAttribute(out, ATTR_FORCE_ADOPTABLE, mForceAdoptable); 1631 final int size = mRecords.size(); 1632 for (int i = 0; i < size; i++) { 1633 final VolumeRecord rec = mRecords.valueAt(i); 1634 writeVolumeRecord(out, rec); 1635 } 1636 out.endTag(null, TAG_VOLUMES); 1637 out.endDocument(); 1638 1639 mSettingsFile.finishWrite(fos); 1640 } catch (IOException e) { 1641 if (fos != null) { 1642 mSettingsFile.failWrite(fos); 1643 } 1644 } 1645 } 1646 1647 public static VolumeRecord readVolumeRecord(XmlPullParser in) throws IOException { 1648 final int type = readIntAttribute(in, ATTR_TYPE); 1649 final String fsUuid = readStringAttribute(in, ATTR_FS_UUID); 1650 final VolumeRecord meta = new VolumeRecord(type, fsUuid); 1651 meta.partGuid = readStringAttribute(in, ATTR_PART_GUID); 1652 meta.nickname = readStringAttribute(in, ATTR_NICKNAME); 1653 meta.userFlags = readIntAttribute(in, ATTR_USER_FLAGS); 1654 meta.createdMillis = readLongAttribute(in, ATTR_CREATED_MILLIS); 1655 meta.lastTrimMillis = readLongAttribute(in, ATTR_LAST_TRIM_MILLIS); 1656 meta.lastBenchMillis = readLongAttribute(in, ATTR_LAST_BENCH_MILLIS); 1657 return meta; 1658 } 1659 1660 public static void writeVolumeRecord(XmlSerializer out, VolumeRecord rec) throws IOException { 1661 out.startTag(null, TAG_VOLUME); 1662 writeIntAttribute(out, ATTR_TYPE, rec.type); 1663 writeStringAttribute(out, ATTR_FS_UUID, rec.fsUuid); 1664 writeStringAttribute(out, ATTR_PART_GUID, rec.partGuid); 1665 writeStringAttribute(out, ATTR_NICKNAME, rec.nickname); 1666 writeIntAttribute(out, ATTR_USER_FLAGS, rec.userFlags); 1667 writeLongAttribute(out, ATTR_CREATED_MILLIS, rec.createdMillis); 1668 writeLongAttribute(out, ATTR_LAST_TRIM_MILLIS, rec.lastTrimMillis); 1669 writeLongAttribute(out, ATTR_LAST_BENCH_MILLIS, rec.lastBenchMillis); 1670 out.endTag(null, TAG_VOLUME); 1671 } 1672 1673 /** 1674 * Exposed API calls below here 1675 */ 1676 1677 @Override 1678 public void registerListener(IMountServiceListener listener) { 1679 mCallbacks.register(listener); 1680 } 1681 1682 @Override 1683 public void unregisterListener(IMountServiceListener listener) { 1684 mCallbacks.unregister(listener); 1685 } 1686 1687 @Override 1688 public void shutdown(final IMountShutdownObserver observer) { 1689 enforcePermission(android.Manifest.permission.SHUTDOWN); 1690 1691 Slog.i(TAG, "Shutting down"); 1692 mHandler.obtainMessage(H_SHUTDOWN, observer).sendToTarget(); 1693 } 1694 1695 @Override 1696 public boolean isUsbMassStorageConnected() { 1697 throw new UnsupportedOperationException(); 1698 } 1699 1700 @Override 1701 public void setUsbMassStorageEnabled(boolean enable) { 1702 throw new UnsupportedOperationException(); 1703 } 1704 1705 @Override 1706 public boolean isUsbMassStorageEnabled() { 1707 throw new UnsupportedOperationException(); 1708 } 1709 1710 @Override 1711 public String getVolumeState(String mountPoint) { 1712 throw new UnsupportedOperationException(); 1713 } 1714 1715 @Override 1716 public boolean isExternalStorageEmulated() { 1717 throw new UnsupportedOperationException(); 1718 } 1719 1720 @Override 1721 public int mountVolume(String path) { 1722 mount(findVolumeIdForPathOrThrow(path)); 1723 return 0; 1724 } 1725 1726 @Override 1727 public void unmountVolume(String path, boolean force, boolean removeEncryption) { 1728 unmount(findVolumeIdForPathOrThrow(path)); 1729 } 1730 1731 @Override 1732 public int formatVolume(String path) { 1733 format(findVolumeIdForPathOrThrow(path)); 1734 return 0; 1735 } 1736 1737 @Override 1738 public void mount(String volId) { 1739 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); 1740 waitForReady(); 1741 1742 final VolumeInfo vol = findVolumeByIdOrThrow(volId); 1743 if (isMountDisallowed(vol)) { 1744 throw new SecurityException("Mounting " + volId + " restricted by policy"); 1745 } 1746 try { 1747 mConnector.execute("volume", "mount", vol.id, vol.mountFlags, vol.mountUserId); 1748 } catch (NativeDaemonConnectorException e) { 1749 throw e.rethrowAsParcelableException(); 1750 } 1751 } 1752 1753 @Override 1754 public void unmount(String volId) { 1755 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); 1756 waitForReady(); 1757 1758 final VolumeInfo vol = findVolumeByIdOrThrow(volId); 1759 1760 // TODO: expand PMS to know about multiple volumes 1761 if (vol.isPrimaryPhysical()) { 1762 final long ident = Binder.clearCallingIdentity(); 1763 try { 1764 synchronized (mUnmountLock) { 1765 mUnmountSignal = new CountDownLatch(1); 1766 mPms.updateExternalMediaStatus(false, true); 1767 waitForLatch(mUnmountSignal, "mUnmountSignal"); 1768 mUnmountSignal = null; 1769 } 1770 } finally { 1771 Binder.restoreCallingIdentity(ident); 1772 } 1773 } 1774 1775 try { 1776 mConnector.execute("volume", "unmount", vol.id); 1777 } catch (NativeDaemonConnectorException e) { 1778 throw e.rethrowAsParcelableException(); 1779 } 1780 } 1781 1782 @Override 1783 public void format(String volId) { 1784 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); 1785 waitForReady(); 1786 1787 final VolumeInfo vol = findVolumeByIdOrThrow(volId); 1788 try { 1789 mConnector.execute("volume", "format", vol.id, "auto"); 1790 } catch (NativeDaemonConnectorException e) { 1791 throw e.rethrowAsParcelableException(); 1792 } 1793 } 1794 1795 @Override 1796 public long benchmark(String volId) { 1797 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); 1798 waitForReady(); 1799 1800 try { 1801 // TODO: make benchmark async so we don't block other commands 1802 final NativeDaemonEvent res = mConnector.execute(3 * DateUtils.MINUTE_IN_MILLIS, 1803 "volume", "benchmark", volId); 1804 return Long.parseLong(res.getMessage()); 1805 } catch (NativeDaemonTimeoutException e) { 1806 return Long.MAX_VALUE; 1807 } catch (NativeDaemonConnectorException e) { 1808 throw e.rethrowAsParcelableException(); 1809 } 1810 } 1811 1812 @Override 1813 public void partitionPublic(String diskId) { 1814 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); 1815 waitForReady(); 1816 1817 final CountDownLatch latch = findOrCreateDiskScanLatch(diskId); 1818 try { 1819 mConnector.execute("volume", "partition", diskId, "public"); 1820 waitForLatch(latch, "partitionPublic", 3 * DateUtils.MINUTE_IN_MILLIS); 1821 } catch (NativeDaemonConnectorException e) { 1822 throw e.rethrowAsParcelableException(); 1823 } catch (TimeoutException e) { 1824 throw new IllegalStateException(e); 1825 } 1826 } 1827 1828 @Override 1829 public void partitionPrivate(String diskId) { 1830 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); 1831 enforceAdminUser(); 1832 waitForReady(); 1833 1834 final CountDownLatch latch = findOrCreateDiskScanLatch(diskId); 1835 try { 1836 mConnector.execute("volume", "partition", diskId, "private"); 1837 waitForLatch(latch, "partitionPrivate", 3 * DateUtils.MINUTE_IN_MILLIS); 1838 } catch (NativeDaemonConnectorException e) { 1839 throw e.rethrowAsParcelableException(); 1840 } catch (TimeoutException e) { 1841 throw new IllegalStateException(e); 1842 } 1843 } 1844 1845 @Override 1846 public void partitionMixed(String diskId, int ratio) { 1847 enforcePermission(android.Manifest.permission.MOUNT_FORMAT_FILESYSTEMS); 1848 enforceAdminUser(); 1849 waitForReady(); 1850 1851 final CountDownLatch latch = findOrCreateDiskScanLatch(diskId); 1852 try { 1853 mConnector.execute("volume", "partition", diskId, "mixed", ratio); 1854 waitForLatch(latch, "partitionMixed", 3 * DateUtils.MINUTE_IN_MILLIS); 1855 } catch (NativeDaemonConnectorException e) { 1856 throw e.rethrowAsParcelableException(); 1857 } catch (TimeoutException e) { 1858 throw new IllegalStateException(e); 1859 } 1860 } 1861 1862 @Override 1863 public void setVolumeNickname(String fsUuid, String nickname) { 1864 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); 1865 waitForReady(); 1866 1867 Preconditions.checkNotNull(fsUuid); 1868 synchronized (mLock) { 1869 final VolumeRecord rec = mRecords.get(fsUuid); 1870 rec.nickname = nickname; 1871 mCallbacks.notifyVolumeRecordChanged(rec); 1872 writeSettingsLocked(); 1873 } 1874 } 1875 1876 @Override 1877 public void setVolumeUserFlags(String fsUuid, int flags, int mask) { 1878 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); 1879 waitForReady(); 1880 1881 Preconditions.checkNotNull(fsUuid); 1882 synchronized (mLock) { 1883 final VolumeRecord rec = mRecords.get(fsUuid); 1884 rec.userFlags = (rec.userFlags & ~mask) | (flags & mask); 1885 mCallbacks.notifyVolumeRecordChanged(rec); 1886 writeSettingsLocked(); 1887 } 1888 } 1889 1890 @Override 1891 public void forgetVolume(String fsUuid) { 1892 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); 1893 waitForReady(); 1894 1895 Preconditions.checkNotNull(fsUuid); 1896 1897 synchronized (mLock) { 1898 final VolumeRecord rec = mRecords.remove(fsUuid); 1899 if (rec != null && !TextUtils.isEmpty(rec.partGuid)) { 1900 mHandler.obtainMessage(H_PARTITION_FORGET, rec.partGuid).sendToTarget(); 1901 } 1902 mCallbacks.notifyVolumeForgotten(fsUuid); 1903 1904 // If this had been primary storage, revert back to internal and 1905 // reset vold so we bind into new volume into place. 1906 if (Objects.equals(mPrimaryStorageUuid, fsUuid)) { 1907 mPrimaryStorageUuid = getDefaultPrimaryStorageUuid(); 1908 mHandler.obtainMessage(H_RESET).sendToTarget(); 1909 } 1910 1911 writeSettingsLocked(); 1912 } 1913 } 1914 1915 @Override 1916 public void forgetAllVolumes() { 1917 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); 1918 waitForReady(); 1919 1920 synchronized (mLock) { 1921 for (int i = 0; i < mRecords.size(); i++) { 1922 final String fsUuid = mRecords.keyAt(i); 1923 final VolumeRecord rec = mRecords.valueAt(i); 1924 if (!TextUtils.isEmpty(rec.partGuid)) { 1925 mHandler.obtainMessage(H_PARTITION_FORGET, rec.partGuid).sendToTarget(); 1926 } 1927 mCallbacks.notifyVolumeForgotten(fsUuid); 1928 } 1929 mRecords.clear(); 1930 1931 if (!Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, mPrimaryStorageUuid)) { 1932 mPrimaryStorageUuid = getDefaultPrimaryStorageUuid(); 1933 } 1934 1935 writeSettingsLocked(); 1936 mHandler.obtainMessage(H_RESET).sendToTarget(); 1937 } 1938 } 1939 1940 private void forgetPartition(String partGuid) { 1941 try { 1942 mConnector.execute("volume", "forget_partition", partGuid); 1943 } catch (NativeDaemonConnectorException e) { 1944 Slog.w(TAG, "Failed to forget key for " + partGuid + ": " + e); 1945 } 1946 } 1947 1948 private void remountUidExternalStorage(int uid, int mode) { 1949 waitForReady(); 1950 1951 String modeName = "none"; 1952 switch (mode) { 1953 case Zygote.MOUNT_EXTERNAL_DEFAULT: { 1954 modeName = "default"; 1955 } break; 1956 1957 case Zygote.MOUNT_EXTERNAL_READ: { 1958 modeName = "read"; 1959 } break; 1960 1961 case Zygote.MOUNT_EXTERNAL_WRITE: { 1962 modeName = "write"; 1963 } break; 1964 } 1965 1966 try { 1967 mConnector.execute("volume", "remount_uid", uid, modeName); 1968 } catch (NativeDaemonConnectorException e) { 1969 Slog.w(TAG, "Failed to remount UID " + uid + " as " + modeName + ": " + e); 1970 } 1971 } 1972 1973 @Override 1974 public void setDebugFlags(int flags, int mask) { 1975 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); 1976 waitForReady(); 1977 1978 if ((mask & StorageManager.DEBUG_EMULATE_FBE) != 0) { 1979 if (StorageManager.isFileEncryptedNativeOnly()) { 1980 throw new IllegalStateException( 1981 "Emulation not available on device with native FBE"); 1982 } 1983 if (mLockPatternUtils.isCredentialRequiredToDecrypt(false)) { 1984 throw new IllegalStateException( 1985 "Emulation requires disabling 'Secure start-up' in Settings > Security"); 1986 } 1987 1988 final long token = Binder.clearCallingIdentity(); 1989 try { 1990 final boolean emulateFbe = (flags & StorageManager.DEBUG_EMULATE_FBE) != 0; 1991 SystemProperties.set(StorageManager.PROP_EMULATE_FBE, Boolean.toString(emulateFbe)); 1992 1993 // Perform hard reboot to kick policy into place 1994 mContext.getSystemService(PowerManager.class).reboot(null); 1995 } finally { 1996 Binder.restoreCallingIdentity(token); 1997 } 1998 } 1999 2000 if ((mask & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0) { 2001 if (StorageManager.isFileEncryptedNativeOnly()) { 2002 throw new IllegalStateException( 2003 "Adoptable storage not available on device with native FBE"); 2004 } 2005 2006 synchronized (mLock) { 2007 mForceAdoptable = (flags & StorageManager.DEBUG_FORCE_ADOPTABLE) != 0; 2008 2009 writeSettingsLocked(); 2010 mHandler.obtainMessage(H_RESET).sendToTarget(); 2011 } 2012 } 2013 2014 if ((mask & (StorageManager.DEBUG_SDCARDFS_FORCE_ON 2015 | StorageManager.DEBUG_SDCARDFS_FORCE_OFF)) != 0) { 2016 final String value; 2017 if ((flags & StorageManager.DEBUG_SDCARDFS_FORCE_ON) != 0) { 2018 value = "force_on"; 2019 } else if ((flags & StorageManager.DEBUG_SDCARDFS_FORCE_OFF) != 0) { 2020 value = "force_off"; 2021 } else { 2022 value = ""; 2023 } 2024 2025 final long token = Binder.clearCallingIdentity(); 2026 try { 2027 SystemProperties.set(StorageManager.PROP_SDCARDFS, value); 2028 2029 // Reset storage to kick new setting into place 2030 mHandler.obtainMessage(H_RESET).sendToTarget(); 2031 } finally { 2032 Binder.restoreCallingIdentity(token); 2033 } 2034 } 2035 } 2036 2037 @Override 2038 public String getPrimaryStorageUuid() { 2039 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); 2040 waitForReady(); 2041 2042 synchronized (mLock) { 2043 return mPrimaryStorageUuid; 2044 } 2045 } 2046 2047 @Override 2048 public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) { 2049 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); 2050 waitForReady(); 2051 2052 final VolumeInfo from; 2053 final VolumeInfo to; 2054 2055 synchronized (mLock) { 2056 if (Objects.equals(mPrimaryStorageUuid, volumeUuid)) { 2057 throw new IllegalArgumentException("Primary storage already at " + volumeUuid); 2058 } 2059 2060 if (mMoveCallback != null) { 2061 throw new IllegalStateException("Move already in progress"); 2062 } 2063 mMoveCallback = callback; 2064 mMoveTargetUuid = volumeUuid; 2065 2066 // When moving to/from primary physical volume, we probably just nuked 2067 // the current storage location, so we have nothing to move. 2068 if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, mPrimaryStorageUuid) 2069 || Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) { 2070 Slog.d(TAG, "Skipping move to/from primary physical"); 2071 onMoveStatusLocked(MOVE_STATUS_COPY_FINISHED); 2072 onMoveStatusLocked(PackageManager.MOVE_SUCCEEDED); 2073 mHandler.obtainMessage(H_RESET).sendToTarget(); 2074 return; 2075 2076 } else { 2077 from = findStorageForUuid(mPrimaryStorageUuid); 2078 to = findStorageForUuid(volumeUuid); 2079 2080 if (from == null) { 2081 Slog.w(TAG, "Failing move due to missing from volume " + mPrimaryStorageUuid); 2082 onMoveStatusLocked(PackageManager.MOVE_FAILED_INTERNAL_ERROR); 2083 return; 2084 } else if (to == null) { 2085 Slog.w(TAG, "Failing move due to missing to volume " + volumeUuid); 2086 onMoveStatusLocked(PackageManager.MOVE_FAILED_INTERNAL_ERROR); 2087 return; 2088 } 2089 } 2090 } 2091 2092 try { 2093 mConnector.execute("volume", "move_storage", from.id, to.id); 2094 } catch (NativeDaemonConnectorException e) { 2095 throw e.rethrowAsParcelableException(); 2096 } 2097 } 2098 2099 @Override 2100 public int[] getStorageUsers(String path) { 2101 enforcePermission(android.Manifest.permission.MOUNT_UNMOUNT_FILESYSTEMS); 2102 waitForReady(); 2103 try { 2104 final String[] r = NativeDaemonEvent.filterMessageList( 2105 mConnector.executeForList("storage", "users", path), 2106 VoldResponseCode.StorageUsersListResult); 2107 2108 // FMT: <pid> <process name> 2109 int[] data = new int[r.length]; 2110 for (int i = 0; i < r.length; i++) { 2111 String[] tok = r[i].split(" "); 2112 try { 2113 data[i] = Integer.parseInt(tok[0]); 2114 } catch (NumberFormatException nfe) { 2115 Slog.e(TAG, String.format("Error parsing pid %s", tok[0])); 2116 return new int[0]; 2117 } 2118 } 2119 return data; 2120 } catch (NativeDaemonConnectorException e) { 2121 Slog.e(TAG, "Failed to retrieve storage users list", e); 2122 return new int[0]; 2123 } 2124 } 2125 2126 private void warnOnNotMounted() { 2127 synchronized (mLock) { 2128 for (int i = 0; i < mVolumes.size(); i++) { 2129 final VolumeInfo vol = mVolumes.valueAt(i); 2130 if (vol.isPrimary() && vol.isMountedWritable()) { 2131 // Cool beans, we have a mounted primary volume 2132 return; 2133 } 2134 } 2135 } 2136 2137 Slog.w(TAG, "No primary storage mounted!"); 2138 } 2139 2140 public String[] getSecureContainerList() { 2141 enforcePermission(android.Manifest.permission.ASEC_ACCESS); 2142 waitForReady(); 2143 warnOnNotMounted(); 2144 2145 try { 2146 return NativeDaemonEvent.filterMessageList( 2147 mConnector.executeForList("asec", "list"), VoldResponseCode.AsecListResult); 2148 } catch (NativeDaemonConnectorException e) { 2149 return new String[0]; 2150 } 2151 } 2152 2153 public int createSecureContainer(String id, int sizeMb, String fstype, String key, 2154 int ownerUid, boolean external) { 2155 enforcePermission(android.Manifest.permission.ASEC_CREATE); 2156 waitForReady(); 2157 warnOnNotMounted(); 2158 2159 int rc = StorageResultCode.OperationSucceeded; 2160 try { 2161 mConnector.execute("asec", "create", id, sizeMb, fstype, new SensitiveArg(key), 2162 ownerUid, external ? "1" : "0"); 2163 } catch (NativeDaemonConnectorException e) { 2164 rc = StorageResultCode.OperationFailedInternalError; 2165 } 2166 2167 if (rc == StorageResultCode.OperationSucceeded) { 2168 synchronized (mAsecMountSet) { 2169 mAsecMountSet.add(id); 2170 } 2171 } 2172 return rc; 2173 } 2174 2175 @Override 2176 public int resizeSecureContainer(String id, int sizeMb, String key) { 2177 enforcePermission(android.Manifest.permission.ASEC_CREATE); 2178 waitForReady(); 2179 warnOnNotMounted(); 2180 2181 int rc = StorageResultCode.OperationSucceeded; 2182 try { 2183 mConnector.execute("asec", "resize", id, sizeMb, new SensitiveArg(key)); 2184 } catch (NativeDaemonConnectorException e) { 2185 rc = StorageResultCode.OperationFailedInternalError; 2186 } 2187 return rc; 2188 } 2189 2190 public int finalizeSecureContainer(String id) { 2191 enforcePermission(android.Manifest.permission.ASEC_CREATE); 2192 warnOnNotMounted(); 2193 2194 int rc = StorageResultCode.OperationSucceeded; 2195 try { 2196 mConnector.execute("asec", "finalize", id); 2197 /* 2198 * Finalization does a remount, so no need 2199 * to update mAsecMountSet 2200 */ 2201 } catch (NativeDaemonConnectorException e) { 2202 rc = StorageResultCode.OperationFailedInternalError; 2203 } 2204 return rc; 2205 } 2206 2207 public int fixPermissionsSecureContainer(String id, int gid, String filename) { 2208 enforcePermission(android.Manifest.permission.ASEC_CREATE); 2209 warnOnNotMounted(); 2210 2211 int rc = StorageResultCode.OperationSucceeded; 2212 try { 2213 mConnector.execute("asec", "fixperms", id, gid, filename); 2214 /* 2215 * Fix permissions does a remount, so no need to update 2216 * mAsecMountSet 2217 */ 2218 } catch (NativeDaemonConnectorException e) { 2219 rc = StorageResultCode.OperationFailedInternalError; 2220 } 2221 return rc; 2222 } 2223 2224 public int destroySecureContainer(String id, boolean force) { 2225 enforcePermission(android.Manifest.permission.ASEC_DESTROY); 2226 waitForReady(); 2227 warnOnNotMounted(); 2228 2229 /* 2230 * Force a GC to make sure AssetManagers in other threads of the 2231 * system_server are cleaned up. We have to do this since AssetManager 2232 * instances are kept as a WeakReference and it's possible we have files 2233 * open on the external storage. 2234 */ 2235 Runtime.getRuntime().gc(); 2236 2237 int rc = StorageResultCode.OperationSucceeded; 2238 try { 2239 final Command cmd = new Command("asec", "destroy", id); 2240 if (force) { 2241 cmd.appendArg("force"); 2242 } 2243 mConnector.execute(cmd); 2244 } catch (NativeDaemonConnectorException e) { 2245 int code = e.getCode(); 2246 if (code == VoldResponseCode.OpFailedStorageBusy) { 2247 rc = StorageResultCode.OperationFailedStorageBusy; 2248 } else { 2249 rc = StorageResultCode.OperationFailedInternalError; 2250 } 2251 } 2252 2253 if (rc == StorageResultCode.OperationSucceeded) { 2254 synchronized (mAsecMountSet) { 2255 if (mAsecMountSet.contains(id)) { 2256 mAsecMountSet.remove(id); 2257 } 2258 } 2259 } 2260 2261 return rc; 2262 } 2263 2264 public int mountSecureContainer(String id, String key, int ownerUid, boolean readOnly) { 2265 enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT); 2266 waitForReady(); 2267 warnOnNotMounted(); 2268 2269 synchronized (mAsecMountSet) { 2270 if (mAsecMountSet.contains(id)) { 2271 return StorageResultCode.OperationFailedStorageMounted; 2272 } 2273 } 2274 2275 int rc = StorageResultCode.OperationSucceeded; 2276 try { 2277 mConnector.execute("asec", "mount", id, new SensitiveArg(key), ownerUid, 2278 readOnly ? "ro" : "rw"); 2279 } catch (NativeDaemonConnectorException e) { 2280 int code = e.getCode(); 2281 if (code != VoldResponseCode.OpFailedStorageBusy) { 2282 rc = StorageResultCode.OperationFailedInternalError; 2283 } 2284 } 2285 2286 if (rc == StorageResultCode.OperationSucceeded) { 2287 synchronized (mAsecMountSet) { 2288 mAsecMountSet.add(id); 2289 } 2290 } 2291 return rc; 2292 } 2293 2294 public int unmountSecureContainer(String id, boolean force) { 2295 enforcePermission(android.Manifest.permission.ASEC_MOUNT_UNMOUNT); 2296 waitForReady(); 2297 warnOnNotMounted(); 2298 2299 synchronized (mAsecMountSet) { 2300 if (!mAsecMountSet.contains(id)) { 2301 return StorageResultCode.OperationFailedStorageNotMounted; 2302 } 2303 } 2304 2305 /* 2306 * Force a GC to make sure AssetManagers in other threads of the 2307 * system_server are cleaned up. We have to do this since AssetManager 2308 * instances are kept as a WeakReference and it's possible we have files 2309 * open on the external storage. 2310 */ 2311 Runtime.getRuntime().gc(); 2312 2313 int rc = StorageResultCode.OperationSucceeded; 2314 try { 2315 final Command cmd = new Command("asec", "unmount", id); 2316 if (force) { 2317 cmd.appendArg("force"); 2318 } 2319 mConnector.execute(cmd); 2320 } catch (NativeDaemonConnectorException e) { 2321 int code = e.getCode(); 2322 if (code == VoldResponseCode.OpFailedStorageBusy) { 2323 rc = StorageResultCode.OperationFailedStorageBusy; 2324 } else { 2325 rc = StorageResultCode.OperationFailedInternalError; 2326 } 2327 } 2328 2329 if (rc == StorageResultCode.OperationSucceeded) { 2330 synchronized (mAsecMountSet) { 2331 mAsecMountSet.remove(id); 2332 } 2333 } 2334 return rc; 2335 } 2336 2337 public boolean isSecureContainerMounted(String id) { 2338 enforcePermission(android.Manifest.permission.ASEC_ACCESS); 2339 waitForReady(); 2340 warnOnNotMounted(); 2341 2342 synchronized (mAsecMountSet) { 2343 return mAsecMountSet.contains(id); 2344 } 2345 } 2346 2347 public int renameSecureContainer(String oldId, String newId) { 2348 enforcePermission(android.Manifest.permission.ASEC_RENAME); 2349 waitForReady(); 2350 warnOnNotMounted(); 2351 2352 synchronized (mAsecMountSet) { 2353 /* 2354 * Because a mounted container has active internal state which cannot be 2355 * changed while active, we must ensure both ids are not currently mounted. 2356 */ 2357 if (mAsecMountSet.contains(oldId) || mAsecMountSet.contains(newId)) { 2358 return StorageResultCode.OperationFailedStorageMounted; 2359 } 2360 } 2361 2362 int rc = StorageResultCode.OperationSucceeded; 2363 try { 2364 mConnector.execute("asec", "rename", oldId, newId); 2365 } catch (NativeDaemonConnectorException e) { 2366 rc = StorageResultCode.OperationFailedInternalError; 2367 } 2368 2369 return rc; 2370 } 2371 2372 public String getSecureContainerPath(String id) { 2373 enforcePermission(android.Manifest.permission.ASEC_ACCESS); 2374 waitForReady(); 2375 warnOnNotMounted(); 2376 2377 final NativeDaemonEvent event; 2378 try { 2379 event = mConnector.execute("asec", "path", id); 2380 event.checkCode(VoldResponseCode.AsecPathResult); 2381 return event.getMessage(); 2382 } catch (NativeDaemonConnectorException e) { 2383 int code = e.getCode(); 2384 if (code == VoldResponseCode.OpFailedStorageNotFound) { 2385 Slog.i(TAG, String.format("Container '%s' not found", id)); 2386 return null; 2387 } else { 2388 throw new IllegalStateException(String.format("Unexpected response code %d", code)); 2389 } 2390 } 2391 } 2392 2393 public String getSecureContainerFilesystemPath(String id) { 2394 enforcePermission(android.Manifest.permission.ASEC_ACCESS); 2395 waitForReady(); 2396 warnOnNotMounted(); 2397 2398 final NativeDaemonEvent event; 2399 try { 2400 event = mConnector.execute("asec", "fspath", id); 2401 event.checkCode(VoldResponseCode.AsecPathResult); 2402 return event.getMessage(); 2403 } catch (NativeDaemonConnectorException e) { 2404 int code = e.getCode(); 2405 if (code == VoldResponseCode.OpFailedStorageNotFound) { 2406 Slog.i(TAG, String.format("Container '%s' not found", id)); 2407 return null; 2408 } else { 2409 throw new IllegalStateException(String.format("Unexpected response code %d", code)); 2410 } 2411 } 2412 } 2413 2414 @Override 2415 public void finishMediaUpdate() { 2416 if (Binder.getCallingUid() != Process.SYSTEM_UID) { 2417 throw new SecurityException("no permission to call finishMediaUpdate()"); 2418 } 2419 if (mUnmountSignal != null) { 2420 mUnmountSignal.countDown(); 2421 } else { 2422 Slog.w(TAG, "Odd, nobody asked to unmount?"); 2423 } 2424 } 2425 2426 private boolean isUidOwnerOfPackageOrSystem(String packageName, int callerUid) { 2427 if (callerUid == android.os.Process.SYSTEM_UID) { 2428 return true; 2429 } 2430 2431 if (packageName == null) { 2432 return false; 2433 } 2434 2435 final int packageUid = mPms.getPackageUid(packageName, 2436 PackageManager.MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getUserId(callerUid)); 2437 2438 if (DEBUG_OBB) { 2439 Slog.d(TAG, "packageName = " + packageName + ", packageUid = " + 2440 packageUid + ", callerUid = " + callerUid); 2441 } 2442 2443 return callerUid == packageUid; 2444 } 2445 2446 public String getMountedObbPath(String rawPath) { 2447 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 2448 2449 waitForReady(); 2450 warnOnNotMounted(); 2451 2452 final ObbState state; 2453 synchronized (mObbMounts) { 2454 state = mObbPathToStateMap.get(rawPath); 2455 } 2456 if (state == null) { 2457 Slog.w(TAG, "Failed to find OBB mounted at " + rawPath); 2458 return null; 2459 } 2460 2461 final NativeDaemonEvent event; 2462 try { 2463 event = mConnector.execute("obb", "path", state.canonicalPath); 2464 event.checkCode(VoldResponseCode.AsecPathResult); 2465 return event.getMessage(); 2466 } catch (NativeDaemonConnectorException e) { 2467 int code = e.getCode(); 2468 if (code == VoldResponseCode.OpFailedStorageNotFound) { 2469 return null; 2470 } else { 2471 throw new IllegalStateException(String.format("Unexpected response code %d", code)); 2472 } 2473 } 2474 } 2475 2476 @Override 2477 public boolean isObbMounted(String rawPath) { 2478 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 2479 synchronized (mObbMounts) { 2480 return mObbPathToStateMap.containsKey(rawPath); 2481 } 2482 } 2483 2484 @Override 2485 public void mountObb( 2486 String rawPath, String canonicalPath, String key, IObbActionListener token, int nonce) { 2487 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 2488 Preconditions.checkNotNull(canonicalPath, "canonicalPath cannot be null"); 2489 Preconditions.checkNotNull(token, "token cannot be null"); 2490 2491 final int callingUid = Binder.getCallingUid(); 2492 final ObbState obbState = new ObbState(rawPath, canonicalPath, callingUid, token, nonce); 2493 final ObbAction action = new MountObbAction(obbState, key, callingUid); 2494 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action)); 2495 2496 if (DEBUG_OBB) 2497 Slog.i(TAG, "Send to OBB handler: " + action.toString()); 2498 } 2499 2500 @Override 2501 public void unmountObb(String rawPath, boolean force, IObbActionListener token, int nonce) { 2502 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 2503 2504 final ObbState existingState; 2505 synchronized (mObbMounts) { 2506 existingState = mObbPathToStateMap.get(rawPath); 2507 } 2508 2509 if (existingState != null) { 2510 // TODO: separate state object from request data 2511 final int callingUid = Binder.getCallingUid(); 2512 final ObbState newState = new ObbState( 2513 rawPath, existingState.canonicalPath, callingUid, token, nonce); 2514 final ObbAction action = new UnmountObbAction(newState, force); 2515 mObbActionHandler.sendMessage(mObbActionHandler.obtainMessage(OBB_RUN_ACTION, action)); 2516 2517 if (DEBUG_OBB) 2518 Slog.i(TAG, "Send to OBB handler: " + action.toString()); 2519 } else { 2520 Slog.w(TAG, "Unknown OBB mount at " + rawPath); 2521 } 2522 } 2523 2524 @Override 2525 public int getEncryptionState() { 2526 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, 2527 "no permission to access the crypt keeper"); 2528 2529 waitForReady(); 2530 2531 final NativeDaemonEvent event; 2532 try { 2533 event = mCryptConnector.execute("cryptfs", "cryptocomplete"); 2534 return Integer.parseInt(event.getMessage()); 2535 } catch (NumberFormatException e) { 2536 // Bad result - unexpected. 2537 Slog.w(TAG, "Unable to parse result from cryptfs cryptocomplete"); 2538 return ENCRYPTION_STATE_ERROR_UNKNOWN; 2539 } catch (NativeDaemonConnectorException e) { 2540 // Something bad happened. 2541 Slog.w(TAG, "Error in communicating with cryptfs in validating"); 2542 return ENCRYPTION_STATE_ERROR_UNKNOWN; 2543 } 2544 } 2545 2546 @Override 2547 public int decryptStorage(String password) { 2548 if (TextUtils.isEmpty(password)) { 2549 throw new IllegalArgumentException("password cannot be empty"); 2550 } 2551 2552 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, 2553 "no permission to access the crypt keeper"); 2554 2555 waitForReady(); 2556 2557 if (DEBUG_EVENTS) { 2558 Slog.i(TAG, "decrypting storage..."); 2559 } 2560 2561 final NativeDaemonEvent event; 2562 try { 2563 event = mCryptConnector.execute("cryptfs", "checkpw", new SensitiveArg(password)); 2564 2565 final int code = Integer.parseInt(event.getMessage()); 2566 if (code == 0) { 2567 // Decrypt was successful. Post a delayed message before restarting in order 2568 // to let the UI to clear itself 2569 mHandler.postDelayed(new Runnable() { 2570 public void run() { 2571 try { 2572 mCryptConnector.execute("cryptfs", "restart"); 2573 } catch (NativeDaemonConnectorException e) { 2574 Slog.e(TAG, "problem executing in background", e); 2575 } 2576 } 2577 }, 1000); // 1 second 2578 } 2579 2580 return code; 2581 } catch (NativeDaemonConnectorException e) { 2582 // Decryption failed 2583 return e.getCode(); 2584 } 2585 } 2586 2587 public int encryptStorage(int type, String password) { 2588 if (TextUtils.isEmpty(password) && type != StorageManager.CRYPT_TYPE_DEFAULT) { 2589 throw new IllegalArgumentException("password cannot be empty"); 2590 } 2591 2592 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, 2593 "no permission to access the crypt keeper"); 2594 2595 waitForReady(); 2596 2597 if (DEBUG_EVENTS) { 2598 Slog.i(TAG, "encrypting storage..."); 2599 } 2600 2601 try { 2602 if (type == StorageManager.CRYPT_TYPE_DEFAULT) { 2603 mCryptConnector.execute("cryptfs", "enablecrypto", "inplace", 2604 CRYPTO_TYPES[type]); 2605 } else { 2606 mCryptConnector.execute("cryptfs", "enablecrypto", "inplace", 2607 CRYPTO_TYPES[type], new SensitiveArg(password)); 2608 } 2609 } catch (NativeDaemonConnectorException e) { 2610 // Encryption failed 2611 return e.getCode(); 2612 } 2613 2614 return 0; 2615 } 2616 2617 /** Set the password for encrypting the master key. 2618 * @param type One of the CRYPTO_TYPE_XXX consts defined in StorageManager. 2619 * @param password The password to set. 2620 */ 2621 public int changeEncryptionPassword(int type, String password) { 2622 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, 2623 "no permission to access the crypt keeper"); 2624 2625 waitForReady(); 2626 2627 if (DEBUG_EVENTS) { 2628 Slog.i(TAG, "changing encryption password..."); 2629 } 2630 2631 try { 2632 NativeDaemonEvent event = mCryptConnector.execute("cryptfs", "changepw", CRYPTO_TYPES[type], 2633 new SensitiveArg(password)); 2634 return Integer.parseInt(event.getMessage()); 2635 } catch (NativeDaemonConnectorException e) { 2636 // Encryption failed 2637 return e.getCode(); 2638 } 2639 } 2640 2641 /** 2642 * Validate a user-supplied password string with cryptfs 2643 */ 2644 @Override 2645 public int verifyEncryptionPassword(String password) throws RemoteException { 2646 // Only the system process is permitted to validate passwords 2647 if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) { 2648 throw new SecurityException("no permission to access the crypt keeper"); 2649 } 2650 2651 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, 2652 "no permission to access the crypt keeper"); 2653 2654 if (TextUtils.isEmpty(password)) { 2655 throw new IllegalArgumentException("password cannot be empty"); 2656 } 2657 2658 waitForReady(); 2659 2660 if (DEBUG_EVENTS) { 2661 Slog.i(TAG, "validating encryption password..."); 2662 } 2663 2664 final NativeDaemonEvent event; 2665 try { 2666 event = mCryptConnector.execute("cryptfs", "verifypw", new SensitiveArg(password)); 2667 Slog.i(TAG, "cryptfs verifypw => " + event.getMessage()); 2668 return Integer.parseInt(event.getMessage()); 2669 } catch (NativeDaemonConnectorException e) { 2670 // Encryption failed 2671 return e.getCode(); 2672 } 2673 } 2674 2675 /** 2676 * Get the type of encryption used to encrypt the master key. 2677 * @return The type, one of the CRYPT_TYPE_XXX consts from StorageManager. 2678 */ 2679 @Override 2680 public int getPasswordType() { 2681 mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL, 2682 "no permission to access the crypt keeper"); 2683 2684 waitForReady(); 2685 2686 final NativeDaemonEvent event; 2687 try { 2688 event = mCryptConnector.execute("cryptfs", "getpwtype"); 2689 for (int i = 0; i < CRYPTO_TYPES.length; ++i) { 2690 if (CRYPTO_TYPES[i].equals(event.getMessage())) 2691 return i; 2692 } 2693 2694 throw new IllegalStateException("unexpected return from cryptfs"); 2695 } catch (NativeDaemonConnectorException e) { 2696 throw e.rethrowAsParcelableException(); 2697 } 2698 } 2699 2700 /** 2701 * Set a field in the crypto header. 2702 * @param field field to set 2703 * @param contents contents to set in field 2704 */ 2705 @Override 2706 public void setField(String field, String contents) throws RemoteException { 2707 mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL, 2708 "no permission to access the crypt keeper"); 2709 2710 waitForReady(); 2711 2712 final NativeDaemonEvent event; 2713 try { 2714 event = mCryptConnector.execute("cryptfs", "setfield", field, contents); 2715 } catch (NativeDaemonConnectorException e) { 2716 throw e.rethrowAsParcelableException(); 2717 } 2718 } 2719 2720 /** 2721 * Gets a field from the crypto header. 2722 * @param field field to get 2723 * @return contents of field 2724 */ 2725 @Override 2726 public String getField(String field) throws RemoteException { 2727 mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL, 2728 "no permission to access the crypt keeper"); 2729 2730 waitForReady(); 2731 2732 final NativeDaemonEvent event; 2733 try { 2734 final String[] contents = NativeDaemonEvent.filterMessageList( 2735 mCryptConnector.executeForList("cryptfs", "getfield", field), 2736 VoldResponseCode.CryptfsGetfieldResult); 2737 String result = new String(); 2738 for (String content : contents) { 2739 result += content; 2740 } 2741 return result; 2742 } catch (NativeDaemonConnectorException e) { 2743 throw e.rethrowAsParcelableException(); 2744 } 2745 } 2746 2747 /** 2748 * Is userdata convertible to file based encryption? 2749 * @return non zero for convertible 2750 */ 2751 @Override 2752 public boolean isConvertibleToFBE() throws RemoteException { 2753 mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL, 2754 "no permission to access the crypt keeper"); 2755 2756 waitForReady(); 2757 2758 final NativeDaemonEvent event; 2759 try { 2760 event = mCryptConnector.execute("cryptfs", "isConvertibleToFBE"); 2761 return Integer.parseInt(event.getMessage()) != 0; 2762 } catch (NativeDaemonConnectorException e) { 2763 throw e.rethrowAsParcelableException(); 2764 } 2765 } 2766 2767 @Override 2768 public String getPassword() throws RemoteException { 2769 mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL, 2770 "only keyguard can retrieve password"); 2771 2772 if (!isReady()) { 2773 return new String(); 2774 } 2775 2776 final NativeDaemonEvent event; 2777 try { 2778 event = mCryptConnector.execute("cryptfs", "getpw"); 2779 if ("-1".equals(event.getMessage())) { 2780 // -1 equals no password 2781 return null; 2782 } 2783 return event.getMessage(); 2784 } catch (NativeDaemonConnectorException e) { 2785 throw e.rethrowAsParcelableException(); 2786 } catch (IllegalArgumentException e) { 2787 Slog.e(TAG, "Invalid response to getPassword"); 2788 return null; 2789 } 2790 } 2791 2792 @Override 2793 public void clearPassword() throws RemoteException { 2794 mContext.enforceCallingOrSelfPermission(Manifest.permission.STORAGE_INTERNAL, 2795 "only keyguard can clear password"); 2796 2797 if (!isReady()) { 2798 return; 2799 } 2800 2801 final NativeDaemonEvent event; 2802 try { 2803 event = mCryptConnector.execute("cryptfs", "clearpw"); 2804 } catch (NativeDaemonConnectorException e) { 2805 throw e.rethrowAsParcelableException(); 2806 } 2807 } 2808 2809 @Override 2810 public void createUserKey(int userId, int serialNumber, boolean ephemeral) { 2811 enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); 2812 waitForReady(); 2813 2814 try { 2815 mCryptConnector.execute("cryptfs", "create_user_key", userId, serialNumber, 2816 ephemeral ? 1 : 0); 2817 } catch (NativeDaemonConnectorException e) { 2818 throw e.rethrowAsParcelableException(); 2819 } 2820 } 2821 2822 @Override 2823 public void destroyUserKey(int userId) { 2824 enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); 2825 waitForReady(); 2826 2827 try { 2828 mCryptConnector.execute("cryptfs", "destroy_user_key", userId); 2829 } catch (NativeDaemonConnectorException e) { 2830 throw e.rethrowAsParcelableException(); 2831 } 2832 } 2833 2834 private SensitiveArg encodeBytes(byte[] bytes) { 2835 if (ArrayUtils.isEmpty(bytes)) { 2836 return new SensitiveArg("!"); 2837 } else { 2838 return new SensitiveArg(HexDump.toHexString(bytes)); 2839 } 2840 } 2841 2842 /* 2843 * Add this token/secret pair to the set of ways we can recover a disk encryption key. 2844 * Changing the token/secret for a disk encryption key is done in two phases: first, adding 2845 * a new token/secret pair with this call, then delting all other pairs with 2846 * fixateNewestUserKeyAuth. This allows other places where a credential is used, such as 2847 * Gatekeeper, to be updated between the two calls. 2848 */ 2849 @Override 2850 public void addUserKeyAuth(int userId, int serialNumber, byte[] token, byte[] secret) { 2851 enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); 2852 waitForReady(); 2853 2854 try { 2855 mCryptConnector.execute("cryptfs", "add_user_key_auth", userId, serialNumber, 2856 encodeBytes(token), encodeBytes(secret)); 2857 } catch (NativeDaemonConnectorException e) { 2858 throw e.rethrowAsParcelableException(); 2859 } 2860 } 2861 2862 /* 2863 * Delete all disk encryption token/secret pairs except the most recently added one 2864 */ 2865 @Override 2866 public void fixateNewestUserKeyAuth(int userId) { 2867 enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); 2868 waitForReady(); 2869 2870 try { 2871 mCryptConnector.execute("cryptfs", "fixate_newest_user_key_auth", userId); 2872 } catch (NativeDaemonConnectorException e) { 2873 throw e.rethrowAsParcelableException(); 2874 } 2875 } 2876 2877 @Override 2878 public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) { 2879 enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); 2880 waitForReady(); 2881 2882 if (StorageManager.isFileEncryptedNativeOrEmulated()) { 2883 // When a user has secure lock screen, require a challenge token to 2884 // actually unlock. This check is mostly in place for emulation mode. 2885 if (mLockPatternUtils.isSecure(userId) && ArrayUtils.isEmpty(token)) { 2886 throw new IllegalStateException("Token required to unlock secure user " + userId); 2887 } 2888 2889 try { 2890 mCryptConnector.execute("cryptfs", "unlock_user_key", userId, serialNumber, 2891 encodeBytes(token), encodeBytes(secret)); 2892 } catch (NativeDaemonConnectorException e) { 2893 throw e.rethrowAsParcelableException(); 2894 } 2895 } 2896 2897 synchronized (mLock) { 2898 mLocalUnlockedUsers = ArrayUtils.appendInt(mLocalUnlockedUsers, userId); 2899 } 2900 } 2901 2902 @Override 2903 public void lockUserKey(int userId) { 2904 enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); 2905 waitForReady(); 2906 2907 try { 2908 mCryptConnector.execute("cryptfs", "lock_user_key", userId); 2909 } catch (NativeDaemonConnectorException e) { 2910 throw e.rethrowAsParcelableException(); 2911 } 2912 2913 synchronized (mLock) { 2914 mLocalUnlockedUsers = ArrayUtils.removeInt(mLocalUnlockedUsers, userId); 2915 } 2916 } 2917 2918 @Override 2919 public boolean isUserKeyUnlocked(int userId) { 2920 synchronized (mLock) { 2921 return ArrayUtils.contains(mLocalUnlockedUsers, userId); 2922 } 2923 } 2924 2925 @Override 2926 public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) { 2927 enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); 2928 waitForReady(); 2929 2930 try { 2931 mCryptConnector.execute("cryptfs", "prepare_user_storage", escapeNull(volumeUuid), 2932 userId, serialNumber, flags); 2933 } catch (NativeDaemonConnectorException e) { 2934 throw e.rethrowAsParcelableException(); 2935 } 2936 } 2937 2938 @Override 2939 public void destroyUserStorage(String volumeUuid, int userId, int flags) { 2940 enforcePermission(android.Manifest.permission.STORAGE_INTERNAL); 2941 waitForReady(); 2942 2943 try { 2944 mCryptConnector.execute("cryptfs", "destroy_user_storage", escapeNull(volumeUuid), 2945 userId, flags); 2946 } catch (NativeDaemonConnectorException e) { 2947 throw e.rethrowAsParcelableException(); 2948 } 2949 } 2950 2951 @Override 2952 public ParcelFileDescriptor mountAppFuse(final String name) throws RemoteException { 2953 try { 2954 final int uid = Binder.getCallingUid(); 2955 final int pid = Binder.getCallingPid(); 2956 final NativeDaemonEvent event = 2957 mConnector.execute("appfuse", "mount", uid, pid, name); 2958 if (event.getFileDescriptors() == null) { 2959 throw new RemoteException("AppFuse FD from vold is null."); 2960 } 2961 return ParcelFileDescriptor.fromFd( 2962 event.getFileDescriptors()[0], 2963 mHandler, 2964 new ParcelFileDescriptor.OnCloseListener() { 2965 @Override 2966 public void onClose(IOException e) { 2967 try { 2968 final NativeDaemonEvent event = mConnector.execute( 2969 "appfuse", "unmount", uid, pid, name); 2970 } catch (NativeDaemonConnectorException unmountException) { 2971 Log.e(TAG, "Failed to unmount appfuse."); 2972 } 2973 } 2974 }); 2975 } catch (NativeDaemonConnectorException e) { 2976 throw e.rethrowAsParcelableException(); 2977 } catch (IOException e) { 2978 throw new RemoteException(e.getMessage()); 2979 } 2980 } 2981 2982 @Override 2983 public int mkdirs(String callingPkg, String appPath) { 2984 final int userId = UserHandle.getUserId(Binder.getCallingUid()); 2985 final UserEnvironment userEnv = new UserEnvironment(userId); 2986 2987 // Validate that reported package name belongs to caller 2988 final AppOpsManager appOps = (AppOpsManager) mContext.getSystemService( 2989 Context.APP_OPS_SERVICE); 2990 appOps.checkPackage(Binder.getCallingUid(), callingPkg); 2991 2992 File appFile = null; 2993 try { 2994 appFile = new File(appPath).getCanonicalFile(); 2995 } catch (IOException e) { 2996 Slog.e(TAG, "Failed to resolve " + appPath + ": " + e); 2997 return -1; 2998 } 2999 3000 // Try translating the app path into a vold path, but require that it 3001 // belong to the calling package. 3002 if (FileUtils.contains(userEnv.buildExternalStorageAppDataDirs(callingPkg), appFile) || 3003 FileUtils.contains(userEnv.buildExternalStorageAppObbDirs(callingPkg), appFile) || 3004 FileUtils.contains(userEnv.buildExternalStorageAppMediaDirs(callingPkg), appFile)) { 3005 appPath = appFile.getAbsolutePath(); 3006 if (!appPath.endsWith("/")) { 3007 appPath = appPath + "/"; 3008 } 3009 3010 try { 3011 mConnector.execute("volume", "mkdirs", appPath); 3012 return 0; 3013 } catch (NativeDaemonConnectorException e) { 3014 return e.getCode(); 3015 } 3016 } 3017 3018 throw new SecurityException("Invalid mkdirs path: " + appFile); 3019 } 3020 3021 @Override 3022 public StorageVolume[] getVolumeList(int uid, String packageName, int flags) { 3023 final int userId = UserHandle.getUserId(uid); 3024 3025 final boolean forWrite = (flags & StorageManager.FLAG_FOR_WRITE) != 0; 3026 final boolean realState = (flags & StorageManager.FLAG_REAL_STATE) != 0; 3027 final boolean includeInvisible = (flags & StorageManager.FLAG_INCLUDE_INVISIBLE) != 0; 3028 3029 final boolean userKeyUnlocked; 3030 final boolean storagePermission; 3031 final long token = Binder.clearCallingIdentity(); 3032 try { 3033 userKeyUnlocked = isUserKeyUnlocked(userId); 3034 storagePermission = mMountServiceInternal.hasExternalStorage(uid, packageName); 3035 } finally { 3036 Binder.restoreCallingIdentity(token); 3037 } 3038 3039 boolean foundPrimary = false; 3040 3041 final ArrayList<StorageVolume> res = new ArrayList<>(); 3042 synchronized (mLock) { 3043 for (int i = 0; i < mVolumes.size(); i++) { 3044 final VolumeInfo vol = mVolumes.valueAt(i); 3045 switch (vol.getType()) { 3046 case VolumeInfo.TYPE_PUBLIC: 3047 case VolumeInfo.TYPE_EMULATED: 3048 break; 3049 default: 3050 continue; 3051 } 3052 3053 boolean match = false; 3054 if (forWrite) { 3055 match = vol.isVisibleForWrite(userId); 3056 } else { 3057 match = vol.isVisibleForRead(userId) 3058 || (includeInvisible && vol.getPath() != null); 3059 } 3060 if (!match) continue; 3061 3062 boolean reportUnmounted = false; 3063 if ((vol.getType() == VolumeInfo.TYPE_EMULATED) && !userKeyUnlocked) { 3064 reportUnmounted = true; 3065 } else if (!storagePermission && !realState) { 3066 reportUnmounted = true; 3067 } 3068 3069 final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, 3070 reportUnmounted); 3071 if (vol.isPrimary()) { 3072 res.add(0, userVol); 3073 foundPrimary = true; 3074 } else { 3075 res.add(userVol); 3076 } 3077 } 3078 } 3079 3080 if (!foundPrimary) { 3081 Log.w(TAG, "No primary storage defined yet; hacking together a stub"); 3082 3083 final boolean primaryPhysical = SystemProperties.getBoolean( 3084 StorageManager.PROP_PRIMARY_PHYSICAL, false); 3085 3086 final String id = "stub_primary"; 3087 final File path = Environment.getLegacyExternalStorageDirectory(); 3088 final String description = mContext.getString(android.R.string.unknownName); 3089 final boolean primary = true; 3090 final boolean removable = primaryPhysical; 3091 final boolean emulated = !primaryPhysical; 3092 final long mtpReserveSize = 0L; 3093 final boolean allowMassStorage = false; 3094 final long maxFileSize = 0L; 3095 final UserHandle owner = new UserHandle(userId); 3096 final String uuid = null; 3097 final String state = Environment.MEDIA_REMOVED; 3098 3099 res.add(0, new StorageVolume(id, StorageVolume.STORAGE_ID_INVALID, path, 3100 description, primary, removable, emulated, mtpReserveSize, 3101 allowMassStorage, maxFileSize, owner, uuid, state)); 3102 } 3103 3104 return res.toArray(new StorageVolume[res.size()]); 3105 } 3106 3107 @Override 3108 public DiskInfo[] getDisks() { 3109 synchronized (mLock) { 3110 final DiskInfo[] res = new DiskInfo[mDisks.size()]; 3111 for (int i = 0; i < mDisks.size(); i++) { 3112 res[i] = mDisks.valueAt(i); 3113 } 3114 return res; 3115 } 3116 } 3117 3118 @Override 3119 public VolumeInfo[] getVolumes(int flags) { 3120 synchronized (mLock) { 3121 final VolumeInfo[] res = new VolumeInfo[mVolumes.size()]; 3122 for (int i = 0; i < mVolumes.size(); i++) { 3123 res[i] = mVolumes.valueAt(i); 3124 } 3125 return res; 3126 } 3127 } 3128 3129 @Override 3130 public VolumeRecord[] getVolumeRecords(int flags) { 3131 synchronized (mLock) { 3132 final VolumeRecord[] res = new VolumeRecord[mRecords.size()]; 3133 for (int i = 0; i < mRecords.size(); i++) { 3134 res[i] = mRecords.valueAt(i); 3135 } 3136 return res; 3137 } 3138 } 3139 3140 private void addObbStateLocked(ObbState obbState) throws RemoteException { 3141 final IBinder binder = obbState.getBinder(); 3142 List<ObbState> obbStates = mObbMounts.get(binder); 3143 3144 if (obbStates == null) { 3145 obbStates = new ArrayList<ObbState>(); 3146 mObbMounts.put(binder, obbStates); 3147 } else { 3148 for (final ObbState o : obbStates) { 3149 if (o.rawPath.equals(obbState.rawPath)) { 3150 throw new IllegalStateException("Attempt to add ObbState twice. " 3151 + "This indicates an error in the MountService logic."); 3152 } 3153 } 3154 } 3155 3156 obbStates.add(obbState); 3157 try { 3158 obbState.link(); 3159 } catch (RemoteException e) { 3160 /* 3161 * The binder died before we could link it, so clean up our state 3162 * and return failure. 3163 */ 3164 obbStates.remove(obbState); 3165 if (obbStates.isEmpty()) { 3166 mObbMounts.remove(binder); 3167 } 3168 3169 // Rethrow the error so mountObb can get it 3170 throw e; 3171 } 3172 3173 mObbPathToStateMap.put(obbState.rawPath, obbState); 3174 } 3175 3176 private void removeObbStateLocked(ObbState obbState) { 3177 final IBinder binder = obbState.getBinder(); 3178 final List<ObbState> obbStates = mObbMounts.get(binder); 3179 if (obbStates != null) { 3180 if (obbStates.remove(obbState)) { 3181 obbState.unlink(); 3182 } 3183 if (obbStates.isEmpty()) { 3184 mObbMounts.remove(binder); 3185 } 3186 } 3187 3188 mObbPathToStateMap.remove(obbState.rawPath); 3189 } 3190 3191 private class ObbActionHandler extends Handler { 3192 private boolean mBound = false; 3193 private final List<ObbAction> mActions = new LinkedList<ObbAction>(); 3194 3195 ObbActionHandler(Looper l) { 3196 super(l); 3197 } 3198 3199 @Override 3200 public void handleMessage(Message msg) { 3201 switch (msg.what) { 3202 case OBB_RUN_ACTION: { 3203 final ObbAction action = (ObbAction) msg.obj; 3204 3205 if (DEBUG_OBB) 3206 Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString()); 3207 3208 // If a bind was already initiated we don't really 3209 // need to do anything. The pending install 3210 // will be processed later on. 3211 if (!mBound) { 3212 // If this is the only one pending we might 3213 // have to bind to the service again. 3214 if (!connectToService()) { 3215 Slog.e(TAG, "Failed to bind to media container service"); 3216 action.handleError(); 3217 return; 3218 } 3219 } 3220 3221 mActions.add(action); 3222 break; 3223 } 3224 case OBB_MCS_BOUND: { 3225 if (DEBUG_OBB) 3226 Slog.i(TAG, "OBB_MCS_BOUND"); 3227 if (msg.obj != null) { 3228 mContainerService = (IMediaContainerService) msg.obj; 3229 } 3230 if (mContainerService == null) { 3231 // Something seriously wrong. Bail out 3232 Slog.e(TAG, "Cannot bind to media container service"); 3233 for (ObbAction action : mActions) { 3234 // Indicate service bind error 3235 action.handleError(); 3236 } 3237 mActions.clear(); 3238 } else if (mActions.size() > 0) { 3239 final ObbAction action = mActions.get(0); 3240 if (action != null) { 3241 action.execute(this); 3242 } 3243 } else { 3244 // Should never happen ideally. 3245 Slog.w(TAG, "Empty queue"); 3246 } 3247 break; 3248 } 3249 case OBB_MCS_RECONNECT: { 3250 if (DEBUG_OBB) 3251 Slog.i(TAG, "OBB_MCS_RECONNECT"); 3252 if (mActions.size() > 0) { 3253 if (mBound) { 3254 disconnectService(); 3255 } 3256 if (!connectToService()) { 3257 Slog.e(TAG, "Failed to bind to media container service"); 3258 for (ObbAction action : mActions) { 3259 // Indicate service bind error 3260 action.handleError(); 3261 } 3262 mActions.clear(); 3263 } 3264 } 3265 break; 3266 } 3267 case OBB_MCS_UNBIND: { 3268 if (DEBUG_OBB) 3269 Slog.i(TAG, "OBB_MCS_UNBIND"); 3270 3271 // Delete pending install 3272 if (mActions.size() > 0) { 3273 mActions.remove(0); 3274 } 3275 if (mActions.size() == 0) { 3276 if (mBound) { 3277 disconnectService(); 3278 } 3279 } else { 3280 // There are more pending requests in queue. 3281 // Just post MCS_BOUND message to trigger processing 3282 // of next pending install. 3283 mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND); 3284 } 3285 break; 3286 } 3287 case OBB_FLUSH_MOUNT_STATE: { 3288 final String path = (String) msg.obj; 3289 3290 if (DEBUG_OBB) 3291 Slog.i(TAG, "Flushing all OBB state for path " + path); 3292 3293 synchronized (mObbMounts) { 3294 final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>(); 3295 3296 final Iterator<ObbState> i = mObbPathToStateMap.values().iterator(); 3297 while (i.hasNext()) { 3298 final ObbState state = i.next(); 3299 3300 /* 3301 * If this entry's source file is in the volume path 3302 * that got unmounted, remove it because it's no 3303 * longer valid. 3304 */ 3305 if (state.canonicalPath.startsWith(path)) { 3306 obbStatesToRemove.add(state); 3307 } 3308 } 3309 3310 for (final ObbState obbState : obbStatesToRemove) { 3311 if (DEBUG_OBB) 3312 Slog.i(TAG, "Removing state for " + obbState.rawPath); 3313 3314 removeObbStateLocked(obbState); 3315 3316 try { 3317 obbState.token.onObbResult(obbState.rawPath, obbState.nonce, 3318 OnObbStateChangeListener.UNMOUNTED); 3319 } catch (RemoteException e) { 3320 Slog.i(TAG, "Couldn't send unmount notification for OBB: " 3321 + obbState.rawPath); 3322 } 3323 } 3324 } 3325 break; 3326 } 3327 } 3328 } 3329 3330 private boolean connectToService() { 3331 if (DEBUG_OBB) 3332 Slog.i(TAG, "Trying to bind to DefaultContainerService"); 3333 3334 Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT); 3335 if (mContext.bindServiceAsUser(service, mDefContainerConn, Context.BIND_AUTO_CREATE, 3336 UserHandle.SYSTEM)) { 3337 mBound = true; 3338 return true; 3339 } 3340 return false; 3341 } 3342 3343 private void disconnectService() { 3344 mContainerService = null; 3345 mBound = false; 3346 mContext.unbindService(mDefContainerConn); 3347 } 3348 } 3349 3350 abstract class ObbAction { 3351 private static final int MAX_RETRIES = 3; 3352 private int mRetries; 3353 3354 ObbState mObbState; 3355 3356 ObbAction(ObbState obbState) { 3357 mObbState = obbState; 3358 } 3359 3360 public void execute(ObbActionHandler handler) { 3361 try { 3362 if (DEBUG_OBB) 3363 Slog.i(TAG, "Starting to execute action: " + toString()); 3364 mRetries++; 3365 if (mRetries > MAX_RETRIES) { 3366 Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up"); 3367 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND); 3368 handleError(); 3369 } else { 3370 handleExecute(); 3371 if (DEBUG_OBB) 3372 Slog.i(TAG, "Posting install MCS_UNBIND"); 3373 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND); 3374 } 3375 } catch (RemoteException e) { 3376 if (DEBUG_OBB) 3377 Slog.i(TAG, "Posting install MCS_RECONNECT"); 3378 mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT); 3379 } catch (Exception e) { 3380 if (DEBUG_OBB) 3381 Slog.d(TAG, "Error handling OBB action", e); 3382 handleError(); 3383 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND); 3384 } 3385 } 3386 3387 abstract void handleExecute() throws RemoteException, IOException; 3388 abstract void handleError(); 3389 3390 protected ObbInfo getObbInfo() throws IOException { 3391 ObbInfo obbInfo; 3392 try { 3393 obbInfo = mContainerService.getObbInfo(mObbState.canonicalPath); 3394 } catch (RemoteException e) { 3395 Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for " 3396 + mObbState.canonicalPath); 3397 obbInfo = null; 3398 } 3399 if (obbInfo == null) { 3400 throw new IOException("Couldn't read OBB file: " + mObbState.canonicalPath); 3401 } 3402 return obbInfo; 3403 } 3404 3405 protected void sendNewStatusOrIgnore(int status) { 3406 if (mObbState == null || mObbState.token == null) { 3407 return; 3408 } 3409 3410 try { 3411 mObbState.token.onObbResult(mObbState.rawPath, mObbState.nonce, status); 3412 } catch (RemoteException e) { 3413 Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged"); 3414 } 3415 } 3416 } 3417 3418 class MountObbAction extends ObbAction { 3419 private final String mKey; 3420 private final int mCallingUid; 3421 3422 MountObbAction(ObbState obbState, String key, int callingUid) { 3423 super(obbState); 3424 mKey = key; 3425 mCallingUid = callingUid; 3426 } 3427 3428 @Override 3429 public void handleExecute() throws IOException, RemoteException { 3430 waitForReady(); 3431 warnOnNotMounted(); 3432 3433 final ObbInfo obbInfo = getObbInfo(); 3434 3435 if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mCallingUid)) { 3436 Slog.w(TAG, "Denied attempt to mount OBB " + obbInfo.filename 3437 + " which is owned by " + obbInfo.packageName); 3438 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED); 3439 return; 3440 } 3441 3442 final boolean isMounted; 3443 synchronized (mObbMounts) { 3444 isMounted = mObbPathToStateMap.containsKey(mObbState.rawPath); 3445 } 3446 if (isMounted) { 3447 Slog.w(TAG, "Attempt to mount OBB which is already mounted: " + obbInfo.filename); 3448 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_ALREADY_MOUNTED); 3449 return; 3450 } 3451 3452 final String hashedKey; 3453 if (mKey == null) { 3454 hashedKey = "none"; 3455 } else { 3456 try { 3457 SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); 3458 3459 KeySpec ks = new PBEKeySpec(mKey.toCharArray(), obbInfo.salt, 3460 PBKDF2_HASH_ROUNDS, CRYPTO_ALGORITHM_KEY_SIZE); 3461 SecretKey key = factory.generateSecret(ks); 3462 BigInteger bi = new BigInteger(key.getEncoded()); 3463 hashedKey = bi.toString(16); 3464 } catch (NoSuchAlgorithmException e) { 3465 Slog.e(TAG, "Could not load PBKDF2 algorithm", e); 3466 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL); 3467 return; 3468 } catch (InvalidKeySpecException e) { 3469 Slog.e(TAG, "Invalid key spec when loading PBKDF2 algorithm", e); 3470 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL); 3471 return; 3472 } 3473 } 3474 3475 int rc = StorageResultCode.OperationSucceeded; 3476 try { 3477 mConnector.execute("obb", "mount", mObbState.canonicalPath, new SensitiveArg(hashedKey), 3478 mObbState.ownerGid); 3479 } catch (NativeDaemonConnectorException e) { 3480 int code = e.getCode(); 3481 if (code != VoldResponseCode.OpFailedStorageBusy) { 3482 rc = StorageResultCode.OperationFailedInternalError; 3483 } 3484 } 3485 3486 if (rc == StorageResultCode.OperationSucceeded) { 3487 if (DEBUG_OBB) 3488 Slog.d(TAG, "Successfully mounted OBB " + mObbState.canonicalPath); 3489 3490 synchronized (mObbMounts) { 3491 addObbStateLocked(mObbState); 3492 } 3493 3494 sendNewStatusOrIgnore(OnObbStateChangeListener.MOUNTED); 3495 } else { 3496 Slog.e(TAG, "Couldn't mount OBB file: " + rc); 3497 3498 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT); 3499 } 3500 } 3501 3502 @Override 3503 public void handleError() { 3504 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL); 3505 } 3506 3507 @Override 3508 public String toString() { 3509 StringBuilder sb = new StringBuilder(); 3510 sb.append("MountObbAction{"); 3511 sb.append(mObbState); 3512 sb.append('}'); 3513 return sb.toString(); 3514 } 3515 } 3516 3517 class UnmountObbAction extends ObbAction { 3518 private final boolean mForceUnmount; 3519 3520 UnmountObbAction(ObbState obbState, boolean force) { 3521 super(obbState); 3522 mForceUnmount = force; 3523 } 3524 3525 @Override 3526 public void handleExecute() throws IOException { 3527 waitForReady(); 3528 warnOnNotMounted(); 3529 3530 final ObbState existingState; 3531 synchronized (mObbMounts) { 3532 existingState = mObbPathToStateMap.get(mObbState.rawPath); 3533 } 3534 3535 if (existingState == null) { 3536 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_NOT_MOUNTED); 3537 return; 3538 } 3539 3540 if (existingState.ownerGid != mObbState.ownerGid) { 3541 Slog.w(TAG, "Permission denied attempting to unmount OBB " + existingState.rawPath 3542 + " (owned by GID " + existingState.ownerGid + ")"); 3543 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED); 3544 return; 3545 } 3546 3547 int rc = StorageResultCode.OperationSucceeded; 3548 try { 3549 final Command cmd = new Command("obb", "unmount", mObbState.canonicalPath); 3550 if (mForceUnmount) { 3551 cmd.appendArg("force"); 3552 } 3553 mConnector.execute(cmd); 3554 } catch (NativeDaemonConnectorException e) { 3555 int code = e.getCode(); 3556 if (code == VoldResponseCode.OpFailedStorageBusy) { 3557 rc = StorageResultCode.OperationFailedStorageBusy; 3558 } else if (code == VoldResponseCode.OpFailedStorageNotFound) { 3559 // If it's not mounted then we've already won. 3560 rc = StorageResultCode.OperationSucceeded; 3561 } else { 3562 rc = StorageResultCode.OperationFailedInternalError; 3563 } 3564 } 3565 3566 if (rc == StorageResultCode.OperationSucceeded) { 3567 synchronized (mObbMounts) { 3568 removeObbStateLocked(existingState); 3569 } 3570 3571 sendNewStatusOrIgnore(OnObbStateChangeListener.UNMOUNTED); 3572 } else { 3573 Slog.w(TAG, "Could not unmount OBB: " + existingState); 3574 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT); 3575 } 3576 } 3577 3578 @Override 3579 public void handleError() { 3580 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL); 3581 } 3582 3583 @Override 3584 public String toString() { 3585 StringBuilder sb = new StringBuilder(); 3586 sb.append("UnmountObbAction{"); 3587 sb.append(mObbState); 3588 sb.append(",force="); 3589 sb.append(mForceUnmount); 3590 sb.append('}'); 3591 return sb.toString(); 3592 } 3593 } 3594 3595 private static class Callbacks extends Handler { 3596 private static final int MSG_STORAGE_STATE_CHANGED = 1; 3597 private static final int MSG_VOLUME_STATE_CHANGED = 2; 3598 private static final int MSG_VOLUME_RECORD_CHANGED = 3; 3599 private static final int MSG_VOLUME_FORGOTTEN = 4; 3600 private static final int MSG_DISK_SCANNED = 5; 3601 private static final int MSG_DISK_DESTROYED = 6; 3602 3603 private final RemoteCallbackList<IMountServiceListener> 3604 mCallbacks = new RemoteCallbackList<>(); 3605 3606 public Callbacks(Looper looper) { 3607 super(looper); 3608 } 3609 3610 public void register(IMountServiceListener callback) { 3611 mCallbacks.register(callback); 3612 } 3613 3614 public void unregister(IMountServiceListener callback) { 3615 mCallbacks.unregister(callback); 3616 } 3617 3618 @Override 3619 public void handleMessage(Message msg) { 3620 final SomeArgs args = (SomeArgs) msg.obj; 3621 final int n = mCallbacks.beginBroadcast(); 3622 for (int i = 0; i < n; i++) { 3623 final IMountServiceListener callback = mCallbacks.getBroadcastItem(i); 3624 try { 3625 invokeCallback(callback, msg.what, args); 3626 } catch (RemoteException ignored) { 3627 } 3628 } 3629 mCallbacks.finishBroadcast(); 3630 args.recycle(); 3631 } 3632 3633 private void invokeCallback(IMountServiceListener callback, int what, SomeArgs args) 3634 throws RemoteException { 3635 switch (what) { 3636 case MSG_STORAGE_STATE_CHANGED: { 3637 callback.onStorageStateChanged((String) args.arg1, (String) args.arg2, 3638 (String) args.arg3); 3639 break; 3640 } 3641 case MSG_VOLUME_STATE_CHANGED: { 3642 callback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3); 3643 break; 3644 } 3645 case MSG_VOLUME_RECORD_CHANGED: { 3646 callback.onVolumeRecordChanged((VolumeRecord) args.arg1); 3647 break; 3648 } 3649 case MSG_VOLUME_FORGOTTEN: { 3650 callback.onVolumeForgotten((String) args.arg1); 3651 break; 3652 } 3653 case MSG_DISK_SCANNED: { 3654 callback.onDiskScanned((DiskInfo) args.arg1, args.argi2); 3655 break; 3656 } 3657 case MSG_DISK_DESTROYED: { 3658 callback.onDiskDestroyed((DiskInfo) args.arg1); 3659 break; 3660 } 3661 } 3662 } 3663 3664 private void notifyStorageStateChanged(String path, String oldState, String newState) { 3665 final SomeArgs args = SomeArgs.obtain(); 3666 args.arg1 = path; 3667 args.arg2 = oldState; 3668 args.arg3 = newState; 3669 obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget(); 3670 } 3671 3672 private void notifyVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { 3673 final SomeArgs args = SomeArgs.obtain(); 3674 args.arg1 = vol.clone(); 3675 args.argi2 = oldState; 3676 args.argi3 = newState; 3677 obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget(); 3678 } 3679 3680 private void notifyVolumeRecordChanged(VolumeRecord rec) { 3681 final SomeArgs args = SomeArgs.obtain(); 3682 args.arg1 = rec.clone(); 3683 obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget(); 3684 } 3685 3686 private void notifyVolumeForgotten(String fsUuid) { 3687 final SomeArgs args = SomeArgs.obtain(); 3688 args.arg1 = fsUuid; 3689 obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget(); 3690 } 3691 3692 private void notifyDiskScanned(DiskInfo disk, int volumeCount) { 3693 final SomeArgs args = SomeArgs.obtain(); 3694 args.arg1 = disk.clone(); 3695 args.argi2 = volumeCount; 3696 obtainMessage(MSG_DISK_SCANNED, args).sendToTarget(); 3697 } 3698 3699 private void notifyDiskDestroyed(DiskInfo disk) { 3700 final SomeArgs args = SomeArgs.obtain(); 3701 args.arg1 = disk.clone(); 3702 obtainMessage(MSG_DISK_DESTROYED, args).sendToTarget(); 3703 } 3704 } 3705 3706 @Override 3707 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 3708 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); 3709 3710 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ", 160); 3711 synchronized (mLock) { 3712 pw.println("Disks:"); 3713 pw.increaseIndent(); 3714 for (int i = 0; i < mDisks.size(); i++) { 3715 final DiskInfo disk = mDisks.valueAt(i); 3716 disk.dump(pw); 3717 } 3718 pw.decreaseIndent(); 3719 3720 pw.println(); 3721 pw.println("Volumes:"); 3722 pw.increaseIndent(); 3723 for (int i = 0; i < mVolumes.size(); i++) { 3724 final VolumeInfo vol = mVolumes.valueAt(i); 3725 if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.id)) continue; 3726 vol.dump(pw); 3727 } 3728 pw.decreaseIndent(); 3729 3730 pw.println(); 3731 pw.println("Records:"); 3732 pw.increaseIndent(); 3733 for (int i = 0; i < mRecords.size(); i++) { 3734 final VolumeRecord note = mRecords.valueAt(i); 3735 note.dump(pw); 3736 } 3737 pw.decreaseIndent(); 3738 3739 pw.println(); 3740 pw.println("Primary storage UUID: " + mPrimaryStorageUuid); 3741 pw.println("Force adoptable: " + mForceAdoptable); 3742 pw.println(); 3743 pw.println("Local unlocked users: " + Arrays.toString(mLocalUnlockedUsers)); 3744 pw.println("System unlocked users: " + Arrays.toString(mSystemUnlockedUsers)); 3745 } 3746 3747 synchronized (mObbMounts) { 3748 pw.println(); 3749 pw.println("mObbMounts:"); 3750 pw.increaseIndent(); 3751 final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet() 3752 .iterator(); 3753 while (binders.hasNext()) { 3754 Entry<IBinder, List<ObbState>> e = binders.next(); 3755 pw.println(e.getKey() + ":"); 3756 pw.increaseIndent(); 3757 final List<ObbState> obbStates = e.getValue(); 3758 for (final ObbState obbState : obbStates) { 3759 pw.println(obbState); 3760 } 3761 pw.decreaseIndent(); 3762 } 3763 pw.decreaseIndent(); 3764 3765 pw.println(); 3766 pw.println("mObbPathToStateMap:"); 3767 pw.increaseIndent(); 3768 final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator(); 3769 while (maps.hasNext()) { 3770 final Entry<String, ObbState> e = maps.next(); 3771 pw.print(e.getKey()); 3772 pw.print(" -> "); 3773 pw.println(e.getValue()); 3774 } 3775 pw.decreaseIndent(); 3776 } 3777 3778 pw.println(); 3779 pw.println("mConnector:"); 3780 pw.increaseIndent(); 3781 mConnector.dump(fd, pw, args); 3782 pw.decreaseIndent(); 3783 3784 pw.println(); 3785 pw.println("mCryptConnector:"); 3786 pw.increaseIndent(); 3787 mCryptConnector.dump(fd, pw, args); 3788 pw.decreaseIndent(); 3789 3790 pw.println(); 3791 pw.print("Last maintenance: "); 3792 pw.println(TimeUtils.formatForLogging(mLastMaintenance)); 3793 } 3794 3795 /** {@inheritDoc} */ 3796 @Override 3797 public void monitor() { 3798 if (mConnector != null) { 3799 mConnector.monitor(); 3800 } 3801 if (mCryptConnector != null) { 3802 mCryptConnector.monitor(); 3803 } 3804 } 3805 3806 private final class MountServiceInternalImpl extends MountServiceInternal { 3807 // Not guarded by a lock. 3808 private final CopyOnWriteArrayList<ExternalStorageMountPolicy> mPolicies = 3809 new CopyOnWriteArrayList<>(); 3810 3811 @Override 3812 public void addExternalStoragePolicy(ExternalStorageMountPolicy policy) { 3813 // No locking - CopyOnWriteArrayList 3814 mPolicies.add(policy); 3815 } 3816 3817 @Override 3818 public void onExternalStoragePolicyChanged(int uid, String packageName) { 3819 final int mountMode = getExternalStorageMountMode(uid, packageName); 3820 remountUidExternalStorage(uid, mountMode); 3821 } 3822 3823 @Override 3824 public int getExternalStorageMountMode(int uid, String packageName) { 3825 // No locking - CopyOnWriteArrayList 3826 int mountMode = Integer.MAX_VALUE; 3827 for (ExternalStorageMountPolicy policy : mPolicies) { 3828 final int policyMode = policy.getMountMode(uid, packageName); 3829 if (policyMode == Zygote.MOUNT_EXTERNAL_NONE) { 3830 return Zygote.MOUNT_EXTERNAL_NONE; 3831 } 3832 mountMode = Math.min(mountMode, policyMode); 3833 } 3834 if (mountMode == Integer.MAX_VALUE) { 3835 return Zygote.MOUNT_EXTERNAL_NONE; 3836 } 3837 return mountMode; 3838 } 3839 3840 public boolean hasExternalStorage(int uid, String packageName) { 3841 // No need to check for system uid. This avoids a deadlock between 3842 // PackageManagerService and AppOpsService. 3843 if (uid == Process.SYSTEM_UID) { 3844 return true; 3845 } 3846 // No locking - CopyOnWriteArrayList 3847 for (ExternalStorageMountPolicy policy : mPolicies) { 3848 final boolean policyHasStorage = policy.hasExternalStorage(uid, packageName); 3849 if (!policyHasStorage) { 3850 return false; 3851 } 3852 } 3853 return true; 3854 } 3855 } 3856} 3857