StorageManager.java revision 789a8fc792725e4988ff43d554b3c8c037c41921
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.GB_IN_BYTES; 20import static android.net.TrafficStats.MB_IN_BYTES; 21 22import android.annotation.IntDef; 23import android.annotation.NonNull; 24import android.annotation.Nullable; 25import android.annotation.RequiresPermission; 26import android.annotation.SdkConstant; 27import android.app.ActivityThread; 28import android.content.ContentResolver; 29import android.content.Context; 30import android.content.pm.ApplicationInfo; 31import android.content.pm.IPackageMoveObserver; 32import android.content.pm.PackageManager; 33import android.os.Binder; 34import android.os.Environment; 35import android.os.FileUtils; 36import android.os.Handler; 37import android.os.Looper; 38import android.os.Message; 39import android.os.ParcelFileDescriptor; 40import android.os.ParcelableException; 41import android.os.ProxyFileDescriptorCallback; 42import android.os.RemoteException; 43import android.os.ServiceManager; 44import android.os.ServiceManager.ServiceNotFoundException; 45import android.os.SystemProperties; 46import android.os.UserHandle; 47import android.provider.Settings; 48import android.system.ErrnoException; 49import android.system.Os; 50import android.system.OsConstants; 51import android.text.TextUtils; 52import android.util.Log; 53import android.util.Pair; 54import android.util.Slog; 55import android.util.SparseArray; 56 57import com.android.internal.annotations.GuardedBy; 58import com.android.internal.annotations.VisibleForTesting; 59import com.android.internal.logging.MetricsLogger; 60import com.android.internal.os.AppFuseMount; 61import com.android.internal.os.FuseAppLoop; 62import com.android.internal.os.FuseUnavailableMountException; 63import com.android.internal.os.RoSystemProperties; 64import com.android.internal.os.SomeArgs; 65import com.android.internal.util.Preconditions; 66 67import java.io.File; 68import java.io.FileDescriptor; 69import java.io.FileNotFoundException; 70import java.io.IOException; 71import java.lang.annotation.Retention; 72import java.lang.annotation.RetentionPolicy; 73import java.lang.ref.WeakReference; 74import java.nio.charset.StandardCharsets; 75import java.util.ArrayList; 76import java.util.Arrays; 77import java.util.Collections; 78import java.util.Iterator; 79import java.util.List; 80import java.util.Objects; 81import java.util.UUID; 82import java.util.concurrent.ThreadFactory; 83import java.util.concurrent.atomic.AtomicInteger; 84 85/** 86 * StorageManager is the interface to the systems storage service. The storage 87 * manager handles storage-related items such as Opaque Binary Blobs (OBBs). 88 * <p> 89 * OBBs contain a filesystem that maybe be encrypted on disk and mounted 90 * on-demand from an application. OBBs are a good way of providing large amounts 91 * of binary assets without packaging them into APKs as they may be multiple 92 * gigabytes in size. However, due to their size, they're most likely stored in 93 * a shared storage pool accessible from all programs. The system does not 94 * guarantee the security of the OBB file itself: if any program modifies the 95 * OBB, there is no guarantee that a read from that OBB will produce the 96 * expected output. 97 * <p> 98 * Get an instance of this class by calling 99 * {@link android.content.Context#getSystemService(java.lang.String)} with an 100 * argument of {@link android.content.Context#STORAGE_SERVICE}. 101 */ 102public class StorageManager { 103 private static final String TAG = "StorageManager"; 104 105 /** {@hide} */ 106 public static final String PROP_PRIMARY_PHYSICAL = "ro.vold.primary_physical"; 107 /** {@hide} */ 108 public static final String PROP_HAS_ADOPTABLE = "vold.has_adoptable"; 109 /** {@hide} */ 110 public static final String PROP_FORCE_ADOPTABLE = "persist.fw.force_adoptable"; 111 /** {@hide} */ 112 public static final String PROP_EMULATE_FBE = "persist.sys.emulate_fbe"; 113 /** {@hide} */ 114 public static final String PROP_SDCARDFS = "persist.sys.sdcardfs"; 115 /** {@hide} */ 116 public static final String PROP_VIRTUAL_DISK = "persist.sys.virtual_disk"; 117 118 /** {@hide} */ 119 public static final String UUID_PRIVATE_INTERNAL = null; 120 /** {@hide} */ 121 public static final String UUID_PRIMARY_PHYSICAL = "primary_physical"; 122 /** {@hide} */ 123 public static final String UUID_SYSTEM = "system"; 124 125 // NOTE: UUID constants below are namespaced 126 // uuid -v5 ad99aa3d-308e-4191-a200-ebcab371c0ad default 127 // uuid -v5 ad99aa3d-308e-4191-a200-ebcab371c0ad primary_physical 128 // uuid -v5 ad99aa3d-308e-4191-a200-ebcab371c0ad system 129 130 /** 131 * UUID representing the default internal storage of this device which 132 * provides {@link Environment#getDataDirectory()}. 133 * <p> 134 * This value is constant across all devices and it will never change, and 135 * thus it cannot be used to uniquely identify a particular physical device. 136 * 137 * @see #getUuidForPath(File) 138 */ 139 public static final UUID UUID_DEFAULT = UUID 140 .fromString("41217664-9172-527a-b3d5-edabb50a7d69"); 141 142 /** {@hide} */ 143 public static final UUID UUID_PRIMARY_PHYSICAL_ = UUID 144 .fromString("0f95a519-dae7-5abf-9519-fbd6209e05fd"); 145 146 /** {@hide} */ 147 public static final UUID UUID_SYSTEM_ = UUID 148 .fromString("5d258386-e60d-59e3-826d-0089cdd42cc0"); 149 150 /** 151 * Activity Action: Allows the user to manage their storage. This activity 152 * provides the ability to free up space on the device by deleting data such 153 * as apps. 154 * <p> 155 * If the sending application has a specific storage device or allocation 156 * size in mind, they can optionally define {@link #EXTRA_UUID} or 157 * {@link #EXTRA_REQUESTED_BYTES}, respectively. 158 */ 159 @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) 160 public static final String ACTION_MANAGE_STORAGE = "android.os.storage.action.MANAGE_STORAGE"; 161 162 /** 163 * Extra {@link UUID} used to indicate the storage volume where an 164 * application is interested in allocating or managing disk space. 165 * 166 * @see #ACTION_MANAGE_STORAGE 167 * @see #UUID_DEFAULT 168 * @see #getUuidForPath(File) 169 */ 170 public static final String EXTRA_UUID = "android.os.storage.extra.UUID"; 171 172 /** 173 * Extra used to indicate the total size (in bytes) that an application is 174 * interested in allocating. 175 * <p> 176 * When defined, the management UI will help guide the user to free up 177 * enough disk space to reach this requested value. 178 * 179 * @see #ACTION_MANAGE_STORAGE 180 */ 181 public static final String EXTRA_REQUESTED_BYTES = "android.os.storage.extra.REQUESTED_BYTES"; 182 183 /** {@hide} */ 184 public static final int DEBUG_FORCE_ADOPTABLE = 1 << 0; 185 /** {@hide} */ 186 public static final int DEBUG_EMULATE_FBE = 1 << 1; 187 /** {@hide} */ 188 public static final int DEBUG_SDCARDFS_FORCE_ON = 1 << 2; 189 /** {@hide} */ 190 public static final int DEBUG_SDCARDFS_FORCE_OFF = 1 << 3; 191 /** {@hide} */ 192 public static final int DEBUG_VIRTUAL_DISK = 1 << 4; 193 194 // NOTE: keep in sync with installd 195 /** {@hide} */ 196 public static final int FLAG_STORAGE_DE = 1 << 0; 197 /** {@hide} */ 198 public static final int FLAG_STORAGE_CE = 1 << 1; 199 200 /** {@hide} */ 201 public static final int FLAG_FOR_WRITE = 1 << 8; 202 /** {@hide} */ 203 public static final int FLAG_REAL_STATE = 1 << 9; 204 /** {@hide} */ 205 public static final int FLAG_INCLUDE_INVISIBLE = 1 << 10; 206 207 /** {@hide} */ 208 public static final int FSTRIM_FLAG_DEEP = 1 << 0; 209 /** {@hide} */ 210 public static final int FSTRIM_FLAG_BENCHMARK = 1 << 1; 211 212 /** @hide The volume is not encrypted. */ 213 public static final int ENCRYPTION_STATE_NONE = 1; 214 215 /** @hide The volume has been encrypted succesfully. */ 216 public static final int ENCRYPTION_STATE_OK = 0; 217 218 /** @hide The volume is in a bad state.*/ 219 public static final int ENCRYPTION_STATE_ERROR_UNKNOWN = -1; 220 221 /** @hide Encryption is incomplete */ 222 public static final int ENCRYPTION_STATE_ERROR_INCOMPLETE = -2; 223 224 /** @hide Encryption is incomplete and irrecoverable */ 225 public static final int ENCRYPTION_STATE_ERROR_INCONSISTENT = -3; 226 227 /** @hide Underlying data is corrupt */ 228 public static final int ENCRYPTION_STATE_ERROR_CORRUPT = -4; 229 230 private static volatile IStorageManager sStorageManager = null; 231 232 private final Context mContext; 233 private final ContentResolver mResolver; 234 235 private final IStorageManager mStorageManager; 236 private final Looper mLooper; 237 private final AtomicInteger mNextNonce = new AtomicInteger(0); 238 239 private final ArrayList<StorageEventListenerDelegate> mDelegates = new ArrayList<>(); 240 241 private static class StorageEventListenerDelegate extends IStorageEventListener.Stub implements 242 Handler.Callback { 243 private static final int MSG_STORAGE_STATE_CHANGED = 1; 244 private static final int MSG_VOLUME_STATE_CHANGED = 2; 245 private static final int MSG_VOLUME_RECORD_CHANGED = 3; 246 private static final int MSG_VOLUME_FORGOTTEN = 4; 247 private static final int MSG_DISK_SCANNED = 5; 248 private static final int MSG_DISK_DESTROYED = 6; 249 250 final StorageEventListener mCallback; 251 final Handler mHandler; 252 253 public StorageEventListenerDelegate(StorageEventListener callback, Looper looper) { 254 mCallback = callback; 255 mHandler = new Handler(looper, this); 256 } 257 258 @Override 259 public boolean handleMessage(Message msg) { 260 final SomeArgs args = (SomeArgs) msg.obj; 261 switch (msg.what) { 262 case MSG_STORAGE_STATE_CHANGED: 263 mCallback.onStorageStateChanged((String) args.arg1, (String) args.arg2, 264 (String) args.arg3); 265 args.recycle(); 266 return true; 267 case MSG_VOLUME_STATE_CHANGED: 268 mCallback.onVolumeStateChanged((VolumeInfo) args.arg1, args.argi2, args.argi3); 269 args.recycle(); 270 return true; 271 case MSG_VOLUME_RECORD_CHANGED: 272 mCallback.onVolumeRecordChanged((VolumeRecord) args.arg1); 273 args.recycle(); 274 return true; 275 case MSG_VOLUME_FORGOTTEN: 276 mCallback.onVolumeForgotten((String) args.arg1); 277 args.recycle(); 278 return true; 279 case MSG_DISK_SCANNED: 280 mCallback.onDiskScanned((DiskInfo) args.arg1, args.argi2); 281 args.recycle(); 282 return true; 283 case MSG_DISK_DESTROYED: 284 mCallback.onDiskDestroyed((DiskInfo) args.arg1); 285 args.recycle(); 286 return true; 287 } 288 args.recycle(); 289 return false; 290 } 291 292 @Override 293 public void onUsbMassStorageConnectionChanged(boolean connected) throws RemoteException { 294 // Ignored 295 } 296 297 @Override 298 public void onStorageStateChanged(String path, String oldState, String newState) { 299 final SomeArgs args = SomeArgs.obtain(); 300 args.arg1 = path; 301 args.arg2 = oldState; 302 args.arg3 = newState; 303 mHandler.obtainMessage(MSG_STORAGE_STATE_CHANGED, args).sendToTarget(); 304 } 305 306 @Override 307 public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) { 308 final SomeArgs args = SomeArgs.obtain(); 309 args.arg1 = vol; 310 args.argi2 = oldState; 311 args.argi3 = newState; 312 mHandler.obtainMessage(MSG_VOLUME_STATE_CHANGED, args).sendToTarget(); 313 } 314 315 @Override 316 public void onVolumeRecordChanged(VolumeRecord rec) { 317 final SomeArgs args = SomeArgs.obtain(); 318 args.arg1 = rec; 319 mHandler.obtainMessage(MSG_VOLUME_RECORD_CHANGED, args).sendToTarget(); 320 } 321 322 @Override 323 public void onVolumeForgotten(String fsUuid) { 324 final SomeArgs args = SomeArgs.obtain(); 325 args.arg1 = fsUuid; 326 mHandler.obtainMessage(MSG_VOLUME_FORGOTTEN, args).sendToTarget(); 327 } 328 329 @Override 330 public void onDiskScanned(DiskInfo disk, int volumeCount) { 331 final SomeArgs args = SomeArgs.obtain(); 332 args.arg1 = disk; 333 args.argi2 = volumeCount; 334 mHandler.obtainMessage(MSG_DISK_SCANNED, args).sendToTarget(); 335 } 336 337 @Override 338 public void onDiskDestroyed(DiskInfo disk) throws RemoteException { 339 final SomeArgs args = SomeArgs.obtain(); 340 args.arg1 = disk; 341 mHandler.obtainMessage(MSG_DISK_DESTROYED, args).sendToTarget(); 342 } 343 } 344 345 /** 346 * Binder listener for OBB action results. 347 */ 348 private final ObbActionListener mObbActionListener = new ObbActionListener(); 349 350 private class ObbActionListener extends IObbActionListener.Stub { 351 @SuppressWarnings("hiding") 352 private SparseArray<ObbListenerDelegate> mListeners = new SparseArray<ObbListenerDelegate>(); 353 354 @Override 355 public void onObbResult(String filename, int nonce, int status) { 356 final ObbListenerDelegate delegate; 357 synchronized (mListeners) { 358 delegate = mListeners.get(nonce); 359 if (delegate != null) { 360 mListeners.remove(nonce); 361 } 362 } 363 364 if (delegate != null) { 365 delegate.sendObbStateChanged(filename, status); 366 } 367 } 368 369 public int addListener(OnObbStateChangeListener listener) { 370 final ObbListenerDelegate delegate = new ObbListenerDelegate(listener); 371 372 synchronized (mListeners) { 373 mListeners.put(delegate.nonce, delegate); 374 } 375 376 return delegate.nonce; 377 } 378 } 379 380 private int getNextNonce() { 381 return mNextNonce.getAndIncrement(); 382 } 383 384 /** 385 * Private class containing sender and receiver code for StorageEvents. 386 */ 387 private class ObbListenerDelegate { 388 private final WeakReference<OnObbStateChangeListener> mObbEventListenerRef; 389 private final Handler mHandler; 390 391 private final int nonce; 392 393 ObbListenerDelegate(OnObbStateChangeListener listener) { 394 nonce = getNextNonce(); 395 mObbEventListenerRef = new WeakReference<OnObbStateChangeListener>(listener); 396 mHandler = new Handler(mLooper) { 397 @Override 398 public void handleMessage(Message msg) { 399 final OnObbStateChangeListener changeListener = getListener(); 400 if (changeListener == null) { 401 return; 402 } 403 404 changeListener.onObbStateChange((String) msg.obj, msg.arg1); 405 } 406 }; 407 } 408 409 OnObbStateChangeListener getListener() { 410 if (mObbEventListenerRef == null) { 411 return null; 412 } 413 return mObbEventListenerRef.get(); 414 } 415 416 void sendObbStateChanged(String path, int state) { 417 mHandler.obtainMessage(0, state, 0, path).sendToTarget(); 418 } 419 } 420 421 /** {@hide} */ 422 @Deprecated 423 public static StorageManager from(Context context) { 424 return context.getSystemService(StorageManager.class); 425 } 426 427 /** 428 * Constructs a StorageManager object through which an application can 429 * can communicate with the systems mount service. 430 * 431 * @param looper The {@link android.os.Looper} which events will be received on. 432 * 433 * <p>Applications can get instance of this class by calling 434 * {@link android.content.Context#getSystemService(java.lang.String)} with an argument 435 * of {@link android.content.Context#STORAGE_SERVICE}. 436 * 437 * @hide 438 */ 439 public StorageManager(Context context, Looper looper) throws ServiceNotFoundException { 440 mContext = context; 441 mResolver = context.getContentResolver(); 442 mLooper = looper; 443 mStorageManager = IStorageManager.Stub.asInterface(ServiceManager.getServiceOrThrow("mount")); 444 } 445 446 /** 447 * Registers a {@link android.os.storage.StorageEventListener StorageEventListener}. 448 * 449 * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object. 450 * 451 * @hide 452 */ 453 public void registerListener(StorageEventListener listener) { 454 synchronized (mDelegates) { 455 final StorageEventListenerDelegate delegate = new StorageEventListenerDelegate(listener, 456 mLooper); 457 try { 458 mStorageManager.registerListener(delegate); 459 } catch (RemoteException e) { 460 throw e.rethrowFromSystemServer(); 461 } 462 mDelegates.add(delegate); 463 } 464 } 465 466 /** 467 * Unregisters a {@link android.os.storage.StorageEventListener StorageEventListener}. 468 * 469 * @param listener A {@link android.os.storage.StorageEventListener StorageEventListener} object. 470 * 471 * @hide 472 */ 473 public void unregisterListener(StorageEventListener listener) { 474 synchronized (mDelegates) { 475 for (Iterator<StorageEventListenerDelegate> i = mDelegates.iterator(); i.hasNext();) { 476 final StorageEventListenerDelegate delegate = i.next(); 477 if (delegate.mCallback == listener) { 478 try { 479 mStorageManager.unregisterListener(delegate); 480 } catch (RemoteException e) { 481 throw e.rethrowFromSystemServer(); 482 } 483 i.remove(); 484 } 485 } 486 } 487 } 488 489 /** 490 * Enables USB Mass Storage (UMS) on the device. 491 * 492 * @hide 493 */ 494 @Deprecated 495 public void enableUsbMassStorage() { 496 } 497 498 /** 499 * Disables USB Mass Storage (UMS) on the device. 500 * 501 * @hide 502 */ 503 @Deprecated 504 public void disableUsbMassStorage() { 505 } 506 507 /** 508 * Query if a USB Mass Storage (UMS) host is connected. 509 * @return true if UMS host is connected. 510 * 511 * @hide 512 */ 513 @Deprecated 514 public boolean isUsbMassStorageConnected() { 515 return false; 516 } 517 518 /** 519 * Query if a USB Mass Storage (UMS) is enabled on the device. 520 * @return true if UMS host is enabled. 521 * 522 * @hide 523 */ 524 @Deprecated 525 public boolean isUsbMassStorageEnabled() { 526 return false; 527 } 528 529 /** 530 * Mount an Opaque Binary Blob (OBB) file. If a <code>key</code> is 531 * specified, it is supplied to the mounting process to be used in any 532 * encryption used in the OBB. 533 * <p> 534 * The OBB will remain mounted for as long as the StorageManager reference 535 * is held by the application. As soon as this reference is lost, the OBBs 536 * in use will be unmounted. The {@link OnObbStateChangeListener} registered 537 * with this call will receive the success or failure of this operation. 538 * <p> 539 * <em>Note:</em> you can only mount OBB files for which the OBB tag on the 540 * file matches a package ID that is owned by the calling program's UID. 541 * That is, shared UID applications can attempt to mount any other 542 * application's OBB that shares its UID. 543 * 544 * @param rawPath the path to the OBB file 545 * @param key secret used to encrypt the OBB; may be <code>null</code> if no 546 * encryption was used on the OBB. 547 * @param listener will receive the success or failure of the operation 548 * @return whether the mount call was successfully queued or not 549 */ 550 public boolean mountObb(String rawPath, String key, OnObbStateChangeListener listener) { 551 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 552 Preconditions.checkNotNull(listener, "listener cannot be null"); 553 554 try { 555 final String canonicalPath = new File(rawPath).getCanonicalPath(); 556 final int nonce = mObbActionListener.addListener(listener); 557 mStorageManager.mountObb(rawPath, canonicalPath, key, mObbActionListener, nonce); 558 return true; 559 } catch (IOException e) { 560 throw new IllegalArgumentException("Failed to resolve path: " + rawPath, e); 561 } catch (RemoteException e) { 562 throw e.rethrowFromSystemServer(); 563 } 564 } 565 566 /** 567 * Unmount an Opaque Binary Blob (OBB) file asynchronously. If the 568 * <code>force</code> flag is true, it will kill any application needed to 569 * unmount the given OBB (even the calling application). 570 * <p> 571 * The {@link OnObbStateChangeListener} registered with this call will 572 * receive the success or failure of this operation. 573 * <p> 574 * <em>Note:</em> you can only mount OBB files for which the OBB tag on the 575 * file matches a package ID that is owned by the calling program's UID. 576 * That is, shared UID applications can obtain access to any other 577 * application's OBB that shares its UID. 578 * <p> 579 * 580 * @param rawPath path to the OBB file 581 * @param force whether to kill any programs using this in order to unmount 582 * it 583 * @param listener will receive the success or failure of the operation 584 * @return whether the unmount call was successfully queued or not 585 */ 586 public boolean unmountObb(String rawPath, boolean force, OnObbStateChangeListener listener) { 587 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 588 Preconditions.checkNotNull(listener, "listener cannot be null"); 589 590 try { 591 final int nonce = mObbActionListener.addListener(listener); 592 mStorageManager.unmountObb(rawPath, force, mObbActionListener, nonce); 593 return true; 594 } catch (RemoteException e) { 595 throw e.rethrowFromSystemServer(); 596 } 597 } 598 599 /** 600 * Check whether an Opaque Binary Blob (OBB) is mounted or not. 601 * 602 * @param rawPath path to OBB image 603 * @return true if OBB is mounted; false if not mounted or on error 604 */ 605 public boolean isObbMounted(String rawPath) { 606 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 607 608 try { 609 return mStorageManager.isObbMounted(rawPath); 610 } catch (RemoteException e) { 611 throw e.rethrowFromSystemServer(); 612 } 613 } 614 615 /** 616 * Check the mounted path of an Opaque Binary Blob (OBB) file. This will 617 * give you the path to where you can obtain access to the internals of the 618 * OBB. 619 * 620 * @param rawPath path to OBB image 621 * @return absolute path to mounted OBB image data or <code>null</code> if 622 * not mounted or exception encountered trying to read status 623 */ 624 public String getMountedObbPath(String rawPath) { 625 Preconditions.checkNotNull(rawPath, "rawPath cannot be null"); 626 627 try { 628 return mStorageManager.getMountedObbPath(rawPath); 629 } catch (RemoteException e) { 630 throw e.rethrowFromSystemServer(); 631 } 632 } 633 634 /** {@hide} */ 635 public @NonNull List<DiskInfo> getDisks() { 636 try { 637 return Arrays.asList(mStorageManager.getDisks()); 638 } catch (RemoteException e) { 639 throw e.rethrowFromSystemServer(); 640 } 641 } 642 643 /** {@hide} */ 644 public @Nullable DiskInfo findDiskById(String id) { 645 Preconditions.checkNotNull(id); 646 // TODO; go directly to service to make this faster 647 for (DiskInfo disk : getDisks()) { 648 if (Objects.equals(disk.id, id)) { 649 return disk; 650 } 651 } 652 return null; 653 } 654 655 /** {@hide} */ 656 public @Nullable VolumeInfo findVolumeById(String id) { 657 Preconditions.checkNotNull(id); 658 // TODO; go directly to service to make this faster 659 for (VolumeInfo vol : getVolumes()) { 660 if (Objects.equals(vol.id, id)) { 661 return vol; 662 } 663 } 664 return null; 665 } 666 667 /** {@hide} */ 668 public @Nullable VolumeInfo findVolumeByUuid(String fsUuid) { 669 Preconditions.checkNotNull(fsUuid); 670 // TODO; go directly to service to make this faster 671 for (VolumeInfo vol : getVolumes()) { 672 if (Objects.equals(vol.fsUuid, fsUuid)) { 673 return vol; 674 } 675 } 676 return null; 677 } 678 679 /** {@hide} */ 680 public @Nullable VolumeRecord findRecordByUuid(String fsUuid) { 681 Preconditions.checkNotNull(fsUuid); 682 // TODO; go directly to service to make this faster 683 for (VolumeRecord rec : getVolumeRecords()) { 684 if (Objects.equals(rec.fsUuid, fsUuid)) { 685 return rec; 686 } 687 } 688 return null; 689 } 690 691 /** {@hide} */ 692 public @Nullable VolumeInfo findPrivateForEmulated(VolumeInfo emulatedVol) { 693 if (emulatedVol != null) { 694 return findVolumeById(emulatedVol.getId().replace("emulated", "private")); 695 } else { 696 return null; 697 } 698 } 699 700 /** {@hide} */ 701 public @Nullable VolumeInfo findEmulatedForPrivate(VolumeInfo privateVol) { 702 if (privateVol != null) { 703 return findVolumeById(privateVol.getId().replace("private", "emulated")); 704 } else { 705 return null; 706 } 707 } 708 709 /** {@hide} */ 710 public @Nullable VolumeInfo findVolumeByQualifiedUuid(String volumeUuid) { 711 if (Objects.equals(StorageManager.UUID_PRIVATE_INTERNAL, volumeUuid)) { 712 return findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL); 713 } else if (Objects.equals(StorageManager.UUID_PRIMARY_PHYSICAL, volumeUuid)) { 714 return getPrimaryPhysicalVolume(); 715 } else { 716 return findVolumeByUuid(volumeUuid); 717 } 718 } 719 720 /** 721 * Return a UUID identifying the storage volume that hosts the given 722 * filesystem path. 723 * <p> 724 * If this path is hosted by the default internal storage of the device at 725 * {@link Environment#getDataDirectory()}, the returned value will be 726 * {@link #UUID_DEFAULT}. 727 * 728 * @throws IOException when the storage device at the given path isn't 729 * present, or when it doesn't have a valid UUID. 730 */ 731 public @NonNull UUID getUuidForPath(@NonNull File path) throws IOException { 732 Preconditions.checkNotNull(path); 733 final String pathString = path.getAbsolutePath(); 734 if (FileUtils.contains(Environment.getDataDirectory().getAbsolutePath(), pathString)) { 735 return UUID_DEFAULT; 736 } 737 try { 738 for (VolumeInfo vol : mStorageManager.getVolumes(0)) { 739 if (vol.path != null && FileUtils.contains(vol.path, pathString)) { 740 // TODO: verify that emulated adopted devices have UUID of 741 // underlying volume 742 return convert(vol.fsUuid); 743 } 744 } 745 } catch (RemoteException e) { 746 throw e.rethrowFromSystemServer(); 747 } 748 throw new FileNotFoundException("Failed to find a storage device for " + path); 749 } 750 751 /** {@hide} */ 752 public @NonNull File findPathForUuid(String volumeUuid) throws FileNotFoundException { 753 final VolumeInfo vol = findVolumeByQualifiedUuid(volumeUuid); 754 if (vol != null) { 755 return vol.getPath(); 756 } 757 throw new FileNotFoundException("Failed to find a storage device for " + volumeUuid); 758 } 759 760 /** {@hide} */ 761 public @NonNull List<VolumeInfo> getVolumes() { 762 try { 763 return Arrays.asList(mStorageManager.getVolumes(0)); 764 } catch (RemoteException e) { 765 throw e.rethrowFromSystemServer(); 766 } 767 } 768 769 /** {@hide} */ 770 public @NonNull List<VolumeInfo> getWritablePrivateVolumes() { 771 try { 772 final ArrayList<VolumeInfo> res = new ArrayList<>(); 773 for (VolumeInfo vol : mStorageManager.getVolumes(0)) { 774 if (vol.getType() == VolumeInfo.TYPE_PRIVATE && vol.isMountedWritable()) { 775 res.add(vol); 776 } 777 } 778 return res; 779 } catch (RemoteException e) { 780 throw e.rethrowFromSystemServer(); 781 } 782 } 783 784 /** {@hide} */ 785 public @NonNull List<VolumeRecord> getVolumeRecords() { 786 try { 787 return Arrays.asList(mStorageManager.getVolumeRecords(0)); 788 } catch (RemoteException e) { 789 throw e.rethrowFromSystemServer(); 790 } 791 } 792 793 /** {@hide} */ 794 public @Nullable String getBestVolumeDescription(VolumeInfo vol) { 795 if (vol == null) return null; 796 797 // Nickname always takes precedence when defined 798 if (!TextUtils.isEmpty(vol.fsUuid)) { 799 final VolumeRecord rec = findRecordByUuid(vol.fsUuid); 800 if (rec != null && !TextUtils.isEmpty(rec.nickname)) { 801 return rec.nickname; 802 } 803 } 804 805 if (!TextUtils.isEmpty(vol.getDescription())) { 806 return vol.getDescription(); 807 } 808 809 if (vol.disk != null) { 810 return vol.disk.getDescription(); 811 } 812 813 return null; 814 } 815 816 /** {@hide} */ 817 public @Nullable VolumeInfo getPrimaryPhysicalVolume() { 818 final List<VolumeInfo> vols = getVolumes(); 819 for (VolumeInfo vol : vols) { 820 if (vol.isPrimaryPhysical()) { 821 return vol; 822 } 823 } 824 return null; 825 } 826 827 /** {@hide} */ 828 public void mount(String volId) { 829 try { 830 mStorageManager.mount(volId); 831 } catch (RemoteException e) { 832 throw e.rethrowFromSystemServer(); 833 } 834 } 835 836 /** {@hide} */ 837 public void unmount(String volId) { 838 try { 839 mStorageManager.unmount(volId); 840 } catch (RemoteException e) { 841 throw e.rethrowFromSystemServer(); 842 } 843 } 844 845 /** {@hide} */ 846 public void format(String volId) { 847 try { 848 mStorageManager.format(volId); 849 } catch (RemoteException e) { 850 throw e.rethrowFromSystemServer(); 851 } 852 } 853 854 /** {@hide} */ 855 public long benchmark(String volId) { 856 try { 857 return mStorageManager.benchmark(volId); 858 } catch (RemoteException e) { 859 throw e.rethrowFromSystemServer(); 860 } 861 } 862 863 /** {@hide} */ 864 public void partitionPublic(String diskId) { 865 try { 866 mStorageManager.partitionPublic(diskId); 867 } catch (RemoteException e) { 868 throw e.rethrowFromSystemServer(); 869 } 870 } 871 872 /** {@hide} */ 873 public void partitionPrivate(String diskId) { 874 try { 875 mStorageManager.partitionPrivate(diskId); 876 } catch (RemoteException e) { 877 throw e.rethrowFromSystemServer(); 878 } 879 } 880 881 /** {@hide} */ 882 public void partitionMixed(String diskId, int ratio) { 883 try { 884 mStorageManager.partitionMixed(diskId, ratio); 885 } catch (RemoteException e) { 886 throw e.rethrowFromSystemServer(); 887 } 888 } 889 890 /** {@hide} */ 891 public void wipeAdoptableDisks() { 892 // We only wipe devices in "adoptable" locations, which are in a 893 // long-term stable slot/location on the device, where apps have a 894 // reasonable chance of storing sensitive data. (Apps need to go through 895 // SAF to write to transient volumes.) 896 final List<DiskInfo> disks = getDisks(); 897 for (DiskInfo disk : disks) { 898 final String diskId = disk.getId(); 899 if (disk.isAdoptable()) { 900 Slog.d(TAG, "Found adoptable " + diskId + "; wiping"); 901 try { 902 // TODO: switch to explicit wipe command when we have it, 903 // for now rely on the fact that vfat format does a wipe 904 mStorageManager.partitionPublic(diskId); 905 } catch (Exception e) { 906 Slog.w(TAG, "Failed to wipe " + diskId + ", but soldiering onward", e); 907 } 908 } else { 909 Slog.d(TAG, "Ignorning non-adoptable disk " + disk.getId()); 910 } 911 } 912 } 913 914 /** {@hide} */ 915 public void setVolumeNickname(String fsUuid, String nickname) { 916 try { 917 mStorageManager.setVolumeNickname(fsUuid, nickname); 918 } catch (RemoteException e) { 919 throw e.rethrowFromSystemServer(); 920 } 921 } 922 923 /** {@hide} */ 924 public void setVolumeInited(String fsUuid, boolean inited) { 925 try { 926 mStorageManager.setVolumeUserFlags(fsUuid, inited ? VolumeRecord.USER_FLAG_INITED : 0, 927 VolumeRecord.USER_FLAG_INITED); 928 } catch (RemoteException e) { 929 throw e.rethrowFromSystemServer(); 930 } 931 } 932 933 /** {@hide} */ 934 public void setVolumeSnoozed(String fsUuid, boolean snoozed) { 935 try { 936 mStorageManager.setVolumeUserFlags(fsUuid, snoozed ? VolumeRecord.USER_FLAG_SNOOZED : 0, 937 VolumeRecord.USER_FLAG_SNOOZED); 938 } catch (RemoteException e) { 939 throw e.rethrowFromSystemServer(); 940 } 941 } 942 943 /** {@hide} */ 944 public void forgetVolume(String fsUuid) { 945 try { 946 mStorageManager.forgetVolume(fsUuid); 947 } catch (RemoteException e) { 948 throw e.rethrowFromSystemServer(); 949 } 950 } 951 952 /** 953 * This is not the API you're looking for. 954 * 955 * @see PackageManager#getPrimaryStorageCurrentVolume() 956 * @hide 957 */ 958 public String getPrimaryStorageUuid() { 959 try { 960 return mStorageManager.getPrimaryStorageUuid(); 961 } catch (RemoteException e) { 962 throw e.rethrowFromSystemServer(); 963 } 964 } 965 966 /** 967 * This is not the API you're looking for. 968 * 969 * @see PackageManager#movePrimaryStorage(VolumeInfo) 970 * @hide 971 */ 972 public void setPrimaryStorageUuid(String volumeUuid, IPackageMoveObserver callback) { 973 try { 974 mStorageManager.setPrimaryStorageUuid(volumeUuid, callback); 975 } catch (RemoteException e) { 976 throw e.rethrowFromSystemServer(); 977 } 978 } 979 980 /** 981 * Return the {@link StorageVolume} that contains the given file, or {@code null} if none. 982 */ 983 public @Nullable StorageVolume getStorageVolume(File file) { 984 return getStorageVolume(getVolumeList(), file); 985 } 986 987 /** {@hide} */ 988 public static @Nullable StorageVolume getStorageVolume(File file, int userId) { 989 return getStorageVolume(getVolumeList(userId, 0), file); 990 } 991 992 /** {@hide} */ 993 private static @Nullable StorageVolume getStorageVolume(StorageVolume[] volumes, File file) { 994 if (file == null) { 995 return null; 996 } 997 try { 998 file = file.getCanonicalFile(); 999 } catch (IOException ignored) { 1000 Slog.d(TAG, "Could not get canonical path for " + file); 1001 return null; 1002 } 1003 for (StorageVolume volume : volumes) { 1004 File volumeFile = volume.getPathFile(); 1005 try { 1006 volumeFile = volumeFile.getCanonicalFile(); 1007 } catch (IOException ignored) { 1008 continue; 1009 } 1010 if (FileUtils.contains(volumeFile, file)) { 1011 return volume; 1012 } 1013 } 1014 return null; 1015 } 1016 1017 /** 1018 * Gets the state of a volume via its mountpoint. 1019 * @hide 1020 */ 1021 @Deprecated 1022 public @NonNull String getVolumeState(String mountPoint) { 1023 final StorageVolume vol = getStorageVolume(new File(mountPoint)); 1024 if (vol != null) { 1025 return vol.getState(); 1026 } else { 1027 return Environment.MEDIA_UNKNOWN; 1028 } 1029 } 1030 1031 /** 1032 * Return the list of shared/external storage volumes available to the 1033 * current user. This includes both the primary shared storage device and 1034 * any attached external volumes including SD cards and USB drives. 1035 * 1036 * @see Environment#getExternalStorageDirectory() 1037 * @see StorageVolume#createAccessIntent(String) 1038 */ 1039 public @NonNull List<StorageVolume> getStorageVolumes() { 1040 final ArrayList<StorageVolume> res = new ArrayList<>(); 1041 Collections.addAll(res, 1042 getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE)); 1043 return res; 1044 } 1045 1046 /** 1047 * Return the primary shared/external storage volume available to the 1048 * current user. This volume is the same storage device returned by 1049 * {@link Environment#getExternalStorageDirectory()} and 1050 * {@link Context#getExternalFilesDir(String)}. 1051 */ 1052 public @NonNull StorageVolume getPrimaryStorageVolume() { 1053 return getVolumeList(UserHandle.myUserId(), FLAG_REAL_STATE | FLAG_INCLUDE_INVISIBLE)[0]; 1054 } 1055 1056 /** {@hide} */ 1057 public static Pair<String, Long> getPrimaryStoragePathAndSize() { 1058 return Pair.create(null, 1059 FileUtils.roundStorageSize(Environment.getDataDirectory().getTotalSpace())); 1060 } 1061 1062 /** {@hide} */ 1063 public long getPrimaryStorageSize() { 1064 return FileUtils.roundStorageSize(Environment.getDataDirectory().getTotalSpace()); 1065 } 1066 1067 /** @removed */ 1068 public @NonNull StorageVolume[] getVolumeList() { 1069 return getVolumeList(mContext.getUserId(), 0); 1070 } 1071 1072 /** {@hide} */ 1073 public static @NonNull StorageVolume[] getVolumeList(int userId, int flags) { 1074 final IStorageManager storageManager = IStorageManager.Stub.asInterface( 1075 ServiceManager.getService("mount")); 1076 try { 1077 String packageName = ActivityThread.currentOpPackageName(); 1078 if (packageName == null) { 1079 // Package name can be null if the activity thread is running but the app 1080 // hasn't bound yet. In this case we fall back to the first package in the 1081 // current UID. This works for runtime permissions as permission state is 1082 // per UID and permission realted app ops are updated for all UID packages. 1083 String[] packageNames = ActivityThread.getPackageManager().getPackagesForUid( 1084 android.os.Process.myUid()); 1085 if (packageNames == null || packageNames.length <= 0) { 1086 return new StorageVolume[0]; 1087 } 1088 packageName = packageNames[0]; 1089 } 1090 final int uid = ActivityThread.getPackageManager().getPackageUid(packageName, 1091 PackageManager.MATCH_DEBUG_TRIAGED_MISSING, userId); 1092 if (uid <= 0) { 1093 return new StorageVolume[0]; 1094 } 1095 return storageManager.getVolumeList(uid, packageName, flags); 1096 } catch (RemoteException e) { 1097 throw e.rethrowFromSystemServer(); 1098 } 1099 } 1100 1101 /** 1102 * Returns list of paths for all mountable volumes. 1103 * @hide 1104 */ 1105 @Deprecated 1106 public @NonNull String[] getVolumePaths() { 1107 StorageVolume[] volumes = getVolumeList(); 1108 int count = volumes.length; 1109 String[] paths = new String[count]; 1110 for (int i = 0; i < count; i++) { 1111 paths[i] = volumes[i].getPath(); 1112 } 1113 return paths; 1114 } 1115 1116 /** @removed */ 1117 public @NonNull StorageVolume getPrimaryVolume() { 1118 return getPrimaryVolume(getVolumeList()); 1119 } 1120 1121 /** {@hide} */ 1122 public static @NonNull StorageVolume getPrimaryVolume(StorageVolume[] volumes) { 1123 for (StorageVolume volume : volumes) { 1124 if (volume.isPrimary()) { 1125 return volume; 1126 } 1127 } 1128 throw new IllegalStateException("Missing primary storage"); 1129 } 1130 1131 private static final int DEFAULT_THRESHOLD_PERCENTAGE = 5; 1132 private static final long DEFAULT_THRESHOLD_MAX_BYTES = 500 * MB_IN_BYTES; 1133 1134 private static final int DEFAULT_CACHE_PERCENTAGE = 10; 1135 private static final long DEFAULT_CACHE_MAX_BYTES = 5 * GB_IN_BYTES; 1136 1137 private static final long DEFAULT_FULL_THRESHOLD_BYTES = MB_IN_BYTES; 1138 1139 /** 1140 * Return the number of available bytes until the given path is considered 1141 * running low on storage. 1142 * 1143 * @hide 1144 */ 1145 public long getStorageBytesUntilLow(File path) { 1146 return path.getUsableSpace() - getStorageFullBytes(path); 1147 } 1148 1149 /** 1150 * Return the number of available bytes at which the given path is 1151 * considered running low on storage. 1152 * 1153 * @hide 1154 */ 1155 public long getStorageLowBytes(File path) { 1156 final long lowPercent = Settings.Global.getInt(mResolver, 1157 Settings.Global.SYS_STORAGE_THRESHOLD_PERCENTAGE, DEFAULT_THRESHOLD_PERCENTAGE); 1158 final long lowBytes = (path.getTotalSpace() * lowPercent) / 100; 1159 1160 final long maxLowBytes = Settings.Global.getLong(mResolver, 1161 Settings.Global.SYS_STORAGE_THRESHOLD_MAX_BYTES, DEFAULT_THRESHOLD_MAX_BYTES); 1162 1163 return Math.min(lowBytes, maxLowBytes); 1164 } 1165 1166 /** 1167 * Return the minimum number of bytes of storage on the device that should 1168 * be reserved for cached data. 1169 * 1170 * @hide 1171 */ 1172 public long getStorageCacheBytes(File path) { 1173 final long cachePercent = Settings.Global.getInt(mResolver, 1174 Settings.Global.SYS_STORAGE_CACHE_PERCENTAGE, DEFAULT_CACHE_PERCENTAGE); 1175 final long cacheBytes = (path.getTotalSpace() * cachePercent) / 100; 1176 1177 final long maxCacheBytes = Settings.Global.getLong(mResolver, 1178 Settings.Global.SYS_STORAGE_CACHE_MAX_BYTES, DEFAULT_CACHE_MAX_BYTES); 1179 1180 return Math.min(cacheBytes, maxCacheBytes); 1181 } 1182 1183 /** 1184 * Return the number of available bytes at which the given path is 1185 * considered full. 1186 * 1187 * @hide 1188 */ 1189 public long getStorageFullBytes(File path) { 1190 return Settings.Global.getLong(mResolver, Settings.Global.SYS_STORAGE_FULL_THRESHOLD_BYTES, 1191 DEFAULT_FULL_THRESHOLD_BYTES); 1192 } 1193 1194 /** {@hide} */ 1195 public void createUserKey(int userId, int serialNumber, boolean ephemeral) { 1196 try { 1197 mStorageManager.createUserKey(userId, serialNumber, ephemeral); 1198 } catch (RemoteException e) { 1199 throw e.rethrowFromSystemServer(); 1200 } 1201 } 1202 1203 /** {@hide} */ 1204 public void destroyUserKey(int userId) { 1205 try { 1206 mStorageManager.destroyUserKey(userId); 1207 } catch (RemoteException e) { 1208 throw e.rethrowFromSystemServer(); 1209 } 1210 } 1211 1212 /** {@hide} */ 1213 public void unlockUserKey(int userId, int serialNumber, byte[] token, byte[] secret) { 1214 try { 1215 mStorageManager.unlockUserKey(userId, serialNumber, token, secret); 1216 } catch (RemoteException e) { 1217 throw e.rethrowFromSystemServer(); 1218 } 1219 } 1220 1221 /** {@hide} */ 1222 public void lockUserKey(int userId) { 1223 try { 1224 mStorageManager.lockUserKey(userId); 1225 } catch (RemoteException e) { 1226 throw e.rethrowFromSystemServer(); 1227 } 1228 } 1229 1230 /** {@hide} */ 1231 public void prepareUserStorage(String volumeUuid, int userId, int serialNumber, int flags) { 1232 try { 1233 mStorageManager.prepareUserStorage(volumeUuid, userId, serialNumber, flags); 1234 } catch (RemoteException e) { 1235 throw e.rethrowFromSystemServer(); 1236 } 1237 } 1238 1239 /** {@hide} */ 1240 public void destroyUserStorage(String volumeUuid, int userId, int flags) { 1241 try { 1242 mStorageManager.destroyUserStorage(volumeUuid, userId, flags); 1243 } catch (RemoteException e) { 1244 throw e.rethrowFromSystemServer(); 1245 } 1246 } 1247 1248 /** {@hide} */ 1249 public static boolean isUserKeyUnlocked(int userId) { 1250 if (sStorageManager == null) { 1251 sStorageManager = IStorageManager.Stub 1252 .asInterface(ServiceManager.getService("mount")); 1253 } 1254 if (sStorageManager == null) { 1255 Slog.w(TAG, "Early during boot, assuming locked"); 1256 return false; 1257 } 1258 final long token = Binder.clearCallingIdentity(); 1259 try { 1260 return sStorageManager.isUserKeyUnlocked(userId); 1261 } catch (RemoteException e) { 1262 throw e.rethrowAsRuntimeException(); 1263 } finally { 1264 Binder.restoreCallingIdentity(token); 1265 } 1266 } 1267 1268 /** 1269 * Return if data stored at or under the given path will be encrypted while 1270 * at rest. This can help apps avoid the overhead of double-encrypting data. 1271 */ 1272 public boolean isEncrypted(File file) { 1273 if (FileUtils.contains(Environment.getDataDirectory(), file)) { 1274 return isEncrypted(); 1275 } else if (FileUtils.contains(Environment.getExpandDirectory(), file)) { 1276 return true; 1277 } 1278 // TODO: extend to support shared storage 1279 return false; 1280 } 1281 1282 /** {@hide} 1283 * Is this device encryptable or already encrypted? 1284 * @return true for encryptable or encrypted 1285 * false not encrypted and not encryptable 1286 */ 1287 public static boolean isEncryptable() { 1288 return RoSystemProperties.CRYPTO_ENCRYPTABLE; 1289 } 1290 1291 /** {@hide} 1292 * Is this device already encrypted? 1293 * @return true for encrypted. (Implies isEncryptable() == true) 1294 * false not encrypted 1295 */ 1296 public static boolean isEncrypted() { 1297 return RoSystemProperties.CRYPTO_ENCRYPTED; 1298 } 1299 1300 /** {@hide} 1301 * Is this device file encrypted? 1302 * @return true for file encrypted. (Implies isEncrypted() == true) 1303 * false not encrypted or block encrypted 1304 */ 1305 public static boolean isFileEncryptedNativeOnly() { 1306 if (!isEncrypted()) { 1307 return false; 1308 } 1309 return RoSystemProperties.CRYPTO_FILE_ENCRYPTED; 1310 } 1311 1312 /** {@hide} 1313 * Is this device block encrypted? 1314 * @return true for block encrypted. (Implies isEncrypted() == true) 1315 * false not encrypted or file encrypted 1316 */ 1317 public static boolean isBlockEncrypted() { 1318 if (!isEncrypted()) { 1319 return false; 1320 } 1321 return RoSystemProperties.CRYPTO_BLOCK_ENCRYPTED; 1322 } 1323 1324 /** {@hide} 1325 * Is this device block encrypted with credentials? 1326 * @return true for crediential block encrypted. 1327 * (Implies isBlockEncrypted() == true) 1328 * false not encrypted, file encrypted or default block encrypted 1329 */ 1330 public static boolean isNonDefaultBlockEncrypted() { 1331 if (!isBlockEncrypted()) { 1332 return false; 1333 } 1334 1335 try { 1336 IStorageManager storageManager = IStorageManager.Stub.asInterface( 1337 ServiceManager.getService("mount")); 1338 return storageManager.getPasswordType() != CRYPT_TYPE_DEFAULT; 1339 } catch (RemoteException e) { 1340 Log.e(TAG, "Error getting encryption type"); 1341 return false; 1342 } 1343 } 1344 1345 /** {@hide} 1346 * Is this device in the process of being block encrypted? 1347 * @return true for encrypting. 1348 * false otherwise 1349 * Whether device isEncrypted at this point is undefined 1350 * Note that only system services and CryptKeeper will ever see this return 1351 * true - no app will ever be launched in this state. 1352 * Also note that this state will not change without a teardown of the 1353 * framework, so no service needs to check for changes during their lifespan 1354 */ 1355 public static boolean isBlockEncrypting() { 1356 final String state = SystemProperties.get("vold.encrypt_progress", ""); 1357 return !"".equalsIgnoreCase(state); 1358 } 1359 1360 /** {@hide} 1361 * Is this device non default block encrypted and in the process of 1362 * prompting for credentials? 1363 * @return true for prompting for credentials. 1364 * (Implies isNonDefaultBlockEncrypted() == true) 1365 * false otherwise 1366 * Note that only system services and CryptKeeper will ever see this return 1367 * true - no app will ever be launched in this state. 1368 * Also note that this state will not change without a teardown of the 1369 * framework, so no service needs to check for changes during their lifespan 1370 */ 1371 public static boolean inCryptKeeperBounce() { 1372 final String status = SystemProperties.get("vold.decrypt"); 1373 return "trigger_restart_min_framework".equals(status); 1374 } 1375 1376 /** {@hide} */ 1377 public static boolean isFileEncryptedEmulatedOnly() { 1378 return SystemProperties.getBoolean(StorageManager.PROP_EMULATE_FBE, false); 1379 } 1380 1381 /** {@hide} 1382 * Is this device running in a file encrypted mode, either native or emulated? 1383 * @return true for file encrypted, false otherwise 1384 */ 1385 public static boolean isFileEncryptedNativeOrEmulated() { 1386 return isFileEncryptedNativeOnly() 1387 || isFileEncryptedEmulatedOnly(); 1388 } 1389 1390 /** {@hide} */ 1391 public static File maybeTranslateEmulatedPathToInternal(File path) { 1392 final IStorageManager storageManager = IStorageManager.Stub.asInterface( 1393 ServiceManager.getService("mount")); 1394 try { 1395 final VolumeInfo[] vols = storageManager.getVolumes(0); 1396 for (VolumeInfo vol : vols) { 1397 if ((vol.getType() == VolumeInfo.TYPE_EMULATED 1398 || vol.getType() == VolumeInfo.TYPE_PUBLIC) && vol.isMountedReadable()) { 1399 final File internalPath = FileUtils.rewriteAfterRename(vol.getPath(), 1400 vol.getInternalPath(), path); 1401 if (internalPath != null && internalPath.exists()) { 1402 return internalPath; 1403 } 1404 } 1405 } 1406 } catch (RemoteException e) { 1407 throw e.rethrowFromSystemServer(); 1408 } 1409 return path; 1410 } 1411 1412 /** {@hide} */ 1413 @VisibleForTesting 1414 public @NonNull ParcelFileDescriptor openProxyFileDescriptor( 1415 int mode, ProxyFileDescriptorCallback callback, Handler handler, ThreadFactory factory) 1416 throws IOException { 1417 MetricsLogger.count(mContext, "storage_open_proxy_file_descriptor", 1); 1418 // Retry is needed because the mount point mFuseAppLoop is using may be unmounted before 1419 // invoking StorageManagerService#openProxyFileDescriptor. In this case, we need to re-mount 1420 // the bridge by calling mountProxyFileDescriptorBridge. 1421 while (true) { 1422 try { 1423 synchronized (mFuseAppLoopLock) { 1424 boolean newlyCreated = false; 1425 if (mFuseAppLoop == null) { 1426 final AppFuseMount mount = mStorageManager.mountProxyFileDescriptorBridge(); 1427 if (mount == null) { 1428 throw new IOException("Failed to mount proxy bridge"); 1429 } 1430 mFuseAppLoop = new FuseAppLoop(mount.mountPointId, mount.fd, factory); 1431 newlyCreated = true; 1432 } 1433 if (handler == null) { 1434 handler = new Handler(Looper.getMainLooper()); 1435 } 1436 try { 1437 final int fileId = mFuseAppLoop.registerCallback(callback, handler); 1438 final ParcelFileDescriptor pfd = mStorageManager.openProxyFileDescriptor( 1439 mFuseAppLoop.getMountPointId(), fileId, mode); 1440 if (pfd == null) { 1441 mFuseAppLoop.unregisterCallback(fileId); 1442 throw new FuseUnavailableMountException( 1443 mFuseAppLoop.getMountPointId()); 1444 } 1445 return pfd; 1446 } catch (FuseUnavailableMountException exception) { 1447 // The bridge is being unmounted. Tried to recreate it unless the bridge was 1448 // just created. 1449 if (newlyCreated) { 1450 throw new IOException(exception); 1451 } 1452 mFuseAppLoop = null; 1453 continue; 1454 } 1455 } 1456 } catch (RemoteException e) { 1457 // Cannot recover from remote exception. 1458 throw new IOException(e); 1459 } 1460 } 1461 } 1462 1463 /** 1464 * Opens seekable ParcelFileDescriptor that routes file operation requests to 1465 * ProxyFileDescriptorCallback. 1466 * 1467 * @param mode The desired access mode, must be one of 1468 * {@link ParcelFileDescriptor#MODE_READ_ONLY}, 1469 * {@link ParcelFileDescriptor#MODE_WRITE_ONLY}, or 1470 * {@link ParcelFileDescriptor#MODE_READ_WRITE} 1471 * @param callback Callback to process file operation requests issued on returned file 1472 * descriptor. 1473 * @return Seekable ParcelFileDescriptor. 1474 * @throws IOException 1475 */ 1476 public @NonNull ParcelFileDescriptor openProxyFileDescriptor( 1477 int mode, ProxyFileDescriptorCallback callback) 1478 throws IOException { 1479 return openProxyFileDescriptor(mode, callback, null, null); 1480 } 1481 1482 /** 1483 * Opens seekable ParcelFileDescriptor that routes file operation requests to 1484 * ProxyFileDescriptorCallback. 1485 * 1486 * @param mode The desired access mode, must be one of 1487 * {@link ParcelFileDescriptor#MODE_READ_ONLY}, 1488 * {@link ParcelFileDescriptor#MODE_WRITE_ONLY}, or 1489 * {@link ParcelFileDescriptor#MODE_READ_WRITE} 1490 * @param callback Callback to process file operation requests issued on returned file 1491 * descriptor. 1492 * @param handler Handler that invokes callback methods. 1493 * @return Seekable ParcelFileDescriptor. 1494 * @throws IOException 1495 */ 1496 public @NonNull ParcelFileDescriptor openProxyFileDescriptor( 1497 int mode, ProxyFileDescriptorCallback callback, Handler handler) 1498 throws IOException { 1499 return openProxyFileDescriptor(mode, callback, handler, null); 1500 } 1501 1502 1503 /** {@hide} */ 1504 @VisibleForTesting 1505 public int getProxyFileDescriptorMountPointId() { 1506 synchronized (mFuseAppLoopLock) { 1507 return mFuseAppLoop != null ? mFuseAppLoop.getMountPointId() : -1; 1508 } 1509 } 1510 1511 /** 1512 * Return quota size in bytes for all cached data belonging to the calling 1513 * app on the given storage volume. 1514 * <p> 1515 * If your app goes above this quota, your cached files will be some of the 1516 * first to be deleted when additional disk space is needed. Conversely, if 1517 * your app stays under this quota, your cached files will be some of the 1518 * last to be deleted when additional disk space is needed. 1519 * <p> 1520 * This quota will change over time depending on how frequently the user 1521 * interacts with your app, and depending on how much disk space is used. 1522 * <p class="note"> 1523 * Note: if your app uses the {@code android:sharedUserId} manifest feature, 1524 * then cached data for all packages in your shared UID is tracked together 1525 * as a single unit. 1526 * </p> 1527 * 1528 * @param storageUuid the UUID of the storage volume that you're interested 1529 * in. The UUID for a specific path can be obtained using 1530 * {@link #getUuidForPath(File)}. 1531 * @throws IOException when the storage device isn't present, or when it 1532 * doesn't support cache quotas. 1533 * @see #getCacheSizeBytes(UUID) 1534 */ 1535 public long getCacheQuotaBytes(@NonNull UUID storageUuid) throws IOException { 1536 try { 1537 final ApplicationInfo app = mContext.getApplicationInfo(); 1538 return mStorageManager.getCacheQuotaBytes(convert(storageUuid), app.uid); 1539 } catch (ParcelableException e) { 1540 e.maybeRethrow(IOException.class); 1541 throw new RuntimeException(e); 1542 } catch (RemoteException e) { 1543 throw e.rethrowFromSystemServer(); 1544 } 1545 } 1546 1547 /** @removed */ 1548 @Deprecated 1549 public long getCacheQuotaBytes(@NonNull File path) throws IOException { 1550 return getCacheQuotaBytes(getUuidForPath(path)); 1551 } 1552 1553 /** 1554 * Return total size in bytes of all cached data belonging to the calling 1555 * app on the given storage volume. 1556 * <p> 1557 * Cached data tracked by this method always includes 1558 * {@link Context#getCacheDir()} and {@link Context#getCodeCacheDir()}, and 1559 * it also includes {@link Context#getExternalCacheDir()} if the primary 1560 * shared/external storage is hosted on the same storage device as your 1561 * private data. 1562 * <p class="note"> 1563 * Note: if your app uses the {@code android:sharedUserId} manifest feature, 1564 * then cached data for all packages in your shared UID is tracked together 1565 * as a single unit. 1566 * </p> 1567 * 1568 * @param storageUuid the UUID of the storage volume that you're interested 1569 * in. The UUID for a specific path can be obtained using 1570 * {@link #getUuidForPath(File)}. 1571 * @throws IOException when the storage device isn't present, or when it 1572 * doesn't support cache quotas. 1573 * @see #getCacheQuotaBytes(UUID) 1574 */ 1575 public long getCacheSizeBytes(@NonNull UUID storageUuid) throws IOException { 1576 try { 1577 final ApplicationInfo app = mContext.getApplicationInfo(); 1578 return mStorageManager.getCacheSizeBytes(convert(storageUuid), app.uid); 1579 } catch (ParcelableException e) { 1580 e.maybeRethrow(IOException.class); 1581 throw new RuntimeException(e); 1582 } catch (RemoteException e) { 1583 throw e.rethrowFromSystemServer(); 1584 } 1585 } 1586 1587 /** @removed */ 1588 @Deprecated 1589 public long getCacheSizeBytes(@NonNull File path) throws IOException { 1590 return getCacheSizeBytes(getUuidForPath(path)); 1591 } 1592 1593 /** @removed */ 1594 @Deprecated 1595 public long getCacheQuotaBytes() throws IOException { 1596 return getCacheQuotaBytes(mContext.getCacheDir()); 1597 } 1598 1599 /** @removed */ 1600 @Deprecated 1601 public long getCacheSizeBytes() throws IOException { 1602 return getCacheSizeBytes(mContext.getCacheDir()); 1603 } 1604 1605 /** @removed */ 1606 @Deprecated 1607 public long getExternalCacheQuotaBytes() throws IOException { 1608 return getCacheQuotaBytes(mContext.getExternalCacheDir()); 1609 } 1610 1611 /** @removed */ 1612 @Deprecated 1613 public long getExternalCacheSizeBytes() throws IOException { 1614 return getCacheSizeBytes(mContext.getExternalCacheDir()); 1615 } 1616 1617 /** 1618 * Flag indicating that a disk space allocation request should operate in an 1619 * aggressive mode. This flag should only be rarely used in situations that 1620 * are critical to system health or security. 1621 * <p> 1622 * When set, the system is more aggressive about the data that it considers 1623 * for possible deletion when allocating disk space. 1624 * <p class="note"> 1625 * Note: your app must hold the 1626 * {@link android.Manifest.permission#ALLOCATE_AGGRESSIVE} permission for 1627 * this flag to take effect. 1628 * </p> 1629 * 1630 * @see #getAllocatableBytes(UUID, int) 1631 * @see #allocateBytes(UUID, long, int) 1632 * @see #allocateBytes(FileDescriptor, long, int) 1633 */ 1634 @RequiresPermission(android.Manifest.permission.ALLOCATE_AGGRESSIVE) 1635 public static final int FLAG_ALLOCATE_AGGRESSIVE = 1; 1636 1637 /** @hide */ 1638 @IntDef(flag = true, value = { 1639 FLAG_ALLOCATE_AGGRESSIVE, 1640 }) 1641 @Retention(RetentionPolicy.SOURCE) 1642 public @interface AllocateFlags {} 1643 1644 /** 1645 * Return the maximum number of new bytes that your app can allocate for 1646 * itself on the given storage volume. This value is typically larger than 1647 * {@link File#getUsableSpace()}, since the system may be willing to delete 1648 * cached files to satisfy an allocation request. You can then allocate 1649 * space for yourself using {@link #allocateBytes(UUID, long, int)} or 1650 * {@link #allocateBytes(FileDescriptor, long, int)}. 1651 * <p> 1652 * This method is best used as a pre-flight check, such as deciding if there 1653 * is enough space to store an entire music album before you allocate space 1654 * for each audio file in the album. Attempts to allocate disk space beyond 1655 * the returned value will fail. 1656 * <p> 1657 * If the returned value is not large enough for the data you'd like to 1658 * store, you can launch {@link #ACTION_MANAGE_STORAGE} with the 1659 * {@link #EXTRA_UUID} and {@link #EXTRA_REQUESTED_BYTES} options to help 1660 * involve the user in freeing up disk space. 1661 * <p class="note"> 1662 * Note: if your app uses the {@code android:sharedUserId} manifest feature, 1663 * then allocatable space for all packages in your shared UID is tracked 1664 * together as a single unit. 1665 * </p> 1666 * 1667 * @param storageUuid the UUID of the storage volume where you're 1668 * considering allocating disk space, since allocatable space can 1669 * vary widely depending on the underlying storage device. The 1670 * UUID for a specific path can be obtained using 1671 * {@link #getUuidForPath(File)}. 1672 * @param flags to apply to the request. 1673 * @return the maximum number of new bytes that the calling app can allocate 1674 * using {@link #allocateBytes(UUID, long, int)} or 1675 * {@link #allocateBytes(FileDescriptor, long, int)}. 1676 * @throws IOException when the storage device isn't present, or when it 1677 * doesn't support allocating space. 1678 */ 1679 public long getAllocatableBytes(@NonNull UUID storageUuid, @AllocateFlags int flags) 1680 throws IOException { 1681 try { 1682 return mStorageManager.getAllocatableBytes(convert(storageUuid), flags); 1683 } catch (ParcelableException e) { 1684 e.maybeRethrow(IOException.class); 1685 throw new RuntimeException(e); 1686 } catch (RemoteException e) { 1687 throw e.rethrowFromSystemServer(); 1688 } 1689 } 1690 1691 /** @removed */ 1692 @Deprecated 1693 public long getAllocatableBytes(@NonNull File path, @AllocateFlags int flags) 1694 throws IOException { 1695 return getAllocatableBytes(getUuidForPath(path), flags); 1696 } 1697 1698 /** 1699 * Allocate the requested number of bytes for your application to use on the 1700 * given storage volume. This will cause the system to delete any cached 1701 * files necessary to satisfy your request. 1702 * <p> 1703 * Attempts to allocate disk space beyond the value returned by 1704 * {@link #getAllocatableBytes(UUID, int)} will fail. 1705 * <p> 1706 * Since multiple apps can be running simultaneously, this method may be 1707 * subject to race conditions. If possible, consider using 1708 * {@link #allocateBytes(FileDescriptor, long, int)} which will guarantee 1709 * that bytes are allocated to an opened file. 1710 * 1711 * @param storageUuid the UUID of the storage volume where you'd like to 1712 * allocate disk space. The UUID for a specific path can be 1713 * obtained using {@link #getUuidForPath(File)}. 1714 * @param bytes the number of bytes to allocate. 1715 * @param flags to apply to the request. 1716 * @throws IOException when the storage device isn't present, or when it 1717 * doesn't support allocating space, or if the device had 1718 * trouble allocating the requested space. 1719 * @see #getAllocatableBytes(UUID, int) 1720 */ 1721 public void allocateBytes(@NonNull UUID storageUuid, long bytes, @AllocateFlags int flags) 1722 throws IOException { 1723 try { 1724 mStorageManager.allocateBytes(convert(storageUuid), bytes, flags); 1725 } catch (ParcelableException e) { 1726 e.maybeRethrow(IOException.class); 1727 } catch (RemoteException e) { 1728 throw e.rethrowFromSystemServer(); 1729 } 1730 } 1731 1732 /** @removed */ 1733 @Deprecated 1734 public void allocateBytes(@NonNull File path, long bytes, @AllocateFlags int flags) 1735 throws IOException { 1736 allocateBytes(getUuidForPath(path), bytes, flags); 1737 } 1738 1739 /** 1740 * Allocate the requested number of bytes for your application to use in the 1741 * given open file. This will cause the system to delete any cached files 1742 * necessary to satisfy your request. 1743 * <p> 1744 * Attempts to allocate disk space beyond the value returned by 1745 * {@link #getAllocatableBytes(UUID, int)} will fail. 1746 * <p> 1747 * This method guarantees that bytes have been allocated to the opened file, 1748 * otherwise it will throw if fast allocation is not possible. Fast 1749 * allocation is typically only supported in private app data directories, 1750 * and on shared/external storage devices which are emulated. 1751 * 1752 * @param fd the open file that you'd like to allocate disk space for. 1753 * @param bytes the number of bytes to allocate. This is the desired final 1754 * size of the open file. If the open file is smaller than this 1755 * requested size, it will be extended without modifying any 1756 * existing contents. If the open file is larger than this 1757 * requested size, it will be truncated. 1758 * @param flags to apply to the request. 1759 * @throws IOException when the storage device isn't present, or when it 1760 * doesn't support allocating space, or if the device had 1761 * trouble allocating the requested space. 1762 * @see #getAllocatableBytes(UUID, int) 1763 * @see Environment#isExternalStorageEmulated(File) 1764 */ 1765 public void allocateBytes(FileDescriptor fd, long bytes, @AllocateFlags int flags) 1766 throws IOException { 1767 final File file = ParcelFileDescriptor.getFile(fd); 1768 for (int i = 0; i < 3; i++) { 1769 try { 1770 final long haveBytes = Os.fstat(fd).st_blocks * 512; 1771 final long needBytes = bytes - haveBytes; 1772 1773 if (needBytes > 0) { 1774 allocateBytes(file, needBytes, flags); 1775 } 1776 1777 Os.posix_fallocate(fd, 0, bytes); 1778 return; 1779 } catch (ErrnoException e) { 1780 if (e.errno == OsConstants.ENOSPC) { 1781 Log.w(TAG, "Odd, not enough space; let's try again?"); 1782 continue; 1783 } 1784 throw e.rethrowAsIOException(); 1785 } 1786 } 1787 throw new IOException( 1788 "Well this is embarassing; we can't allocate " + bytes + " for " + file); 1789 } 1790 1791 private static final String XATTR_CACHE_GROUP = "user.cache_group"; 1792 private static final String XATTR_CACHE_TOMBSTONE = "user.cache_tombstone"; 1793 1794 /** {@hide} */ 1795 private static void setCacheBehavior(File path, String name, boolean enabled) 1796 throws IOException { 1797 if (!path.isDirectory()) { 1798 throw new IOException("Cache behavior can only be set on directories"); 1799 } 1800 if (enabled) { 1801 try { 1802 Os.setxattr(path.getAbsolutePath(), name, 1803 "1".getBytes(StandardCharsets.UTF_8), 0); 1804 } catch (ErrnoException e) { 1805 throw e.rethrowAsIOException(); 1806 } 1807 } else { 1808 try { 1809 Os.removexattr(path.getAbsolutePath(), name); 1810 } catch (ErrnoException e) { 1811 if (e.errno != OsConstants.ENODATA) { 1812 throw e.rethrowAsIOException(); 1813 } 1814 } 1815 } 1816 } 1817 1818 /** {@hide} */ 1819 private static boolean isCacheBehavior(File path, String name) throws IOException { 1820 try { 1821 Os.getxattr(path.getAbsolutePath(), name); 1822 return true; 1823 } catch (ErrnoException e) { 1824 if (e.errno != OsConstants.ENODATA) { 1825 throw e.rethrowAsIOException(); 1826 } else { 1827 return false; 1828 } 1829 } 1830 } 1831 1832 /** 1833 * Enable or disable special cache behavior that treats this directory and 1834 * its contents as an entire group. 1835 * <p> 1836 * When enabled and this directory is considered for automatic deletion by 1837 * the OS, all contained files will either be deleted together, or not at 1838 * all. This is useful when you have a directory that contains several 1839 * related metadata files that depend on each other, such as movie file and 1840 * a subtitle file. 1841 * <p> 1842 * When enabled, the <em>newest</em> {@link File#lastModified()} value of 1843 * any contained files is considered the modified time of the entire 1844 * directory. 1845 * <p> 1846 * This behavior can only be set on a directory, and it applies recursively 1847 * to all contained files and directories. 1848 */ 1849 public void setCacheBehaviorGroup(File path, boolean group) throws IOException { 1850 setCacheBehavior(path, XATTR_CACHE_GROUP, group); 1851 } 1852 1853 /** 1854 * Read the current value set by 1855 * {@link #setCacheBehaviorGroup(File, boolean)}. 1856 */ 1857 public boolean isCacheBehaviorGroup(File path) throws IOException { 1858 return isCacheBehavior(path, XATTR_CACHE_GROUP); 1859 } 1860 1861 /** @removed */ 1862 @Deprecated 1863 public void setCacheBehaviorAtomic(File path, boolean atomic) throws IOException { 1864 setCacheBehaviorGroup(path, atomic); 1865 } 1866 1867 /** @removed */ 1868 @Deprecated 1869 public boolean isCacheBehaviorAtomic(File path) throws IOException { 1870 return isCacheBehaviorGroup(path); 1871 } 1872 1873 /** 1874 * Enable or disable special cache behavior that leaves deleted cache files 1875 * intact as tombstones. 1876 * <p> 1877 * When enabled and a file contained in this directory is automatically 1878 * deleted by the OS, the file will be truncated to have a length of 0 bytes 1879 * instead of being fully deleted. This is useful if you need to distinguish 1880 * between a file that was deleted versus one that never existed. 1881 * <p> 1882 * This behavior can only be set on a directory, and it applies recursively 1883 * to all contained files and directories. 1884 * <p class="note"> 1885 * Note: this behavior is ignored completely if the user explicitly requests 1886 * that all cached data be cleared. 1887 * </p> 1888 */ 1889 public void setCacheBehaviorTombstone(File path, boolean tombstone) throws IOException { 1890 setCacheBehavior(path, XATTR_CACHE_TOMBSTONE, tombstone); 1891 } 1892 1893 /** 1894 * Read the current value set by 1895 * {@link #setCacheBehaviorTombstone(File, boolean)}. 1896 */ 1897 public boolean isCacheBehaviorTombstone(File path) throws IOException { 1898 return isCacheBehavior(path, XATTR_CACHE_TOMBSTONE); 1899 } 1900 1901 /** {@hide} */ 1902 public static UUID convert(String uuid) { 1903 if (Objects.equals(uuid, UUID_PRIVATE_INTERNAL)) { 1904 return UUID_DEFAULT; 1905 } else if (Objects.equals(uuid, UUID_PRIMARY_PHYSICAL)) { 1906 return UUID_PRIMARY_PHYSICAL_; 1907 } else if (Objects.equals(uuid, UUID_SYSTEM)) { 1908 return UUID_SYSTEM_; 1909 } else { 1910 return UUID.fromString(uuid); 1911 } 1912 } 1913 1914 /** {@hide} */ 1915 public static String convert(UUID storageUuid) { 1916 if (UUID_DEFAULT.equals(storageUuid)) { 1917 return UUID_PRIVATE_INTERNAL; 1918 } else if (UUID_PRIMARY_PHYSICAL_.equals(storageUuid)) { 1919 return UUID_PRIMARY_PHYSICAL; 1920 } else if (UUID_SYSTEM_.equals(storageUuid)) { 1921 return UUID_SYSTEM; 1922 } else { 1923 return storageUuid.toString(); 1924 } 1925 } 1926 1927 private final Object mFuseAppLoopLock = new Object(); 1928 1929 @GuardedBy("mFuseAppLoopLock") 1930 private @Nullable FuseAppLoop mFuseAppLoop = null; 1931 1932 /// Consts to match the password types in cryptfs.h 1933 /** @hide */ 1934 public static final int CRYPT_TYPE_PASSWORD = 0; 1935 /** @hide */ 1936 public static final int CRYPT_TYPE_DEFAULT = 1; 1937 /** @hide */ 1938 public static final int CRYPT_TYPE_PATTERN = 2; 1939 /** @hide */ 1940 public static final int CRYPT_TYPE_PIN = 3; 1941 1942 // Constants for the data available via StorageManagerService.getField. 1943 /** @hide */ 1944 public static final String SYSTEM_LOCALE_KEY = "SystemLocale"; 1945 /** @hide */ 1946 public static final String OWNER_INFO_KEY = "OwnerInfo"; 1947 /** @hide */ 1948 public static final String PATTERN_VISIBLE_KEY = "PatternVisible"; 1949 /** @hide */ 1950 public static final String PASSWORD_VISIBLE_KEY = "PasswordVisible"; 1951} 1952