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