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