StorageManager.java revision 9fb00183a04036a58ee208f5bfd6c9768982f0aa
1/* 2 * Copyright (C) 2008 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 android.os.storage; 18 19import static android.net.TrafficStats.MB_IN_BYTES; 20 21import android.annotation.NonNull; 22import android.annotation.Nullable; 23import android.annotation.SdkConstant; 24import android.app.ActivityThread; 25import android.content.ContentResolver; 26import android.content.Context; 27import android.content.pm.IPackageMoveObserver; 28import android.content.pm.PackageManager; 29import android.os.Binder; 30import android.os.Environment; 31import android.os.FileUtils; 32import android.os.Handler; 33import android.os.ProxyFileDescriptorCallback; 34import android.os.Looper; 35import android.os.Message; 36import android.os.ParcelFileDescriptor; 37import android.os.RemoteException; 38import android.os.ServiceManager; 39import android.os.ServiceManager.ServiceNotFoundException; 40import android.os.SystemProperties; 41import android.os.UserHandle; 42import android.provider.Settings; 43import android.text.TextUtils; 44import android.util.Log; 45import android.util.Pair; 46import android.util.Slog; 47import android.util.SparseArray; 48import com.android.internal.annotations.GuardedBy; 49import com.android.internal.annotations.VisibleForTesting; 50import com.android.internal.os.AppFuseMount; 51import com.android.internal.os.FuseAppLoop; 52import com.android.internal.os.RoSystemProperties; 53import com.android.internal.os.SomeArgs; 54import com.android.internal.util.Preconditions; 55 56import java.io.BufferedReader; 57import java.io.File; 58import java.io.FileInputStream; 59import java.io.FileNotFoundException; 60import java.io.IOException; 61import java.io.InputStreamReader; 62import java.lang.ref.WeakReference; 63import java.util.ArrayList; 64import java.util.Arrays; 65import java.util.Collections; 66import java.util.Iterator; 67import java.util.List; 68import java.util.Objects; 69import java.util.concurrent.ThreadFactory; 70import java.util.concurrent.atomic.AtomicInteger; 71 72/** 73 * StorageManager is the interface to the systems storage service. The storage 74 * manager handles storage-related items such as Opaque Binary Blobs (OBBs). 75 * <p> 76 * OBBs contain a filesystem that maybe be encrypted on disk and mounted 77 * on-demand from an application. OBBs are a good way of providing large amounts 78 * of binary assets without packaging them into APKs as they may be multiple 79 * gigabytes in size. However, due to their size, they're most likely stored in 80 * a shared storage pool accessible from all programs. The system does not 81 * guarantee the security of the OBB file itself: if any program modifies the 82 * OBB, there is no guarantee that a read from that OBB will produce the 83 * expected output. 84 * <p> 85 * Get an instance of this class by calling 86 * {@link android.content.Context#getSystemService(java.lang.String)} with an 87 * argument of {@link android.content.Context#STORAGE_SERVICE}. 88 */ 89public class StorageManager { 90 private static final String TAG = "StorageManager"; 91 92 /** {@hide} */ 93 public static final String PROP_PRIMARY_PHYSICAL = "ro.vold.primary_physical"; 94 /** {@hide} */ 95 public static final String PROP_HAS_ADOPTABLE = "vold.has_adoptable"; 96 /** {@hide} */ 97 public static final String PROP_FORCE_ADOPTABLE = "persist.fw.force_adoptable"; 98 /** {@hide} */ 99 public static final String PROP_EMULATE_FBE = "persist.sys.emulate_fbe"; 100 /** {@hide} */ 101 public static final String PROP_SDCARDFS = "persist.sys.sdcardfs"; 102 103 /** {@hide} */ 104 public static final String UUID_PRIVATE_INTERNAL = null; 105 /** {@hide} */ 106 public static final String UUID_PRIMARY_PHYSICAL = "primary_physical"; 107 108 109 /** 110 * Activity Action: Allows the user to manage their storage. This activity provides the ability 111 * to free up space on the device by deleting data such as apps. 112 * <p> 113 * Input: Nothing. 114 * <p> 115 * Output: Nothing. 116 */ 117 @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) 118 public static final String ACTION_MANAGE_STORAGE 119 = "android.os.storage.action.MANAGE_STORAGE"; 120 121 /** {@hide} */ 122 public static final int DEBUG_FORCE_ADOPTABLE = 1 << 0; 123 /** {@hide} */ 124 public static final int DEBUG_EMULATE_FBE = 1 << 1; 125 /** {@hide} */ 126 public static final int DEBUG_SDCARDFS_FORCE_ON = 1 << 2; 127 /** {@hide} */ 128 public static final int DEBUG_SDCARDFS_FORCE_OFF = 1 << 3; 129 130 // NOTE: keep in sync with installd 131 /** {@hide} */ 132 public static final int FLAG_STORAGE_DE = 1 << 0; 133 /** {@hide} */ 134 public static final int FLAG_STORAGE_CE = 1 << 1; 135 136 /** {@hide} */ 137 public static final int FLAG_FOR_WRITE = 1 << 8; 138 /** {@hide} */ 139 public static final int FLAG_REAL_STATE = 1 << 9; 140 /** {@hide} */ 141 public static final int FLAG_INCLUDE_INVISIBLE = 1 << 10; 142 143 /** {@hide} */ 144 public static final int FSTRIM_FLAG_DEEP = 1 << 0; 145 /** {@hide} */ 146 public static final int FSTRIM_FLAG_BENCHMARK = 1 << 1; 147 148 /** @hide The volume is not encrypted. */ 149 public static final int ENCRYPTION_STATE_NONE = 1; 150 151 /** @hide The volume has been encrypted succesfully. */ 152 public static final int ENCRYPTION_STATE_OK = 0; 153 154 /** @hide The volume is in a bad state.*/ 155 public static final int ENCRYPTION_STATE_ERROR_UNKNOWN = -1; 156 157 /** @hide Encryption is incomplete */ 158 public static final int ENCRYPTION_STATE_ERROR_INCOMPLETE = -2; 159 160 /** @hide Encryption is incomplete and irrecoverable */ 161 public static final int ENCRYPTION_STATE_ERROR_INCONSISTENT = -3; 162 163 /** @hide Underlying data is corrupt */ 164 public static final int ENCRYPTION_STATE_ERROR_CORRUPT = -4; 165 166 private static volatile IStorageManager sStorageManager = null; 167 168 // TODO: the location of the primary storage block varies from device to device, so we need to 169 // try the most likely candidates - a long-term solution would be a device-specific vold 170 // function that returns the calculated size. 171 private static final String[] INTERNAL_STORAGE_SIZE_PATHS = { 172 "/sys/block/mmcblk0/size", 173 "/sys/block/sda/size" 174 }; 175 private static final int INTERNAL_STORAGE_SECTOR_SIZE = 512; 176 177 private final Context mContext; 178 private final ContentResolver mResolver; 179 180 private final IStorageManager mStorageManager; 181 private final Looper mLooper; 182 private final AtomicInteger mNextNonce = new AtomicInteger(0); 183 184 private final ArrayList<StorageEventListenerDelegate> mDelegates = new ArrayList<>(); 185 186 private static class StorageEventListenerDelegate extends IStorageEventListener.Stub implements 187 Handler.Callback { 188 private static final int MSG_STORAGE_STATE_CHANGED = 1; 189 private static final int MSG_VOLUME_STATE_CHANGED = 2; 190 private static final int MSG_VOLUME_RECORD_CHANGED = 3; 191 private static final int MSG_VOLUME_FORGOTTEN = 4; 192 private static final int MSG_DISK_SCANNED = 5; 193 private static final int MSG_DISK_DESTROYED = 6; 194 195 final StorageEventListener mCallback; 196 final Handler mHandler; 197 198 public StorageEventListenerDelegate(StorageEventListener callback, Looper looper) { 199 mCallback = callback; 200 mHandler = new Handler(looper, this); 201 } 202 203 @Override 204 public boolean handleMessage(Message msg) { 205 final SomeArgs args = (SomeArgs) msg.obj; 206 switch (msg.what) { 207 case MSG_STORAGE_STATE_CHANGED: 208 mCallback.onStorageStateChanged((String) args.arg1, (String) args.arg2, 209 (String) args.arg3); 210 args.recycle(); 211 return true; 212 case MSG_VOLUME_STATE_CHANGED: 213 mCallback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3); 214 args.recycle(); 215 return true; 216 case MSG_VOLUME_RECORD_CHANGED: 217 mCallback.onVolumeRecordChanged((VolumeRecord) args.arg1); 218 args.recycle(); 219 return true; 220 case MSG_VOLUME_FORGOTTEN: 221 mCallback.onVolumeForgotten((String) args.arg1); 222 args.recycle(); 223 return true; 224 case MSG_DISK_SCANNED: 225 mCallback.onDiskScanned((DiskInfo) args.arg1, args.argi2); 226 args.recycle(); 227 return true; 228 case MSG_DISK_DESTROYED: 229 mCallback.onDiskDestroyed((DiskInfo) args.arg1); 230 args.recycle(); 231 return true; 232 } 233 args.recycle(); 234 return false; 235 } 236 237 @Override 238 public void onUsbMassStorageConnectionChanged(boolean connected) throws RemoteException { 239 // Ignored 240 } 241 242 @Override 243 public void onStorageStateChanged(String path, String oldState, String newState) { 244 final SomeArgs args = SomeArgs.obtain(); 245 args.arg1 = path; 246 args.arg2 = oldState; 247 args.arg3 = newState; 248 mHandler.obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget(); 249 } 250 251 @Override 252 public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { 253 final SomeArgs args = SomeArgs.obtain(); 254 args.arg1 = vol; 255 args.argi2 = oldState; 256 args.argi3 = newState; 257 mHandler.obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget(); 258 } 259 260 @Override 261 public void onVolumeRecordChanged(VolumeRecord rec) { 262 final SomeArgs args = SomeArgs.obtain(); 263 args.arg1 = rec; 264 mHandler.obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget(); 265 } 266 267 @Override 268 public void onVolumeForgotten(String fsUuid) { 269 final SomeArgs args = SomeArgs.obtain(); 270 args.arg1 = fsUuid; 271 mHandler.obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget(); 272 } 273 274 @Override 275 public void onDiskScanned(DiskInfo disk, int volumeCount) { 276 final SomeArgs args = SomeArgs.obtain(); 277 args.arg1 = disk; 278 args.argi2 = volumeCount; 279 mHandler.obtainMessage(MSG_DISK_SCANNED, args).sendToTarget(); 280 } 281 282 @Override 283 public void onDiskDestroyed(DiskInfo disk) throws RemoteException { 284 final SomeArgs args = SomeArgs.obtain(); 285 args.arg1 = disk; 286 mHandler.obtainMessage(MSG_DISK_DESTROYED, args).sendToTarget(); 287 } 288 } 289 290 /** 291 * Binder listener for OBB action results. 292 */ 293 private final ObbActionListener mObbActionListener = new ObbActionListener(); 294 295 private class ObbActionListener extends IObbActionListener.Stub { 296 @SuppressWarnings("hiding") 297 private SparseArray<ObbListenerDelegate> mListeners = new SparseArray<ObbListenerDelegate>(); 298 299 @Override 300 public void onObbResult(String filename, int nonce, int status) { 301 final ObbListenerDelegate delegate; 302 synchronized (mListeners) { 303 delegate = mListeners.get(nonce); 304 if (delegate != null) { 305 mListeners.remove(nonce); 306 } 307 } 308 309 if (delegate != null) { 310 delegate.sendObbStateChanged(filename, status); 311 } 312 } 313 314 public int addListener(OnObbStateChangeListener listener) { 315 final ObbListenerDelegate delegate = new ObbListenerDelegate(listener); 316 317 synchronized (mListeners) { 318 mListeners.put(delegate.nonce, delegate); 319 } 320 321 return delegate.nonce; 322 } 323 } 324 325 private int getNextNonce() { 326 return mNextNonce.getAndIncrement(); 327 } 328 329 /** 330 * Private class containing sender and receiver code for StorageEvents. 331 */ 332 private class ObbListenerDelegate { 333 private final WeakReference<OnObbStateChangeListener> mObbEventListenerRef; 334 private final Handler mHandler; 335 336 private final int nonce; 337 338 ObbListenerDelegate(OnObbStateChangeListener listener) { 339 nonce = getNextNonce(); 340 mObbEventListenerRef = new WeakReference<OnObbStateChangeListener>(listener); 341 mHandler = new Handler(mLooper) { 342 @Override 343 public void handleMessage(Message msg) { 344 final OnObbStateChangeListener changeListener = getListener(); 345 if (changeListener == null) { 346 return; 347 } 348 349 changeListener.onObbStateChange((String) msg.obj, msg.arg1); 350 } 351 }; 352 } 353 354 OnObbStateChangeListener getListener() { 355 if (mObbEventListenerRef == null) { 356 return null; 357 } 358 return mObbEventListenerRef.get(); 359 } 360 361 void sendObbStateChanged(String path, int state) { 362 mHandler.obtainMessage(0, state, 0, path).sendToTarget(); 363 } 364 } 365 366 /** {@hide} */ 367 @Deprecated 368 public static StorageManager from(Context context) { 369 return context.getSystemService(StorageManager.class); 370 } 371 372 /** 373 * Constructs a StorageManager object through which an application can 374 * can communicate with the systems mount service. 375 * 376 * @param looper The {@link android.os.Looper} which events will be received on. 377 * 378 * <p>Applications can get instance of this class by calling 379 * {@link android.content.Context#getSystemService(java.lang.String)} with an argument 380 * of {@link android.content.Context#STORAGE_SERVICE}. 381 * 382 * @hide 383 */ 384 public StorageManager(Context context, Looper looper) throws ServiceNotFoundException { 385 mContext = context; 386 mResolver = context.getContentResolver(); 387 mLooper = looper; 388 mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getServiceOrThrow("mount")); 389 } 390 391 /** 392 * Registers a {@link android.os.storage.StorageEventListener StorageEventListener}. 393 * 394 * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object. 395 * 396 * @hide 397 */ 398 public void registerListener(StorageEventListener listener) { 399 synchronized (mDelegates) { 400 final StorageEventListenerDelegate delegate = new StorageEventListenerDelegate(listener, 401 mLooper); 402 try { 403 mStorageManager.registerListener(delegate); 404 } catch (RemoteException e) { 405 throw e.rethrowFromSystemServer(); 406 } 407 mDelegates.add(delegate); 408 } 409 } 410 411 /** 412 * Unregisters a {@link android.os.storage.StorageEventListener StorageEventListener}. 413 * 414 * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object. 415 * 416 * @hide 417 */ 418 public void unregisterListener(StorageEventListener listener) { 419 synchronized (mDelegates) { 420 for (Iterator<StorageEventListenerDelegate> i = mDelegates.iterator(); i.hasNext();) { 421 final StorageEventListenerDelegate delegate = i.next(); 422 if (delegate.mCallback == listener) { 423 try { 424 mStorageManager.unregisterListener(delegate); 425 } catch (RemoteException e) { 426 throw e.rethrowFromSystemServer(); 427 } 428 i.remove(); 429 } 430 } 431 } 432 } 433 434 /** 435 * Enables USB Mass Storage (UMS) on the device. 436 * 437 * @hide 438 */ 439 @Deprecated 440 public void enableUsbMassStorage() { 441 } 442 443 /** 444 * Disables USB Mass Storage (UMS) on the device. 445 * 446 * @hide 447 */ 448 @Deprecated 449 public void disableUsbMassStorage() { 450 } 451 452 /** 453 * Query if a USB Mass Storage (UMS) host is connected. 454 * @return true if UMS host is connected. 455 * 456 * @hide 457 */ 458 @Deprecated 459 public boolean isUsbMassStorageConnected() { 460 return false; 461 } 462 463 /** 464 * Query if a USB Mass Storage (UMS) is enabled on the device. 465 * @return true if UMS host is enabled. 466 * 467 * @hide 468 */ 469 @Deprecated 470 public boolean isUsbMassStorageEnabled() { 471 return false; 472 } 473 474 /** 475 * Mount an Opaque Binary Blob (OBB) file. If a <code>key</code> is 476 * specified, it is supplied to the mounting process to be used in any 477 * encryption used in the OBB. 478 * <p> 479 * The OBB will remain mounted for as long as the StorageManager reference 480 * is held by the application. As soon as this reference is lost, the OBBs 481 * in use will be unmounted. The {@link OnObbStateChangeListener} registered 482 * with this call will receive the success or failure of this operation. 483 * <p> 484 * <em>Note:</em> you can only mount OBB files for which the OBB tag on the 485 * file matches a package ID that is owned by the calling program's UID. 486 * That is, shared UID applications can attempt to mount any other 487 * application's OBB that shares its UID. 488 * 489 * @param rawPath the path to the OBB file 490 * @param key secret used to encrypt the OBB; may be <code>null</code> if no 491 * encryption was used on the OBB. 492 * @param listener will receive the success or failure of the operation 493 * @return whether the mount call was successfully queued or not 494 */ 495 public boolean mountObb(String rawPath, String key, OnObbStateChangeListener listener) { 496 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 497 Preconditions.checkNotNull(listener, "listener cannot be null"); 498 499 try { 500 final String canonicalPath = new File(rawPath).getCanonicalPath(); 501 final int nonce = mObbActionListener.addListener(listener); 502 mStorageManager.mountObb(rawPath, canonicalPath, key, mObbActionListener, nonce); 503 return true; 504 } catch (IOException e) { 505 throw new IllegalArgumentException("Failed to resolve path: " + rawPath, e); 506 } catch (RemoteException e) { 507 throw e.rethrowFromSystemServer(); 508 } 509 } 510 511 /** 512 * Unmount an Opaque Binary Blob (OBB) file asynchronously. If the 513 * <code>force</code> flag is true, it will kill any application needed to 514 * unmount the given OBB (even the calling application). 515 * <p> 516 * The {@link OnObbStateChangeListener} registered with this call will 517 * receive the success or failure of this operation. 518 * <p> 519 * <em>Note:</em> you can only mount OBB files for which the OBB tag on the 520 * file matches a package ID that is owned by the calling program's UID. 521 * That is, shared UID applications can obtain access to any other 522 * application's OBB that shares its UID. 523 * <p> 524 * 525 * @param rawPath path to the OBB file 526 * @param force whether to kill any programs using this in order to unmount 527 * it 528 * @param listener will receive the success or failure of the operation 529 * @return whether the unmount call was successfully queued or not 530 */ 531 public boolean unmountObb(String rawPath, boolean force, OnObbStateChangeListener listener) { 532 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 533 Preconditions.checkNotNull(listener, "listener cannot be null"); 534 535 try { 536 final int nonce = mObbActionListener.addListener(listener); 537 mStorageManager.unmountObb(rawPath, force, mObbActionListener, nonce); 538 return true; 539 } catch (RemoteException e) { 540 throw e.rethrowFromSystemServer(); 541 } 542 } 543 544 /** 545 * Check whether an Opaque Binary Blob (OBB) is mounted or not. 546 * 547 * @param rawPath path to OBB image 548 * @return true if OBB is mounted; false if not mounted or on error 549 */ 550 public boolean isObbMounted(String rawPath) { 551 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 552 553 try { 554 return mStorageManager.isObbMounted(rawPath); 555 } catch (RemoteException e) { 556 throw e.rethrowFromSystemServer(); 557 } 558 } 559 560 /** 561 * Check the mounted path of an Opaque Binary Blob (OBB) file. This will 562 * give you the path to where you can obtain access to the internals of the 563 * OBB. 564 * 565 * @param rawPath path to OBB image 566 * @return absolute path to mounted OBB image data or <code>null</code> if 567 * not mounted or exception encountered trying to read status 568 */ 569 public String getMountedObbPath(String rawPath) { 570 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 571 572 try { 573 return mStorageManager.getMountedObbPath(rawPath); 574 } catch (RemoteException e) { 575 throw e.rethrowFromSystemServer(); 576 } 577 } 578 579 /** {@hide} */ 580 public @NonNull List<DiskInfo> getDisks() { 581 try { 582 return Arrays.asList(mStorageManager.getDisks()); 583 } catch (RemoteException e) { 584 throw e.rethrowFromSystemServer(); 585 } 586 } 587 588 /** {@hide} */ 589 public @Nullable DiskInfo findDiskById(String id) { 590 Preconditions.checkNotNull(id); 591 // TODO; go directly to service to make this faster 592 for (DiskInfo disk : getDisks()) { 593 if (Objects.equals(disk.id, id)) { 594 return disk; 595 } 596 } 597 return null; 598 } 599 600 /** {@hide} */ 601 public @Nullable VolumeInfo findVolumeById(String id) { 602 Preconditions.checkNotNull(id); 603 // TODO; go directly to service to make this faster 604 for (VolumeInfo vol : getVolumes()) { 605 if (Objects.equals(vol.id, id)) { 606 return vol; 607 } 608 } 609 return null; 610 } 611 612 /** {@hide} */ 613 public @Nullable VolumeInfo findVolumeByUuid(String fsUuid) { 614 Preconditions.checkNotNull(fsUuid); 615 // TODO; go directly to service to make this faster 616 for (VolumeInfo vol : getVolumes()) { 617 if (Objects.equals(vol.fsUuid, fsUuid)) { 618 return vol; 619 } 620 } 621 return null; 622 } 623 624 /** {@hide} */ 625 public @Nullable VolumeRecord findRecordByUuid(String fsUuid) { 626 Preconditions.checkNotNull(fsUuid); 627 // TODO; go directly to service to make this faster 628 for (VolumeRecord rec : getVolumeRecords()) { 629 if (Objects.equals(rec.fsUuid, fsUuid)) { 630 return rec; 631 } 632 } 633 return null; 634 } 635 636 /** {@hide} */ 637 public @Nullable VolumeInfo findPrivateForEmulated(VolumeInfo emulatedVol) { 638 if (emulatedVol != null) { 639 return findVolumeById(emulatedVol.getId().replace("emulated", "private")); 640 } else { 641 return null; 642 } 643 } 644 645 /** {@hide} */ 646 public @Nullable VolumeInfo findEmulatedForPrivate(VolumeInfo privateVol) { 647 if (privateVol != null) { 648 return findVolumeById(privateVol.getId().replace("private", "emulated")); 649 } else { 650 return null; 651 } 652 } 653 654 /** {@hide} */ 655 public @Nullable VolumeInfo findVolumeByQualifiedUuid(String volumeUuid) { 656 if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) { 657 return findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL); 658 } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) { 659 return getPrimaryPhysicalVolume(); 660 } else { 661 return findVolumeByUuid(volumeUuid); 662 } 663 } 664 665 /** {@hide} */ 666 public @NonNull List<VolumeInfo> getVolumes() { 667 try { 668 return Arrays.asList(mStorageManager.getVolumes(0)); 669 } catch (RemoteException e) { 670 throw e.rethrowFromSystemServer(); 671 } 672 } 673 674 /** {@hide} */ 675 public @NonNull List<VolumeInfo> getWritablePrivateVolumes() { 676 try { 677 final ArrayList<VolumeInfo> res = new ArrayList<>(); 678 for (VolumeInfo vol : mStorageManager.getVolumes(0)) { 679 if (vol.getType() == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()) { 680 res.add(vol); 681 } 682 } 683 return res; 684 } catch (RemoteException e) { 685 throw e.rethrowFromSystemServer(); 686 } 687 } 688 689 /** {@hide} */ 690 public @NonNull List<VolumeRecord> getVolumeRecords() { 691 try { 692 return Arrays.asList(mStorageManager.getVolumeRecords(0)); 693 } catch (RemoteException e) { 694 throw e.rethrowFromSystemServer(); 695 } 696 } 697 698 /** {@hide} */ 699 public @Nullable String getBestVolumeDescription(VolumeInfo vol) { 700 if (vol == null) return null; 701 702 // Nickname always takes precedence when defined 703 if (!TextUtils.isEmpty(vol.fsUuid)) { 704 final VolumeRecord rec = findRecordByUuid(vol.fsUuid); 705 if (rec != null && !TextUtils.isEmpty(rec.nickname)) { 706 return rec.nickname; 707 } 708 } 709 710 if (!TextUtils.isEmpty(vol.getDescription())) { 711 return vol.getDescription(); 712 } 713 714 if (vol.disk != null) { 715 return vol.disk.getDescription(); 716 } 717 718 return null; 719 } 720 721 /** {@hide} */ 722 public @Nullable VolumeInfo getPrimaryPhysicalVolume() { 723 final List<VolumeInfo> vols = getVolumes(); 724 for (VolumeInfo vol : vols) { 725 if (vol.isPrimaryPhysical()) { 726 return vol; 727 } 728 } 729 return null; 730 } 731 732 /** {@hide} */ 733 public void mount(String volId) { 734 try { 735 mStorageManager.mount(volId); 736 } catch (RemoteException e) { 737 throw e.rethrowFromSystemServer(); 738 } 739 } 740 741 /** {@hide} */ 742 public void unmount(String volId) { 743 try { 744 mStorageManager.unmount(volId); 745 } catch (RemoteException e) { 746 throw e.rethrowFromSystemServer(); 747 } 748 } 749 750 /** {@hide} */ 751 public void format(String volId) { 752 try { 753 mStorageManager.format(volId); 754 } catch (RemoteException e) { 755 throw e.rethrowFromSystemServer(); 756 } 757 } 758 759 /** {@hide} */ 760 public long benchmark(String volId) { 761 try { 762 return mStorageManager.benchmark(volId); 763 } catch (RemoteException e) { 764 throw e.rethrowFromSystemServer(); 765 } 766 } 767 768 /** {@hide} */ 769 public void partitionPublic(String diskId) { 770 try { 771 mStorageManager.partitionPublic(diskId); 772 } catch (RemoteException e) { 773 throw e.rethrowFromSystemServer(); 774 } 775 } 776 777 /** {@hide} */ 778 public void partitionPrivate(String diskId) { 779 try { 780 mStorageManager.partitionPrivate(diskId); 781 } catch (RemoteException e) { 782 throw e.rethrowFromSystemServer(); 783 } 784 } 785 786 /** {@hide} */ 787 public void partitionMixed(String diskId, int ratio) { 788 try { 789 mStorageManager.partitionMixed(diskId, ratio); 790 } catch (RemoteException e) { 791 throw e.rethrowFromSystemServer(); 792 } 793 } 794 795 /** {@hide} */ 796 public void wipeAdoptableDisks() { 797 // We only wipe devices in "adoptable" locations, which are in a 798 // long-term stable slot/location on the device, where apps have a 799 // reasonable chance of storing sensitive data. (Apps need to go through 800 // SAF to write to transient volumes.) 801 final List<DiskInfo> disks = getDisks(); 802 for (DiskInfo disk : disks) { 803 final String diskId = disk.getId(); 804 if (disk.isAdoptable()) { 805 Slog.d(TAG, "Found adoptable " + diskId + "; wiping"); 806 try { 807 // TODO: switch to explicit wipe command when we have it, 808 // for now rely on the fact that vfat format does a wipe 809 mStorageManager.partitionPublic(diskId); 810 } catch (Exception e) { 811 Slog.w(TAG, "Failed to wipe " + diskId + ", but soldiering onward", e); 812 } 813 } else { 814 Slog.d(TAG, "Ignorning non-adoptable disk " + disk.getId()); 815 } 816 } 817 } 818 819 /** {@hide} */ 820 public void setVolumeNickname(String fsUuid, String nickname) { 821 try { 822 mStorageManager.setVolumeNickname(fsUuid, nickname); 823 } catch (RemoteException e) { 824 throw e.rethrowFromSystemServer(); 825 } 826 } 827 828 /** {@hide} */ 829 public void setVolumeInited(String fsUuid, boolean inited) { 830 try { 831 mStorageManager.setVolumeUserFlags(fsUuid, inited ? VolumeRecord.USER_FLAG_INITED : 0, 832 VolumeRecord.USER_FLAG_INITED); 833 } catch (RemoteException e) { 834 throw e.rethrowFromSystemServer(); 835 } 836 } 837 838 /** {@hide} */ 839 public void setVolumeSnoozed(String fsUuid, boolean snoozed) { 840 try { 841 mStorageManager.setVolumeUserFlags(fsUuid, snoozed ? VolumeRecord.USER_FLAG_SNOOZED : 0, 842 VolumeRecord.USER_FLAG_SNOOZED); 843 } catch (RemoteException e) { 844 throw e.rethrowFromSystemServer(); 845 } 846 } 847 848 /** {@hide} */ 849 public void forgetVolume(String fsUuid) { 850 try { 851 mStorageManager.forgetVolume(fsUuid); 852 } catch (RemoteException e) { 853 throw e.rethrowFromSystemServer(); 854 } 855 } 856 857 /** 858 * This is not the API you're looking for. 859 * 860 * @see PackageManager#getPrimaryStorageCurrentVolume() 861 * @hide 862 */ 863 public String getPrimaryStorageUuid() { 864 try { 865 return mStorageManager.getPrimaryStorageUuid(); 866 } catch (RemoteException e) { 867 throw e.rethrowFromSystemServer(); 868 } 869 } 870 871 /** 872 * This is not the API you're looking for. 873 * 874 * @see PackageManager#movePrimaryStorage(VolumeInfo) 875 * @hide 876 */ 877 public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) { 878 try { 879 mStorageManager.setPrimaryStorageUuid(volumeUuid, callback); 880 } catch (RemoteException e) { 881 throw e.rethrowFromSystemServer(); 882 } 883 } 884 885 /** 886 * Return the {@link StorageVolume} that contains the given file, or {@code null} if none. 887 */ 888 public @Nullable StorageVolume getStorageVolume(File file) { 889 return getStorageVolume(getVolumeList(), file); 890 } 891 892 /** {@hide} */ 893 public static @Nullable StorageVolume getStorageVolume(File file, int userId) { 894 return getStorageVolume(getVolumeList(userId, 0), file); 895 } 896 897 /** {@hide} */ 898 private static @Nullable StorageVolume getStorageVolume(StorageVolume[] volumes, File file) { 899 if (file == null) { 900 return null; 901 } 902 try { 903 file = file.getCanonicalFile(); 904 } catch (IOException ignored) { 905 Slog.d(TAG, "Could not get canonical path for " + file); 906 return null; 907 } 908 for (StorageVolume volume : volumes) { 909 File volumeFile = volume.getPathFile(); 910 try { 911 volumeFile = volumeFile.getCanonicalFile(); 912 } catch (IOException ignored) { 913 continue; 914 } 915 if (FileUtils.contains(volumeFile, file)) { 916 return volume; 917 } 918 } 919 return null; 920 } 921 922 /** 923 * Gets the state of a volume via its mountpoint. 924 * @hide 925 */ 926 @Deprecated 927 public @NonNull String getVolumeState(String mountPoint) { 928 final StorageVolume vol = getStorageVolume(new File(mountPoint)); 929 if (vol != null) { 930 return vol.getState(); 931 } else { 932 return Environment.MEDIA_UNKNOWN; 933 } 934 } 935 936 /** 937 * Return the list of shared/external storage volumes available to the 938 * current user. This includes both the primary shared storage device and 939 * any attached external volumes including SD cards and USB drives. 940 * 941 * @see Environment#getExternalStorageDirectory() 942 * @see StorageVolume#createAccessIntent(String) 943 */ 944 public @NonNull List<StorageVolume> getStorageVolumes() { 945 final ArrayList<StorageVolume> res = new ArrayList<>(); 946 Collections.addAll(res, 947 getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE)); 948 return res; 949 } 950 951 /** 952 * Return the primary shared/external storage volume available to the 953 * current user. This volume is the same storage device returned by 954 * {@link Environment#getExternalStorageDirectory()} and 955 * {@link Context#getExternalFilesDir(String)}. 956 */ 957 public @NonNull StorageVolume getPrimaryStorageVolume() { 958 return getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE)[0]; 959 } 960 961 /** {@hide} */ 962 public static Pair<String, Long> getPrimaryStoragePathAndSize() { 963 for (String path : INTERNAL_STORAGE_SIZE_PATHS) { 964 final long numberBlocks = readLong(path); 965 if (numberBlocks > 0) { 966 return new Pair<>(path, Long.valueOf(numberBlocks * INTERNAL_STORAGE_SECTOR_SIZE)); 967 } 968 } 969 return null; 970 } 971 972 973 /** {@hide} */ 974 public long getPrimaryStorageSize() { 975 final Pair<String, Long> pair = getPrimaryStoragePathAndSize(); 976 return pair == null ? 0 : pair.second.longValue(); 977 } 978 979 private static long readLong(String path) { 980 try (final FileInputStream fis = new FileInputStream(path); 981 final BufferedReader reader = new BufferedReader(new InputStreamReader(fis));) { 982 return Long.parseLong(reader.readLine()); 983 } catch (FileNotFoundException e) { 984 // This is expected since we are trying to parse multiple paths. 985 Slog.i(TAG, "readLong(): Path doesn't exist: " + path + ": " + e); 986 return 0; 987 } catch (NumberFormatException e) { 988 Slog.e(TAG, "readLong(): Could not parse " + path + ": " + e); 989 return 0; 990 } catch (Exception e) { 991 Slog.e(TAG, "readLong(): Unknown exception while opening " + path + ": " + e); 992 return 0; 993 } 994 } 995 996 /** @removed */ 997 public @NonNull StorageVolume[] getVolumeList() { 998 return getVolumeList(mContext.getUserId(), 0); 999 } 1000 1001 /** {@hide} */ 1002 public static @NonNull StorageVolume[] getVolumeList(int userId, int flags) { 1003 final IStorageManager storageManager = IStorageManager.Stub.asInterface( 1004 ServiceManager.getService("mount")); 1005 try { 1006 String packageName = ActivityThread.currentOpPackageName(); 1007 if (packageName == null) { 1008 // Package name can be null if the activity thread is running but the app 1009 // hasn't bound yet. In this case we fall back to the first package in the 1010 // current UID. This works for runtime permissions as permission state is 1011 // per UID and permission realted app ops are updated for all UID packages. 1012 String[] packageNames = ActivityThread.getPackageManager().getPackagesForUid( 1013 android.os.Process.myUid()); 1014 if (packageNames == null || packageNames.length <= 0) { 1015 return new StorageVolume[0]; 1016 } 1017 packageName = packageNames[0]; 1018 } 1019 final int uid = ActivityThread.getPackageManager().getPackageUid(packageName, 1020 PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId); 1021 if (uid <= 0) { 1022 return new StorageVolume[0]; 1023 } 1024 return storageManager.getVolumeList(uid, packageName, flags); 1025 } catch (RemoteException e) { 1026 throw e.rethrowFromSystemServer(); 1027 } 1028 } 1029 1030 /** 1031 * Returns list of paths for all mountable volumes. 1032 * @hide 1033 */ 1034 @Deprecated 1035 public @NonNull String[] getVolumePaths() { 1036 StorageVolume[] volumes = getVolumeList(); 1037 int count = volumes.length; 1038 String[] paths = new String[count]; 1039 for (int i = 0; i < count; i++) { 1040 paths[i] = volumes[i].getPath(); 1041 } 1042 return paths; 1043 } 1044 1045 /** @removed */ 1046 public @NonNull StorageVolume getPrimaryVolume() { 1047 return getPrimaryVolume(getVolumeList()); 1048 } 1049 1050 /** {@hide} */ 1051 public static @NonNull StorageVolume getPrimaryVolume(StorageVolume[] volumes) { 1052 for (StorageVolume volume : volumes) { 1053 if (volume.isPrimary()) { 1054 return volume; 1055 } 1056 } 1057 throw new IllegalStateException("Missing primary storage"); 1058 } 1059 1060 /** {@hide} */ 1061 private static final int DEFAULT_THRESHOLD_PERCENTAGE = 10; 1062 private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500 * MB_IN_BYTES; 1063 private static final long DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES; 1064 1065 /** 1066 * Return the number of available bytes until the given path is considered 1067 * running low on storage. 1068 * 1069 * @hide 1070 */ 1071 public long getStorageBytesUntilLow(File path) { 1072 return path.getUsableSpace() - getStorageFullBytes(path); 1073 } 1074 1075 /** 1076 * Return the number of available bytes at which the given path is 1077 * considered running low on storage. 1078 * 1079 * @hide 1080 */ 1081 public long getStorageLowBytes(File path) { 1082 final long lowPercent = Settings.Global.getInt(mResolver, 1083 Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE); 1084 final long lowBytes = (path.getTotalSpace() * lowPercent) / 100; 1085 1086 final long maxLowBytes = Settings.Global.getLong(mResolver, 1087 Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES, DEFAULT_THRESHOLD_MAX_BYTES); 1088 1089 return Math.min(lowBytes, maxLowBytes); 1090 } 1091 1092 /** 1093 * Return the number of available bytes at which the given path is 1094 * considered full. 1095 * 1096 * @hide 1097 */ 1098 public long getStorageFullBytes(File path) { 1099 return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES, 1100 DEFAULT_FULL_THRESHOLD_BYTES); 1101 } 1102 1103 /** {@hide} */ 1104 public void createUserKey(int userId, int serialNumber, boolean ephemeral) { 1105 try { 1106 mStorageManager.createUserKey(userId, serialNumber, ephemeral); 1107 } catch (RemoteException e) { 1108 throw e.rethrowFromSystemServer(); 1109 } 1110 } 1111 1112 /** {@hide} */ 1113 public void destroyUserKey(int userId) { 1114 try { 1115 mStorageManager.destroyUserKey(userId); 1116 } catch (RemoteException e) { 1117 throw e.rethrowFromSystemServer(); 1118 } 1119 } 1120 1121 /** {@hide} */ 1122 public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) { 1123 try { 1124 mStorageManager.unlockUserKey(userId, serialNumber, token, secret); 1125 } catch (RemoteException e) { 1126 throw e.rethrowFromSystemServer(); 1127 } 1128 } 1129 1130 /** {@hide} */ 1131 public void lockUserKey(int userId) { 1132 try { 1133 mStorageManager.lockUserKey(userId); 1134 } catch (RemoteException e) { 1135 throw e.rethrowFromSystemServer(); 1136 } 1137 } 1138 1139 /** {@hide} */ 1140 public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) { 1141 try { 1142 mStorageManager.prepareUserStorage(volumeUuid, userId, serialNumber, flags); 1143 } catch (RemoteException e) { 1144 throw e.rethrowFromSystemServer(); 1145 } 1146 } 1147 1148 /** {@hide} */ 1149 public void destroyUserStorage(String volumeUuid, int userId, int flags) { 1150 try { 1151 mStorageManager.destroyUserStorage(volumeUuid, userId, flags); 1152 } catch (RemoteException e) { 1153 throw e.rethrowFromSystemServer(); 1154 } 1155 } 1156 1157 /** {@hide} */ 1158 public static boolean isUserKeyUnlocked(int userId) { 1159 if (sStorageManager == null) { 1160 sStorageManager = IStorageManager.Stub 1161 .asInterface(ServiceManager.getService("mount")); 1162 } 1163 if (sStorageManager == null) { 1164 Slog.w(TAG, "Early during boot, assuming locked"); 1165 return false; 1166 } 1167 final long token = Binder.clearCallingIdentity(); 1168 try { 1169 return sStorageManager.isUserKeyUnlocked(userId); 1170 } catch (RemoteException e) { 1171 throw e.rethrowAsRuntimeException(); 1172 } finally { 1173 Binder.restoreCallingIdentity(token); 1174 } 1175 } 1176 1177 /** 1178 * Return if data stored at or under the given path will be encrypted while 1179 * at rest. This can help apps avoid the overhead of double-encrypting data. 1180 */ 1181 public boolean isEncrypted(File file) { 1182 if (FileUtils.contains(Environment.getDataDirectory(), file)) { 1183 return isEncrypted(); 1184 } else if (FileUtils.contains(Environment.getExpandDirectory(), file)) { 1185 return true; 1186 } 1187 // TODO: extend to support shared storage 1188 return false; 1189 } 1190 1191 /** {@hide} 1192 * Is this device encryptable or already encrypted? 1193 * @return true for encryptable or encrypted 1194 * false not encrypted and not encryptable 1195 */ 1196 public static boolean isEncryptable() { 1197 return RoSystemProperties.CRYPTO_ENCRYPTABLE; 1198 } 1199 1200 /** {@hide} 1201 * Is this device already encrypted? 1202 * @return true for encrypted. (Implies isEncryptable() == true) 1203 * false not encrypted 1204 */ 1205 public static boolean isEncrypted() { 1206 return RoSystemProperties.CRYPTO_ENCRYPTED; 1207 } 1208 1209 /** {@hide} 1210 * Is this device file encrypted? 1211 * @return true for file encrypted. (Implies isEncrypted() == true) 1212 * false not encrypted or block encrypted 1213 */ 1214 public static boolean isFileEncryptedNativeOnly() { 1215 if (!isEncrypted()) { 1216 return false; 1217 } 1218 return RoSystemProperties.CRYPTO_FILE_ENCRYPTED; 1219 } 1220 1221 /** {@hide} 1222 * Is this device block encrypted? 1223 * @return true for block encrypted. (Implies isEncrypted() == true) 1224 * false not encrypted or file encrypted 1225 */ 1226 public static boolean isBlockEncrypted() { 1227 if (!isEncrypted()) { 1228 return false; 1229 } 1230 return RoSystemProperties.CRYPTO_BLOCK_ENCRYPTED; 1231 } 1232 1233 /** {@hide} 1234 * Is this device block encrypted with credentials? 1235 * @return true for crediential block encrypted. 1236 * (Implies isBlockEncrypted() == true) 1237 * false not encrypted, file encrypted or default block encrypted 1238 */ 1239 public static boolean isNonDefaultBlockEncrypted() { 1240 if (!isBlockEncrypted()) { 1241 return false; 1242 } 1243 1244 try { 1245 IStorageManager storageManager = IStorageManager.Stub.asInterface( 1246 ServiceManager.getService("mount")); 1247 return storageManager.getPasswordType() != CRYPT_TYPE_DEFAULT; 1248 } catch (RemoteException e) { 1249 Log.e(TAG, "Error getting encryption type"); 1250 return false; 1251 } 1252 } 1253 1254 /** {@hide} 1255 * Is this device in the process of being block encrypted? 1256 * @return true for encrypting. 1257 * false otherwise 1258 * Whether device isEncrypted at this point is undefined 1259 * Note that only system services and CryptKeeper will ever see this return 1260 * true - no app will ever be launched in this state. 1261 * Also note that this state will not change without a teardown of the 1262 * framework, so no service needs to check for changes during their lifespan 1263 */ 1264 public static boolean isBlockEncrypting() { 1265 final String state = SystemProperties.get("vold.encrypt_progress", ""); 1266 return !"".equalsIgnoreCase(state); 1267 } 1268 1269 /** {@hide} 1270 * Is this device non default block encrypted and in the process of 1271 * prompting for credentials? 1272 * @return true for prompting for credentials. 1273 * (Implies isNonDefaultBlockEncrypted() == true) 1274 * false otherwise 1275 * Note that only system services and CryptKeeper will ever see this return 1276 * true - no app will ever be launched in this state. 1277 * Also note that this state will not change without a teardown of the 1278 * framework, so no service needs to check for changes during their lifespan 1279 */ 1280 public static boolean inCryptKeeperBounce() { 1281 final String status = SystemProperties.get("vold.decrypt"); 1282 return "trigger_restart_min_framework".equals(status); 1283 } 1284 1285 /** {@hide} */ 1286 public static boolean isFileEncryptedEmulatedOnly() { 1287 return SystemProperties.getBoolean(StorageManager.PROP_EMULATE_FBE, false); 1288 } 1289 1290 /** {@hide} 1291 * Is this device running in a file encrypted mode, either native or emulated? 1292 * @return true for file encrypted, false otherwise 1293 */ 1294 public static boolean isFileEncryptedNativeOrEmulated() { 1295 return isFileEncryptedNativeOnly() 1296 || isFileEncryptedEmulatedOnly(); 1297 } 1298 1299 /** {@hide} */ 1300 public static File maybeTranslateEmulatedPathToInternal(File path) { 1301 final IStorageManager storageManager = IStorageManager.Stub.asInterface( 1302 ServiceManager.getService("mount")); 1303 try { 1304 final VolumeInfo[] vols = storageManager.getVolumes(0); 1305 for (VolumeInfo vol : vols) { 1306 if ((vol.getType() == VolumeInfo.TYPE_EMULATED 1307 || vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isMountedReadable()) { 1308 final File internalPath = FileUtils.rewriteAfterRename(vol.getPath(), 1309 vol.getInternalPath(), path); 1310 if (internalPath != null && internalPath.exists()) { 1311 return internalPath; 1312 } 1313 } 1314 } 1315 } catch (RemoteException e) { 1316 throw e.rethrowFromSystemServer(); 1317 } 1318 return path; 1319 } 1320 1321 /** {@hide} */ 1322 public ParcelFileDescriptor mountAppFuse(String name) { 1323 try { 1324 return mStorageManager.mountAppFuse(name); 1325 } catch (RemoteException e) { 1326 throw e.rethrowFromSystemServer(); 1327 } 1328 } 1329 1330 /** {@hide} */ 1331 @VisibleForTesting 1332 public @NonNull ParcelFileDescriptor openProxyFileDescriptor( 1333 int mode, ProxyFileDescriptorCallback callback, ThreadFactory factory) 1334 throws IOException { 1335 // Retry is needed because the mount point mFuseAppLoop is using may be unmounted before 1336 // invoking StorageManagerService#openProxyFileDescriptor. In this case, we need to re-mount 1337 // the bridge by calling mountProxyFileDescriptorBridge. 1338 int retry = 3; 1339 while (retry-- > 0) { 1340 try { 1341 synchronized (mFuseAppLoopLock) { 1342 if (mFuseAppLoop == null) { 1343 final AppFuseMount mount = mStorageManager.mountProxyFileDescriptorBridge(); 1344 if (mount == null) { 1345 Log.e(TAG, "Failed to open proxy file bridge."); 1346 throw new IOException("Failed to open proxy file bridge."); 1347 } 1348 mFuseAppLoop = FuseAppLoop.open(mount.mountPointId, mount.fd, factory); 1349 } 1350 1351 try { 1352 final int fileId = mFuseAppLoop.registerCallback(callback); 1353 final ParcelFileDescriptor pfd = 1354 mStorageManager.openProxyFileDescriptor( 1355 mFuseAppLoop.getMountPointId(), fileId, mode); 1356 if (pfd != null) { 1357 return pfd; 1358 } 1359 // Probably the bridge is being unmounted but mFuseAppLoop has not been 1360 // noticed it yet. 1361 mFuseAppLoop.unregisterCallback(fileId); 1362 } catch (FuseAppLoop.UnmountedException error) { 1363 Log.d(TAG, "mFuseAppLoop has been already unmounted."); 1364 mFuseAppLoop = null; 1365 continue; 1366 } 1367 } 1368 try { 1369 Thread.sleep(100); 1370 } catch (InterruptedException e) { 1371 break; 1372 } 1373 } catch (RemoteException e) { 1374 e.rethrowFromSystemServer(); 1375 } 1376 } 1377 1378 throw new IOException("Failed to mount bridge."); 1379 } 1380 1381 /** {@hide} */ 1382 public @NonNull ParcelFileDescriptor openProxyFileDescriptor( 1383 int mode, ProxyFileDescriptorCallback callback) 1384 throws IOException { 1385 return openProxyFileDescriptor(mode, callback, null); 1386 } 1387 1388 /** {@hide} */ 1389 @VisibleForTesting 1390 public int getProxyFileDescriptorMountPointId() { 1391 synchronized (mFuseAppLoopLock) { 1392 return mFuseAppLoop != null ? mFuseAppLoop.getMountPointId() : -1; 1393 } 1394 } 1395 1396 private final Object mFuseAppLoopLock = new Object(); 1397 1398 @GuardedBy("mFuseAppLoopLock") 1399 private @Nullable FuseAppLoop mFuseAppLoop = null; 1400 1401 /// Consts to match the password types in cryptfs.h 1402 /** @hide */ 1403 public static final int CRYPT_TYPE_PASSWORD = 0; 1404 /** @hide */ 1405 public static final int CRYPT_TYPE_DEFAULT = 1; 1406 /** @hide */ 1407 public static final int CRYPT_TYPE_PATTERN = 2; 1408 /** @hide */ 1409 public static final int CRYPT_TYPE_PIN = 3; 1410 1411 // Constants for the data available via StorageManagerService.getField. 1412 /** @hide */ 1413 public static final String SYSTEM_LOCALE_KEY = "SystemLocale"; 1414 /** @hide */ 1415 public static final String OWNER_INFO_KEY = "OwnerInfo"; 1416 /** @hide */ 1417 public static final String PATTERN_VISIBLE_KEY = "PatternVisible"; 1418 /** @hide */ 1419 public static final String PASSWORD_VISIBLE_KEY = "PasswordVisible"; 1420} 1421