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