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