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 if (type == StorageManager.CRYPT_TYPE_DEFAULT) { 2419 mCryptConnector.execute("cryptfs", "enablecrypto", "inplace", 2420 CRYPTO_TYPES[type]); 2421 } else { 2422 mCryptConnector.execute("cryptfs", "enablecrypto", "inplace", 2423 CRYPTO_TYPES[type], new SensitiveArg(password)); 2424 } 2425 } catch (NativeDaemonConnectorException e) { 2426 // Encryption failed 2427 return e.getCode(); 2428 } 2429 2430 return 0; 2431 } 2432 2433 /** Set the password for encrypting the master key. 2434 * @param type One of the CRYPTO_TYPE_XXX consts defined in StorageManager. 2435 * @param password The password to set. 2436 */ 2437 public int changeEncryptionPassword(int type, String password) { 2438 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, 2439 "no permission to access the crypt keeper"); 2440 2441 waitForReady(); 2442 2443 if (DEBUG_EVENTS) { 2444 Slog.i(TAG, "changing encryption password..."); 2445 } 2446 2447 try { 2448 NativeDaemonEvent event = mCryptConnector.execute("cryptfs", "changepw", CRYPTO_TYPES[type], 2449 new SensitiveArg(password)); 2450 return Integer.parseInt(event.getMessage()); 2451 } catch (NativeDaemonConnectorException e) { 2452 // Encryption failed 2453 return e.getCode(); 2454 } 2455 } 2456 2457 /** 2458 * Validate a user-supplied password string with cryptfs 2459 */ 2460 @Override 2461 public int verifyEncryptionPassword(String password) throws RemoteException { 2462 // Only the system process is permitted to validate passwords 2463 if (Binder.getCallingUid() != android.os.Process.SYSTEM_UID) { 2464 throw new SecurityException("no permission to access the crypt keeper"); 2465 } 2466 2467 mContext.enforceCallingOrSelfPermission(Manifest.permission.CRYPT_KEEPER, 2468 "no permission to access the crypt keeper"); 2469 2470 if (TextUtils.isEmpty(password)) { 2471 throw new IllegalArgumentException("password cannot be empty"); 2472 } 2473 2474 waitForReady(); 2475 2476 if (DEBUG_EVENTS) { 2477 Slog.i(TAG, "validating encryption password..."); 2478 } 2479 2480 final NativeDaemonEvent event; 2481 try { 2482 event = mCryptConnector.execute("cryptfs", "verifypw", new SensitiveArg(password)); 2483 Slog.i(TAG, "cryptfs verifypw => " + event.getMessage()); 2484 return Integer.parseInt(event.getMessage()); 2485 } catch (NativeDaemonConnectorException e) { 2486 // Encryption failed 2487 return e.getCode(); 2488 } 2489 } 2490 2491 /** 2492 * Get the type of encryption used to encrypt the master key. 2493 * @return The type, one of the CRYPT_TYPE_XXX consts from StorageManager. 2494 */ 2495 @Override 2496 public int getPasswordType() { 2497 2498 waitForReady(); 2499 2500 final NativeDaemonEvent event; 2501 try { 2502 event = mCryptConnector.execute("cryptfs", "getpwtype"); 2503 for (int i = 0; i < CRYPTO_TYPES.length; ++i) { 2504 if (CRYPTO_TYPES[i].equals(event.getMessage())) 2505 return i; 2506 } 2507 2508 throw new IllegalStateException("unexpected return from cryptfs"); 2509 } catch (NativeDaemonConnectorException e) { 2510 throw e.rethrowAsParcelableException(); 2511 } 2512 } 2513 2514 /** 2515 * Set a field in the crypto header. 2516 * @param field field to set 2517 * @param contents contents to set in field 2518 */ 2519 @Override 2520 public void setField(String field, String contents) throws RemoteException { 2521 2522 waitForReady(); 2523 2524 final NativeDaemonEvent event; 2525 try { 2526 event = mCryptConnector.execute("cryptfs", "setfield", field, contents); 2527 } catch (NativeDaemonConnectorException e) { 2528 throw e.rethrowAsParcelableException(); 2529 } 2530 } 2531 2532 /** 2533 * Gets a field from the crypto header. 2534 * @param field field to get 2535 * @return contents of field 2536 */ 2537 @Override 2538 public String getField(String field) throws RemoteException { 2539 2540 waitForReady(); 2541 2542 final NativeDaemonEvent event; 2543 try { 2544 final String[] contents = NativeDaemonEvent.filterMessageList( 2545 mCryptConnector.executeForList("cryptfs", "getfield", field), 2546 VoldResponseCode.CryptfsGetfieldResult); 2547 String result = new String(); 2548 for (String content : contents) { 2549 result += content; 2550 } 2551 return result; 2552 } catch (NativeDaemonConnectorException e) { 2553 throw e.rethrowAsParcelableException(); 2554 } 2555 } 2556 2557 @Override 2558 public String getPassword() throws RemoteException { 2559 mContext.enforceCallingOrSelfPermission(Manifest.permission.ACCESS_KEYGUARD_SECURE_STORAGE, 2560 "only keyguard can retrieve password"); 2561 if (!isReady()) { 2562 return new String(); 2563 } 2564 2565 final NativeDaemonEvent event; 2566 try { 2567 event = mCryptConnector.execute("cryptfs", "getpw"); 2568 if ("-1".equals(event.getMessage())) { 2569 // -1 equals no password 2570 return null; 2571 } 2572 return event.getMessage(); 2573 } catch (NativeDaemonConnectorException e) { 2574 throw e.rethrowAsParcelableException(); 2575 } catch (IllegalArgumentException e) { 2576 Slog.e(TAG, "Invalid response to getPassword"); 2577 return null; 2578 } 2579 } 2580 2581 @Override 2582 public void clearPassword() throws RemoteException { 2583 if (!isReady()) { 2584 return; 2585 } 2586 2587 final NativeDaemonEvent event; 2588 try { 2589 event = mCryptConnector.execute("cryptfs", "clearpw"); 2590 } catch (NativeDaemonConnectorException e) { 2591 throw e.rethrowAsParcelableException(); 2592 } 2593 } 2594 2595 @Override 2596 public void createNewUserDir(int userHandle, String path) { 2597 if (Binder.getCallingUid() != Process.SYSTEM_UID) { 2598 throw new SecurityException("Only SYSTEM_UID can create user directories"); 2599 } 2600 2601 waitForReady(); 2602 2603 if (DEBUG_EVENTS) { 2604 Slog.i(TAG, "Creating new user dir"); 2605 } 2606 2607 try { 2608 NativeDaemonEvent event = mCryptConnector.execute( 2609 "cryptfs", "createnewuserdir", userHandle, path); 2610 if (!"0".equals(event.getMessage())) { 2611 String error = "createnewuserdir sent unexpected message: " 2612 + event.getMessage(); 2613 Slog.e(TAG, error); 2614 // ext4enc:TODO is this the right exception? 2615 throw new RuntimeException(error); 2616 } 2617 } catch (NativeDaemonConnectorException e) { 2618 Slog.e(TAG, "createnewuserdir threw exception", e); 2619 throw new RuntimeException("createnewuserdir threw exception", e); 2620 } 2621 } 2622 2623 // ext4enc:TODO duplication between this and createNewUserDir is nasty 2624 @Override 2625 public void deleteUserKey(int userHandle) { 2626 if (Binder.getCallingUid() != Process.SYSTEM_UID) { 2627 throw new SecurityException("Only SYSTEM_UID can delete user keys"); 2628 } 2629 2630 waitForReady(); 2631 2632 if (DEBUG_EVENTS) { 2633 Slog.i(TAG, "Deleting user key"); 2634 } 2635 2636 try { 2637 NativeDaemonEvent event = mCryptConnector.execute( 2638 "cryptfs", "deleteuserkey", userHandle); 2639 if (!"0".equals(event.getMessage())) { 2640 String error = "deleteuserkey sent unexpected message: " 2641 + event.getMessage(); 2642 Slog.e(TAG, error); 2643 // ext4enc:TODO is this the right exception? 2644 throw new RuntimeException(error); 2645 } 2646 } catch (NativeDaemonConnectorException e) { 2647 Slog.e(TAG, "deleteuserkey threw exception", e); 2648 throw new RuntimeException("deleteuserkey threw exception", e); 2649 } 2650 } 2651 2652 @Override 2653 public int mkdirs(String callingPkg, String appPath) { 2654 final int userId = UserHandle.getUserId(Binder.getCallingUid()); 2655 final UserEnvironment userEnv = new UserEnvironment(userId); 2656 2657 // Validate that reported package name belongs to caller 2658 final AppOpsManager appOps = (AppOpsManager) mContext.getSystemService( 2659 Context.APP_OPS_SERVICE); 2660 appOps.checkPackage(Binder.getCallingUid(), callingPkg); 2661 2662 File appFile = null; 2663 try { 2664 appFile = new File(appPath).getCanonicalFile(); 2665 } catch (IOException e) { 2666 Slog.e(TAG, "Failed to resolve " + appPath + ": " + e); 2667 return -1; 2668 } 2669 2670 // Try translating the app path into a vold path, but require that it 2671 // belong to the calling package. 2672 if (FileUtils.contains(userEnv.buildExternalStorageAppDataDirs(callingPkg), appFile) || 2673 FileUtils.contains(userEnv.buildExternalStorageAppObbDirs(callingPkg), appFile) || 2674 FileUtils.contains(userEnv.buildExternalStorageAppMediaDirs(callingPkg), appFile)) { 2675 appPath = appFile.getAbsolutePath(); 2676 if (!appPath.endsWith("/")) { 2677 appPath = appPath + "/"; 2678 } 2679 2680 try { 2681 mConnector.execute("volume", "mkdirs", appPath); 2682 return 0; 2683 } catch (NativeDaemonConnectorException e) { 2684 return e.getCode(); 2685 } 2686 } 2687 2688 throw new SecurityException("Invalid mkdirs path: " + appFile); 2689 } 2690 2691 @Override 2692 public StorageVolume[] getVolumeList(int uid, String packageName, int flags) { 2693 final boolean forWrite = (flags & StorageManager.FLAG_FOR_WRITE) != 0; 2694 2695 final ArrayList<StorageVolume> res = new ArrayList<>(); 2696 boolean foundPrimary = false; 2697 2698 final int userId = UserHandle.getUserId(uid); 2699 final boolean reportUnmounted; 2700 final long identity = Binder.clearCallingIdentity(); 2701 try { 2702 reportUnmounted = !mMountServiceInternal.hasExternalStorage( 2703 uid, packageName); 2704 } finally { 2705 Binder.restoreCallingIdentity(identity); 2706 } 2707 2708 synchronized (mLock) { 2709 for (int i = 0; i < mVolumes.size(); i++) { 2710 final VolumeInfo vol = mVolumes.valueAt(i); 2711 if (forWrite ? vol.isVisibleForWrite(userId) : vol.isVisibleForRead(userId)) { 2712 final StorageVolume userVol = vol.buildStorageVolume(mContext, userId, 2713 reportUnmounted); 2714 if (vol.isPrimary()) { 2715 res.add(0, userVol); 2716 foundPrimary = true; 2717 } else { 2718 res.add(userVol); 2719 } 2720 } 2721 } 2722 } 2723 2724 if (!foundPrimary) { 2725 Log.w(TAG, "No primary storage defined yet; hacking together a stub"); 2726 2727 final boolean primaryPhysical = SystemProperties.getBoolean( 2728 StorageManager.PROP_PRIMARY_PHYSICAL, false); 2729 2730 final String id = "stub_primary"; 2731 final File path = Environment.getLegacyExternalStorageDirectory(); 2732 final String description = mContext.getString(android.R.string.unknownName); 2733 final boolean primary = true; 2734 final boolean removable = primaryPhysical; 2735 final boolean emulated = !primaryPhysical; 2736 final long mtpReserveSize = 0L; 2737 final boolean allowMassStorage = false; 2738 final long maxFileSize = 0L; 2739 final UserHandle owner = new UserHandle(userId); 2740 final String uuid = null; 2741 final String state = Environment.MEDIA_REMOVED; 2742 2743 res.add(0, new StorageVolume(id, StorageVolume.STORAGE_ID_INVALID, path, 2744 description, primary, removable, emulated, mtpReserveSize, 2745 allowMassStorage, maxFileSize, owner, uuid, state)); 2746 } 2747 2748 return res.toArray(new StorageVolume[res.size()]); 2749 } 2750 2751 @Override 2752 public DiskInfo[] getDisks() { 2753 synchronized (mLock) { 2754 final DiskInfo[] res = new DiskInfo[mDisks.size()]; 2755 for (int i = 0; i < mDisks.size(); i++) { 2756 res[i] = mDisks.valueAt(i); 2757 } 2758 return res; 2759 } 2760 } 2761 2762 @Override 2763 public VolumeInfo[] getVolumes(int flags) { 2764 synchronized (mLock) { 2765 final VolumeInfo[] res = new VolumeInfo[mVolumes.size()]; 2766 for (int i = 0; i < mVolumes.size(); i++) { 2767 res[i] = mVolumes.valueAt(i); 2768 } 2769 return res; 2770 } 2771 } 2772 2773 @Override 2774 public VolumeRecord[] getVolumeRecords(int flags) { 2775 synchronized (mLock) { 2776 final VolumeRecord[] res = new VolumeRecord[mRecords.size()]; 2777 for (int i = 0; i < mRecords.size(); i++) { 2778 res[i] = mRecords.valueAt(i); 2779 } 2780 return res; 2781 } 2782 } 2783 2784 private void addObbStateLocked(ObbState obbState) throws RemoteException { 2785 final IBinder binder = obbState.getBinder(); 2786 List<ObbState> obbStates = mObbMounts.get(binder); 2787 2788 if (obbStates == null) { 2789 obbStates = new ArrayList<ObbState>(); 2790 mObbMounts.put(binder, obbStates); 2791 } else { 2792 for (final ObbState o : obbStates) { 2793 if (o.rawPath.equals(obbState.rawPath)) { 2794 throw new IllegalStateException("Attempt to add ObbState twice. " 2795 + "This indicates an error in the MountService logic."); 2796 } 2797 } 2798 } 2799 2800 obbStates.add(obbState); 2801 try { 2802 obbState.link(); 2803 } catch (RemoteException e) { 2804 /* 2805 * The binder died before we could link it, so clean up our state 2806 * and return failure. 2807 */ 2808 obbStates.remove(obbState); 2809 if (obbStates.isEmpty()) { 2810 mObbMounts.remove(binder); 2811 } 2812 2813 // Rethrow the error so mountObb can get it 2814 throw e; 2815 } 2816 2817 mObbPathToStateMap.put(obbState.rawPath, obbState); 2818 } 2819 2820 private void removeObbStateLocked(ObbState obbState) { 2821 final IBinder binder = obbState.getBinder(); 2822 final List<ObbState> obbStates = mObbMounts.get(binder); 2823 if (obbStates != null) { 2824 if (obbStates.remove(obbState)) { 2825 obbState.unlink(); 2826 } 2827 if (obbStates.isEmpty()) { 2828 mObbMounts.remove(binder); 2829 } 2830 } 2831 2832 mObbPathToStateMap.remove(obbState.rawPath); 2833 } 2834 2835 private class ObbActionHandler extends Handler { 2836 private boolean mBound = false; 2837 private final List<ObbAction> mActions = new LinkedList<ObbAction>(); 2838 2839 ObbActionHandler(Looper l) { 2840 super(l); 2841 } 2842 2843 @Override 2844 public void handleMessage(Message msg) { 2845 switch (msg.what) { 2846 case OBB_RUN_ACTION: { 2847 final ObbAction action = (ObbAction) msg.obj; 2848 2849 if (DEBUG_OBB) 2850 Slog.i(TAG, "OBB_RUN_ACTION: " + action.toString()); 2851 2852 // If a bind was already initiated we don't really 2853 // need to do anything. The pending install 2854 // will be processed later on. 2855 if (!mBound) { 2856 // If this is the only one pending we might 2857 // have to bind to the service again. 2858 if (!connectToService()) { 2859 Slog.e(TAG, "Failed to bind to media container service"); 2860 action.handleError(); 2861 return; 2862 } 2863 } 2864 2865 mActions.add(action); 2866 break; 2867 } 2868 case OBB_MCS_BOUND: { 2869 if (DEBUG_OBB) 2870 Slog.i(TAG, "OBB_MCS_BOUND"); 2871 if (msg.obj != null) { 2872 mContainerService = (IMediaContainerService) msg.obj; 2873 } 2874 if (mContainerService == null) { 2875 // Something seriously wrong. Bail out 2876 Slog.e(TAG, "Cannot bind to media container service"); 2877 for (ObbAction action : mActions) { 2878 // Indicate service bind error 2879 action.handleError(); 2880 } 2881 mActions.clear(); 2882 } else if (mActions.size() > 0) { 2883 final ObbAction action = mActions.get(0); 2884 if (action != null) { 2885 action.execute(this); 2886 } 2887 } else { 2888 // Should never happen ideally. 2889 Slog.w(TAG, "Empty queue"); 2890 } 2891 break; 2892 } 2893 case OBB_MCS_RECONNECT: { 2894 if (DEBUG_OBB) 2895 Slog.i(TAG, "OBB_MCS_RECONNECT"); 2896 if (mActions.size() > 0) { 2897 if (mBound) { 2898 disconnectService(); 2899 } 2900 if (!connectToService()) { 2901 Slog.e(TAG, "Failed to bind to media container service"); 2902 for (ObbAction action : mActions) { 2903 // Indicate service bind error 2904 action.handleError(); 2905 } 2906 mActions.clear(); 2907 } 2908 } 2909 break; 2910 } 2911 case OBB_MCS_UNBIND: { 2912 if (DEBUG_OBB) 2913 Slog.i(TAG, "OBB_MCS_UNBIND"); 2914 2915 // Delete pending install 2916 if (mActions.size() > 0) { 2917 mActions.remove(0); 2918 } 2919 if (mActions.size() == 0) { 2920 if (mBound) { 2921 disconnectService(); 2922 } 2923 } else { 2924 // There are more pending requests in queue. 2925 // Just post MCS_BOUND message to trigger processing 2926 // of next pending install. 2927 mObbActionHandler.sendEmptyMessage(OBB_MCS_BOUND); 2928 } 2929 break; 2930 } 2931 case OBB_FLUSH_MOUNT_STATE: { 2932 final String path = (String) msg.obj; 2933 2934 if (DEBUG_OBB) 2935 Slog.i(TAG, "Flushing all OBB state for path " + path); 2936 2937 synchronized (mObbMounts) { 2938 final List<ObbState> obbStatesToRemove = new LinkedList<ObbState>(); 2939 2940 final Iterator<ObbState> i = mObbPathToStateMap.values().iterator(); 2941 while (i.hasNext()) { 2942 final ObbState state = i.next(); 2943 2944 /* 2945 * If this entry's source file is in the volume path 2946 * that got unmounted, remove it because it's no 2947 * longer valid. 2948 */ 2949 if (state.canonicalPath.startsWith(path)) { 2950 obbStatesToRemove.add(state); 2951 } 2952 } 2953 2954 for (final ObbState obbState : obbStatesToRemove) { 2955 if (DEBUG_OBB) 2956 Slog.i(TAG, "Removing state for " + obbState.rawPath); 2957 2958 removeObbStateLocked(obbState); 2959 2960 try { 2961 obbState.token.onObbResult(obbState.rawPath, obbState.nonce, 2962 OnObbStateChangeListener.UNMOUNTED); 2963 } catch (RemoteException e) { 2964 Slog.i(TAG, "Couldn't send unmount notification for OBB: " 2965 + obbState.rawPath); 2966 } 2967 } 2968 } 2969 break; 2970 } 2971 } 2972 } 2973 2974 private boolean connectToService() { 2975 if (DEBUG_OBB) 2976 Slog.i(TAG, "Trying to bind to DefaultContainerService"); 2977 2978 Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT); 2979 if (mContext.bindServiceAsUser(service, mDefContainerConn, Context.BIND_AUTO_CREATE, 2980 UserHandle.OWNER)) { 2981 mBound = true; 2982 return true; 2983 } 2984 return false; 2985 } 2986 2987 private void disconnectService() { 2988 mContainerService = null; 2989 mBound = false; 2990 mContext.unbindService(mDefContainerConn); 2991 } 2992 } 2993 2994 abstract class ObbAction { 2995 private static final int MAX_RETRIES = 3; 2996 private int mRetries; 2997 2998 ObbState mObbState; 2999 3000 ObbAction(ObbState obbState) { 3001 mObbState = obbState; 3002 } 3003 3004 public void execute(ObbActionHandler handler) { 3005 try { 3006 if (DEBUG_OBB) 3007 Slog.i(TAG, "Starting to execute action: " + toString()); 3008 mRetries++; 3009 if (mRetries > MAX_RETRIES) { 3010 Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up"); 3011 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND); 3012 handleError(); 3013 return; 3014 } else { 3015 handleExecute(); 3016 if (DEBUG_OBB) 3017 Slog.i(TAG, "Posting install MCS_UNBIND"); 3018 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND); 3019 } 3020 } catch (RemoteException e) { 3021 if (DEBUG_OBB) 3022 Slog.i(TAG, "Posting install MCS_RECONNECT"); 3023 mObbActionHandler.sendEmptyMessage(OBB_MCS_RECONNECT); 3024 } catch (Exception e) { 3025 if (DEBUG_OBB) 3026 Slog.d(TAG, "Error handling OBB action", e); 3027 handleError(); 3028 mObbActionHandler.sendEmptyMessage(OBB_MCS_UNBIND); 3029 } 3030 } 3031 3032 abstract void handleExecute() throws RemoteException, IOException; 3033 abstract void handleError(); 3034 3035 protected ObbInfo getObbInfo() throws IOException { 3036 ObbInfo obbInfo; 3037 try { 3038 obbInfo = mContainerService.getObbInfo(mObbState.ownerPath); 3039 } catch (RemoteException e) { 3040 Slog.d(TAG, "Couldn't call DefaultContainerService to fetch OBB info for " 3041 + mObbState.ownerPath); 3042 obbInfo = null; 3043 } 3044 if (obbInfo == null) { 3045 throw new IOException("Couldn't read OBB file: " + mObbState.ownerPath); 3046 } 3047 return obbInfo; 3048 } 3049 3050 protected void sendNewStatusOrIgnore(int status) { 3051 if (mObbState == null || mObbState.token == null) { 3052 return; 3053 } 3054 3055 try { 3056 mObbState.token.onObbResult(mObbState.rawPath, mObbState.nonce, status); 3057 } catch (RemoteException e) { 3058 Slog.w(TAG, "MountServiceListener went away while calling onObbStateChanged"); 3059 } 3060 } 3061 } 3062 3063 class MountObbAction extends ObbAction { 3064 private final String mKey; 3065 private final int mCallingUid; 3066 3067 MountObbAction(ObbState obbState, String key, int callingUid) { 3068 super(obbState); 3069 mKey = key; 3070 mCallingUid = callingUid; 3071 } 3072 3073 @Override 3074 public void handleExecute() throws IOException, RemoteException { 3075 waitForReady(); 3076 warnOnNotMounted(); 3077 3078 final ObbInfo obbInfo = getObbInfo(); 3079 3080 if (!isUidOwnerOfPackageOrSystem(obbInfo.packageName, mCallingUid)) { 3081 Slog.w(TAG, "Denied attempt to mount OBB " + obbInfo.filename 3082 + " which is owned by " + obbInfo.packageName); 3083 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED); 3084 return; 3085 } 3086 3087 final boolean isMounted; 3088 synchronized (mObbMounts) { 3089 isMounted = mObbPathToStateMap.containsKey(mObbState.rawPath); 3090 } 3091 if (isMounted) { 3092 Slog.w(TAG, "Attempt to mount OBB which is already mounted: " + obbInfo.filename); 3093 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_ALREADY_MOUNTED); 3094 return; 3095 } 3096 3097 final String hashedKey; 3098 if (mKey == null) { 3099 hashedKey = "none"; 3100 } else { 3101 try { 3102 SecretKeyFactory factory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1"); 3103 3104 KeySpec ks = new PBEKeySpec(mKey.toCharArray(), obbInfo.salt, 3105 PBKDF2_HASH_ROUNDS, CRYPTO_ALGORITHM_KEY_SIZE); 3106 SecretKey key = factory.generateSecret(ks); 3107 BigInteger bi = new BigInteger(key.getEncoded()); 3108 hashedKey = bi.toString(16); 3109 } catch (NoSuchAlgorithmException e) { 3110 Slog.e(TAG, "Could not load PBKDF2 algorithm", e); 3111 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL); 3112 return; 3113 } catch (InvalidKeySpecException e) { 3114 Slog.e(TAG, "Invalid key spec when loading PBKDF2 algorithm", e); 3115 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL); 3116 return; 3117 } 3118 } 3119 3120 int rc = StorageResultCode.OperationSucceeded; 3121 try { 3122 mConnector.execute("obb", "mount", mObbState.voldPath, new SensitiveArg(hashedKey), 3123 mObbState.ownerGid); 3124 } catch (NativeDaemonConnectorException e) { 3125 int code = e.getCode(); 3126 if (code != VoldResponseCode.OpFailedStorageBusy) { 3127 rc = StorageResultCode.OperationFailedInternalError; 3128 } 3129 } 3130 3131 if (rc == StorageResultCode.OperationSucceeded) { 3132 if (DEBUG_OBB) 3133 Slog.d(TAG, "Successfully mounted OBB " + mObbState.voldPath); 3134 3135 synchronized (mObbMounts) { 3136 addObbStateLocked(mObbState); 3137 } 3138 3139 sendNewStatusOrIgnore(OnObbStateChangeListener.MOUNTED); 3140 } else { 3141 Slog.e(TAG, "Couldn't mount OBB file: " + rc); 3142 3143 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_MOUNT); 3144 } 3145 } 3146 3147 @Override 3148 public void handleError() { 3149 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL); 3150 } 3151 3152 @Override 3153 public String toString() { 3154 StringBuilder sb = new StringBuilder(); 3155 sb.append("MountObbAction{"); 3156 sb.append(mObbState); 3157 sb.append('}'); 3158 return sb.toString(); 3159 } 3160 } 3161 3162 class UnmountObbAction extends ObbAction { 3163 private final boolean mForceUnmount; 3164 3165 UnmountObbAction(ObbState obbState, boolean force) { 3166 super(obbState); 3167 mForceUnmount = force; 3168 } 3169 3170 @Override 3171 public void handleExecute() throws IOException { 3172 waitForReady(); 3173 warnOnNotMounted(); 3174 3175 final ObbInfo obbInfo = getObbInfo(); 3176 3177 final ObbState existingState; 3178 synchronized (mObbMounts) { 3179 existingState = mObbPathToStateMap.get(mObbState.rawPath); 3180 } 3181 3182 if (existingState == null) { 3183 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_NOT_MOUNTED); 3184 return; 3185 } 3186 3187 if (existingState.ownerGid != mObbState.ownerGid) { 3188 Slog.w(TAG, "Permission denied attempting to unmount OBB " + existingState.rawPath 3189 + " (owned by GID " + existingState.ownerGid + ")"); 3190 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_PERMISSION_DENIED); 3191 return; 3192 } 3193 3194 int rc = StorageResultCode.OperationSucceeded; 3195 try { 3196 final Command cmd = new Command("obb", "unmount", mObbState.voldPath); 3197 if (mForceUnmount) { 3198 cmd.appendArg("force"); 3199 } 3200 mConnector.execute(cmd); 3201 } catch (NativeDaemonConnectorException e) { 3202 int code = e.getCode(); 3203 if (code == VoldResponseCode.OpFailedStorageBusy) { 3204 rc = StorageResultCode.OperationFailedStorageBusy; 3205 } else if (code == VoldResponseCode.OpFailedStorageNotFound) { 3206 // If it's not mounted then we've already won. 3207 rc = StorageResultCode.OperationSucceeded; 3208 } else { 3209 rc = StorageResultCode.OperationFailedInternalError; 3210 } 3211 } 3212 3213 if (rc == StorageResultCode.OperationSucceeded) { 3214 synchronized (mObbMounts) { 3215 removeObbStateLocked(existingState); 3216 } 3217 3218 sendNewStatusOrIgnore(OnObbStateChangeListener.UNMOUNTED); 3219 } else { 3220 Slog.w(TAG, "Could not unmount OBB: " + existingState); 3221 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_COULD_NOT_UNMOUNT); 3222 } 3223 } 3224 3225 @Override 3226 public void handleError() { 3227 sendNewStatusOrIgnore(OnObbStateChangeListener.ERROR_INTERNAL); 3228 } 3229 3230 @Override 3231 public String toString() { 3232 StringBuilder sb = new StringBuilder(); 3233 sb.append("UnmountObbAction{"); 3234 sb.append(mObbState); 3235 sb.append(",force="); 3236 sb.append(mForceUnmount); 3237 sb.append('}'); 3238 return sb.toString(); 3239 } 3240 } 3241 3242 @VisibleForTesting 3243 public static String buildObbPath(final String canonicalPath, int userId, boolean forVold) { 3244 // TODO: allow caller to provide Environment for full testing 3245 // TODO: extend to support OBB mounts on secondary external storage 3246 3247 // Only adjust paths when storage is emulated 3248 if (!Environment.isExternalStorageEmulated()) { 3249 return canonicalPath; 3250 } 3251 3252 String path = canonicalPath.toString(); 3253 3254 // First trim off any external storage prefix 3255 final UserEnvironment userEnv = new UserEnvironment(userId); 3256 3257 // /storage/emulated/0 3258 final String externalPath = userEnv.getExternalStorageDirectory().getAbsolutePath(); 3259 // /storage/emulated_legacy 3260 final String legacyExternalPath = Environment.getLegacyExternalStorageDirectory() 3261 .getAbsolutePath(); 3262 3263 if (path.startsWith(externalPath)) { 3264 path = path.substring(externalPath.length() + 1); 3265 } else if (path.startsWith(legacyExternalPath)) { 3266 path = path.substring(legacyExternalPath.length() + 1); 3267 } else { 3268 return canonicalPath; 3269 } 3270 3271 // Handle special OBB paths on emulated storage 3272 final String obbPath = "Android/obb"; 3273 if (path.startsWith(obbPath)) { 3274 path = path.substring(obbPath.length() + 1); 3275 3276 final UserEnvironment ownerEnv = new UserEnvironment(UserHandle.USER_OWNER); 3277 return new File(ownerEnv.buildExternalStorageAndroidObbDirs()[0], path) 3278 .getAbsolutePath(); 3279 } 3280 3281 // Handle normal external storage paths 3282 return new File(userEnv.getExternalStorageDirectory(), path).getAbsolutePath(); 3283 } 3284 3285 private static class Callbacks extends Handler { 3286 private static final int MSG_STORAGE_STATE_CHANGED = 1; 3287 private static final int MSG_VOLUME_STATE_CHANGED = 2; 3288 private static final int MSG_VOLUME_RECORD_CHANGED = 3; 3289 private static final int MSG_VOLUME_FORGOTTEN = 4; 3290 private static final int MSG_DISK_SCANNED = 5; 3291 private static final int MSG_DISK_DESTROYED = 6; 3292 3293 private final RemoteCallbackList<IMountServiceListener> 3294 mCallbacks = new RemoteCallbackList<>(); 3295 3296 public Callbacks(Looper looper) { 3297 super(looper); 3298 } 3299 3300 public void register(IMountServiceListener callback) { 3301 mCallbacks.register(callback); 3302 } 3303 3304 public void unregister(IMountServiceListener callback) { 3305 mCallbacks.unregister(callback); 3306 } 3307 3308 @Override 3309 public void handleMessage(Message msg) { 3310 final SomeArgs args = (SomeArgs) msg.obj; 3311 final int n = mCallbacks.beginBroadcast(); 3312 for (int i = 0; i < n; i++) { 3313 final IMountServiceListener callback = mCallbacks.getBroadcastItem(i); 3314 try { 3315 invokeCallback(callback, msg.what, args); 3316 } catch (RemoteException ignored) { 3317 } 3318 } 3319 mCallbacks.finishBroadcast(); 3320 args.recycle(); 3321 } 3322 3323 private void invokeCallback(IMountServiceListener callback, int what, SomeArgs args) 3324 throws RemoteException { 3325 switch (what) { 3326 case MSG_STORAGE_STATE_CHANGED: { 3327 callback.onStorageStateChanged((String) args.arg1, (String) args.arg2, 3328 (String) args.arg3); 3329 break; 3330 } 3331 case MSG_VOLUME_STATE_CHANGED: { 3332 callback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3); 3333 break; 3334 } 3335 case MSG_VOLUME_RECORD_CHANGED: { 3336 callback.onVolumeRecordChanged((VolumeRecord) args.arg1); 3337 break; 3338 } 3339 case MSG_VOLUME_FORGOTTEN: { 3340 callback.onVolumeForgotten((String) args.arg1); 3341 break; 3342 } 3343 case MSG_DISK_SCANNED: { 3344 callback.onDiskScanned((DiskInfo) args.arg1, args.argi2); 3345 break; 3346 } 3347 case MSG_DISK_DESTROYED: { 3348 callback.onDiskDestroyed((DiskInfo) args.arg1); 3349 break; 3350 } 3351 } 3352 } 3353 3354 private void notifyStorageStateChanged(String path, String oldState, String newState) { 3355 final SomeArgs args = SomeArgs.obtain(); 3356 args.arg1 = path; 3357 args.arg2 = oldState; 3358 args.arg3 = newState; 3359 obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget(); 3360 } 3361 3362 private void notifyVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { 3363 final SomeArgs args = SomeArgs.obtain(); 3364 args.arg1 = vol.clone(); 3365 args.argi2 = oldState; 3366 args.argi3 = newState; 3367 obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget(); 3368 } 3369 3370 private void notifyVolumeRecordChanged(VolumeRecord rec) { 3371 final SomeArgs args = SomeArgs.obtain(); 3372 args.arg1 = rec.clone(); 3373 obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget(); 3374 } 3375 3376 private void notifyVolumeForgotten(String fsUuid) { 3377 final SomeArgs args = SomeArgs.obtain(); 3378 args.arg1 = fsUuid; 3379 obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget(); 3380 } 3381 3382 private void notifyDiskScanned(DiskInfo disk, int volumeCount) { 3383 final SomeArgs args = SomeArgs.obtain(); 3384 args.arg1 = disk.clone(); 3385 args.argi2 = volumeCount; 3386 obtainMessage(MSG_DISK_SCANNED, args).sendToTarget(); 3387 } 3388 3389 private void notifyDiskDestroyed(DiskInfo disk) { 3390 final SomeArgs args = SomeArgs.obtain(); 3391 args.arg1 = disk.clone(); 3392 obtainMessage(MSG_DISK_DESTROYED, args).sendToTarget(); 3393 } 3394 } 3395 3396 @Override 3397 protected void dump(FileDescriptor fd, PrintWriter writer, String[] args) { 3398 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); 3399 3400 final IndentingPrintWriter pw = new IndentingPrintWriter(writer, " ", 160); 3401 synchronized (mLock) { 3402 pw.println("Disks:"); 3403 pw.increaseIndent(); 3404 for (int i = 0; i < mDisks.size(); i++) { 3405 final DiskInfo disk = mDisks.valueAt(i); 3406 disk.dump(pw); 3407 } 3408 pw.decreaseIndent(); 3409 3410 pw.println(); 3411 pw.println("Volumes:"); 3412 pw.increaseIndent(); 3413 for (int i = 0; i < mVolumes.size(); i++) { 3414 final VolumeInfo vol = mVolumes.valueAt(i); 3415 if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(vol.id)) continue; 3416 vol.dump(pw); 3417 } 3418 pw.decreaseIndent(); 3419 3420 pw.println(); 3421 pw.println("Records:"); 3422 pw.increaseIndent(); 3423 for (int i = 0; i < mRecords.size(); i++) { 3424 final VolumeRecord note = mRecords.valueAt(i); 3425 note.dump(pw); 3426 } 3427 pw.decreaseIndent(); 3428 3429 pw.println(); 3430 pw.println("Primary storage UUID: " + mPrimaryStorageUuid); 3431 pw.println("Force adoptable: " + mForceAdoptable); 3432 } 3433 3434 synchronized (mObbMounts) { 3435 pw.println(); 3436 pw.println("mObbMounts:"); 3437 pw.increaseIndent(); 3438 final Iterator<Entry<IBinder, List<ObbState>>> binders = mObbMounts.entrySet() 3439 .iterator(); 3440 while (binders.hasNext()) { 3441 Entry<IBinder, List<ObbState>> e = binders.next(); 3442 pw.println(e.getKey() + ":"); 3443 pw.increaseIndent(); 3444 final List<ObbState> obbStates = e.getValue(); 3445 for (final ObbState obbState : obbStates) { 3446 pw.println(obbState); 3447 } 3448 pw.decreaseIndent(); 3449 } 3450 pw.decreaseIndent(); 3451 3452 pw.println(); 3453 pw.println("mObbPathToStateMap:"); 3454 pw.increaseIndent(); 3455 final Iterator<Entry<String, ObbState>> maps = mObbPathToStateMap.entrySet().iterator(); 3456 while (maps.hasNext()) { 3457 final Entry<String, ObbState> e = maps.next(); 3458 pw.print(e.getKey()); 3459 pw.print(" -> "); 3460 pw.println(e.getValue()); 3461 } 3462 pw.decreaseIndent(); 3463 } 3464 3465 pw.println(); 3466 pw.println("mConnection:"); 3467 pw.increaseIndent(); 3468 mConnector.dump(fd, pw, args); 3469 pw.decreaseIndent(); 3470 3471 pw.println(); 3472 pw.print("Last maintenance: "); 3473 pw.println(TimeUtils.formatForLogging(mLastMaintenance)); 3474 } 3475 3476 /** {@inheritDoc} */ 3477 @Override 3478 public void monitor() { 3479 if (mConnector != null) { 3480 mConnector.monitor(); 3481 } 3482 if (mCryptConnector != null) { 3483 mCryptConnector.monitor(); 3484 } 3485 } 3486 3487 private final class MountServiceInternalImpl extends MountServiceInternal { 3488 // Not guarded by a lock. 3489 private final CopyOnWriteArrayList<ExternalStorageMountPolicy> mPolicies = 3490 new CopyOnWriteArrayList<>(); 3491 3492 @Override 3493 public void addExternalStoragePolicy(ExternalStorageMountPolicy policy) { 3494 // No locking - CopyOnWriteArrayList 3495 mPolicies.add(policy); 3496 } 3497 3498 @Override 3499 public void onExternalStoragePolicyChanged(int uid, String packageName) { 3500 final int mountMode = getExternalStorageMountMode(uid, packageName); 3501 remountUidExternalStorage(uid, mountMode); 3502 } 3503 3504 @Override 3505 public int getExternalStorageMountMode(int uid, String packageName) { 3506 // No locking - CopyOnWriteArrayList 3507 int mountMode = Integer.MAX_VALUE; 3508 for (ExternalStorageMountPolicy policy : mPolicies) { 3509 final int policyMode = policy.getMountMode(uid, packageName); 3510 if (policyMode == Zygote.MOUNT_EXTERNAL_NONE) { 3511 return Zygote.MOUNT_EXTERNAL_NONE; 3512 } 3513 mountMode = Math.min(mountMode, policyMode); 3514 } 3515 if (mountMode == Integer.MAX_VALUE) { 3516 return Zygote.MOUNT_EXTERNAL_NONE; 3517 } 3518 return mountMode; 3519 } 3520 3521 public boolean hasExternalStorage(int uid, String packageName) { 3522 // No need to check for system uid. This avoids a deadlock between 3523 // PackageManagerService and AppOpsService. 3524 if (uid == Process.SYSTEM_UID) { 3525 return true; 3526 } 3527 // No locking - CopyOnWriteArrayList 3528 for (ExternalStorageMountPolicy policy : mPolicies) { 3529 final boolean policyHasStorage = policy.hasExternalStorage(uid, packageName); 3530 if (!policyHasStorage) { 3531 return false; 3532 } 3533 } 3534 return true; 3535 } 3536 } 3537} 3538