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