1 /* 2 * Copyright (C) 2015 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 17 package android.os.storage; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.compat.annotation.UnsupportedAppUsage; 22 import android.content.Context; 23 import android.content.Intent; 24 import android.content.res.Resources; 25 import android.net.Uri; 26 import android.os.Environment; 27 import android.os.IVold; 28 import android.os.Parcel; 29 import android.os.Parcelable; 30 import android.os.UserHandle; 31 import android.provider.DocumentsContract; 32 import android.text.TextUtils; 33 import android.util.ArrayMap; 34 import android.util.DebugUtils; 35 import android.util.SparseArray; 36 import android.util.SparseIntArray; 37 38 import com.android.internal.R; 39 import com.android.internal.util.IndentingPrintWriter; 40 import com.android.internal.util.Preconditions; 41 42 import java.io.CharArrayWriter; 43 import java.io.File; 44 import java.util.Comparator; 45 import java.util.Locale; 46 import java.util.Objects; 47 48 /** 49 * Information about a storage volume that may be mounted. A volume may be a 50 * partition on a physical {@link DiskInfo}, an emulated volume above some other 51 * storage medium, or a standalone container like an ASEC or OBB. 52 * <p> 53 * Volumes may be mounted with various flags: 54 * <ul> 55 * <li>{@link #MOUNT_FLAG_PRIMARY} means the volume provides primary external 56 * storage, historically found at {@code /sdcard}. 57 * <li>{@link #MOUNT_FLAG_VISIBLE} means the volume is visible to third-party 58 * apps for direct filesystem access. The system should send out relevant 59 * storage broadcasts and index any media on visible volumes. Visible volumes 60 * are considered a more stable part of the device, which is why we take the 61 * time to index them. In particular, transient volumes like USB OTG devices 62 * <em>should not</em> be marked as visible; their contents should be surfaced 63 * to apps through the Storage Access Framework. 64 * </ul> 65 * 66 * @hide 67 */ 68 public class VolumeInfo implements Parcelable { 69 public static final String ACTION_VOLUME_STATE_CHANGED = 70 "android.os.storage.action.VOLUME_STATE_CHANGED"; 71 public static final String EXTRA_VOLUME_ID = 72 "android.os.storage.extra.VOLUME_ID"; 73 public static final String EXTRA_VOLUME_STATE = 74 "android.os.storage.extra.VOLUME_STATE"; 75 76 /** Stub volume representing internal private storage */ 77 public static final String ID_PRIVATE_INTERNAL = "private"; 78 /** Real volume representing internal emulated storage */ 79 public static final String ID_EMULATED_INTERNAL = "emulated"; 80 81 @UnsupportedAppUsage 82 public static final int TYPE_PUBLIC = IVold.VOLUME_TYPE_PUBLIC; 83 public static final int TYPE_PRIVATE = IVold.VOLUME_TYPE_PRIVATE; 84 @UnsupportedAppUsage 85 public static final int TYPE_EMULATED = IVold.VOLUME_TYPE_EMULATED; 86 public static final int TYPE_ASEC = IVold.VOLUME_TYPE_ASEC; 87 public static final int TYPE_OBB = IVold.VOLUME_TYPE_OBB; 88 public static final int TYPE_STUB = IVold.VOLUME_TYPE_STUB; 89 90 public static final int STATE_UNMOUNTED = IVold.VOLUME_STATE_UNMOUNTED; 91 public static final int STATE_CHECKING = IVold.VOLUME_STATE_CHECKING; 92 public static final int STATE_MOUNTED = IVold.VOLUME_STATE_MOUNTED; 93 public static final int STATE_MOUNTED_READ_ONLY = IVold.VOLUME_STATE_MOUNTED_READ_ONLY; 94 public static final int STATE_FORMATTING = IVold.VOLUME_STATE_FORMATTING; 95 public static final int STATE_EJECTING = IVold.VOLUME_STATE_EJECTING; 96 public static final int STATE_UNMOUNTABLE = IVold.VOLUME_STATE_UNMOUNTABLE; 97 public static final int STATE_REMOVED = IVold.VOLUME_STATE_REMOVED; 98 public static final int STATE_BAD_REMOVAL = IVold.VOLUME_STATE_BAD_REMOVAL; 99 100 public static final int MOUNT_FLAG_PRIMARY = IVold.MOUNT_FLAG_PRIMARY; 101 public static final int MOUNT_FLAG_VISIBLE = IVold.MOUNT_FLAG_VISIBLE; 102 103 private static SparseArray<String> sStateToEnvironment = new SparseArray<>(); 104 private static ArrayMap<String, String> sEnvironmentToBroadcast = new ArrayMap<>(); 105 private static SparseIntArray sStateToDescrip = new SparseIntArray(); 106 107 private static final Comparator<VolumeInfo> 108 sDescriptionComparator = new Comparator<VolumeInfo>() { 109 @Override 110 public int compare(VolumeInfo lhs, VolumeInfo rhs) { 111 if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(lhs.getId())) { 112 return -1; 113 } else if (lhs.getDescription() == null) { 114 return 1; 115 } else if (rhs.getDescription() == null) { 116 return -1; 117 } else { 118 return lhs.getDescription().compareTo(rhs.getDescription()); 119 } 120 } 121 }; 122 123 static { sStateToEnvironment.put(VolumeInfo.STATE_UNMOUNTED, Environment.MEDIA_UNMOUNTED)124 sStateToEnvironment.put(VolumeInfo.STATE_UNMOUNTED, Environment.MEDIA_UNMOUNTED); sStateToEnvironment.put(VolumeInfo.STATE_CHECKING, Environment.MEDIA_CHECKING)125 sStateToEnvironment.put(VolumeInfo.STATE_CHECKING, Environment.MEDIA_CHECKING); sStateToEnvironment.put(VolumeInfo.STATE_MOUNTED, Environment.MEDIA_MOUNTED)126 sStateToEnvironment.put(VolumeInfo.STATE_MOUNTED, Environment.MEDIA_MOUNTED); sStateToEnvironment.put(VolumeInfo.STATE_MOUNTED_READ_ONLY, Environment.MEDIA_MOUNTED_READ_ONLY)127 sStateToEnvironment.put(VolumeInfo.STATE_MOUNTED_READ_ONLY, Environment.MEDIA_MOUNTED_READ_ONLY); sStateToEnvironment.put(VolumeInfo.STATE_FORMATTING, Environment.MEDIA_UNMOUNTED)128 sStateToEnvironment.put(VolumeInfo.STATE_FORMATTING, Environment.MEDIA_UNMOUNTED); sStateToEnvironment.put(VolumeInfo.STATE_EJECTING, Environment.MEDIA_EJECTING)129 sStateToEnvironment.put(VolumeInfo.STATE_EJECTING, Environment.MEDIA_EJECTING); sStateToEnvironment.put(VolumeInfo.STATE_UNMOUNTABLE, Environment.MEDIA_UNMOUNTABLE)130 sStateToEnvironment.put(VolumeInfo.STATE_UNMOUNTABLE, Environment.MEDIA_UNMOUNTABLE); sStateToEnvironment.put(VolumeInfo.STATE_REMOVED, Environment.MEDIA_REMOVED)131 sStateToEnvironment.put(VolumeInfo.STATE_REMOVED, Environment.MEDIA_REMOVED); sStateToEnvironment.put(VolumeInfo.STATE_BAD_REMOVAL, Environment.MEDIA_BAD_REMOVAL)132 sStateToEnvironment.put(VolumeInfo.STATE_BAD_REMOVAL, Environment.MEDIA_BAD_REMOVAL); 133 sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTED, Intent.ACTION_MEDIA_UNMOUNTED)134 sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTED, Intent.ACTION_MEDIA_UNMOUNTED); sEnvironmentToBroadcast.put(Environment.MEDIA_CHECKING, Intent.ACTION_MEDIA_CHECKING)135 sEnvironmentToBroadcast.put(Environment.MEDIA_CHECKING, Intent.ACTION_MEDIA_CHECKING); sEnvironmentToBroadcast.put(Environment.MEDIA_MOUNTED, Intent.ACTION_MEDIA_MOUNTED)136 sEnvironmentToBroadcast.put(Environment.MEDIA_MOUNTED, Intent.ACTION_MEDIA_MOUNTED); sEnvironmentToBroadcast.put(Environment.MEDIA_MOUNTED_READ_ONLY, Intent.ACTION_MEDIA_MOUNTED)137 sEnvironmentToBroadcast.put(Environment.MEDIA_MOUNTED_READ_ONLY, Intent.ACTION_MEDIA_MOUNTED); sEnvironmentToBroadcast.put(Environment.MEDIA_EJECTING, Intent.ACTION_MEDIA_EJECT)138 sEnvironmentToBroadcast.put(Environment.MEDIA_EJECTING, Intent.ACTION_MEDIA_EJECT); sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTABLE, Intent.ACTION_MEDIA_UNMOUNTABLE)139 sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTABLE, Intent.ACTION_MEDIA_UNMOUNTABLE); sEnvironmentToBroadcast.put(Environment.MEDIA_REMOVED, Intent.ACTION_MEDIA_REMOVED)140 sEnvironmentToBroadcast.put(Environment.MEDIA_REMOVED, Intent.ACTION_MEDIA_REMOVED); sEnvironmentToBroadcast.put(Environment.MEDIA_BAD_REMOVAL, Intent.ACTION_MEDIA_BAD_REMOVAL)141 sEnvironmentToBroadcast.put(Environment.MEDIA_BAD_REMOVAL, Intent.ACTION_MEDIA_BAD_REMOVAL); 142 sStateToDescrip.put(VolumeInfo.STATE_UNMOUNTED, R.string.ext_media_status_unmounted)143 sStateToDescrip.put(VolumeInfo.STATE_UNMOUNTED, R.string.ext_media_status_unmounted); sStateToDescrip.put(VolumeInfo.STATE_CHECKING, R.string.ext_media_status_checking)144 sStateToDescrip.put(VolumeInfo.STATE_CHECKING, R.string.ext_media_status_checking); sStateToDescrip.put(VolumeInfo.STATE_MOUNTED, R.string.ext_media_status_mounted)145 sStateToDescrip.put(VolumeInfo.STATE_MOUNTED, R.string.ext_media_status_mounted); sStateToDescrip.put(VolumeInfo.STATE_MOUNTED_READ_ONLY, R.string.ext_media_status_mounted_ro)146 sStateToDescrip.put(VolumeInfo.STATE_MOUNTED_READ_ONLY, R.string.ext_media_status_mounted_ro); sStateToDescrip.put(VolumeInfo.STATE_FORMATTING, R.string.ext_media_status_formatting)147 sStateToDescrip.put(VolumeInfo.STATE_FORMATTING, R.string.ext_media_status_formatting); sStateToDescrip.put(VolumeInfo.STATE_EJECTING, R.string.ext_media_status_ejecting)148 sStateToDescrip.put(VolumeInfo.STATE_EJECTING, R.string.ext_media_status_ejecting); sStateToDescrip.put(VolumeInfo.STATE_UNMOUNTABLE, R.string.ext_media_status_unmountable)149 sStateToDescrip.put(VolumeInfo.STATE_UNMOUNTABLE, R.string.ext_media_status_unmountable); sStateToDescrip.put(VolumeInfo.STATE_REMOVED, R.string.ext_media_status_removed)150 sStateToDescrip.put(VolumeInfo.STATE_REMOVED, R.string.ext_media_status_removed); sStateToDescrip.put(VolumeInfo.STATE_BAD_REMOVAL, R.string.ext_media_status_bad_removal)151 sStateToDescrip.put(VolumeInfo.STATE_BAD_REMOVAL, R.string.ext_media_status_bad_removal); 152 } 153 154 /** vold state */ 155 public final String id; 156 @UnsupportedAppUsage 157 public final int type; 158 @UnsupportedAppUsage 159 public final DiskInfo disk; 160 public final String partGuid; 161 public int mountFlags = 0; 162 public int mountUserId = UserHandle.USER_NULL; 163 @UnsupportedAppUsage 164 public int state = STATE_UNMOUNTED; 165 public String fsType; 166 @UnsupportedAppUsage 167 public String fsUuid; 168 @UnsupportedAppUsage 169 public String fsLabel; 170 @UnsupportedAppUsage 171 public String path; 172 @UnsupportedAppUsage 173 public String internalPath; 174 VolumeInfo(String id, int type, DiskInfo disk, String partGuid)175 public VolumeInfo(String id, int type, DiskInfo disk, String partGuid) { 176 this.id = Preconditions.checkNotNull(id); 177 this.type = type; 178 this.disk = disk; 179 this.partGuid = partGuid; 180 } 181 182 @UnsupportedAppUsage VolumeInfo(Parcel parcel)183 public VolumeInfo(Parcel parcel) { 184 id = parcel.readString8(); 185 type = parcel.readInt(); 186 if (parcel.readInt() != 0) { 187 disk = DiskInfo.CREATOR.createFromParcel(parcel); 188 } else { 189 disk = null; 190 } 191 partGuid = parcel.readString8(); 192 mountFlags = parcel.readInt(); 193 mountUserId = parcel.readInt(); 194 state = parcel.readInt(); 195 fsType = parcel.readString8(); 196 fsUuid = parcel.readString8(); 197 fsLabel = parcel.readString8(); 198 path = parcel.readString8(); 199 internalPath = parcel.readString8(); 200 } 201 202 @UnsupportedAppUsage getEnvironmentForState(int state)203 public static @NonNull String getEnvironmentForState(int state) { 204 final String envState = sStateToEnvironment.get(state); 205 if (envState != null) { 206 return envState; 207 } else { 208 return Environment.MEDIA_UNKNOWN; 209 } 210 } 211 getBroadcastForEnvironment(String envState)212 public static @Nullable String getBroadcastForEnvironment(String envState) { 213 return sEnvironmentToBroadcast.get(envState); 214 } 215 getBroadcastForState(int state)216 public static @Nullable String getBroadcastForState(int state) { 217 return getBroadcastForEnvironment(getEnvironmentForState(state)); 218 } 219 getDescriptionComparator()220 public static @NonNull Comparator<VolumeInfo> getDescriptionComparator() { 221 return sDescriptionComparator; 222 } 223 224 @UnsupportedAppUsage getId()225 public @NonNull String getId() { 226 return id; 227 } 228 229 @UnsupportedAppUsage getDisk()230 public @Nullable DiskInfo getDisk() { 231 return disk; 232 } 233 234 @UnsupportedAppUsage getDiskId()235 public @Nullable String getDiskId() { 236 return (disk != null) ? disk.id : null; 237 } 238 239 @UnsupportedAppUsage getType()240 public int getType() { 241 return type; 242 } 243 244 @UnsupportedAppUsage getState()245 public int getState() { 246 return state; 247 } 248 getStateDescription()249 public int getStateDescription() { 250 return sStateToDescrip.get(state, 0); 251 } 252 253 @UnsupportedAppUsage getFsUuid()254 public @Nullable String getFsUuid() { 255 return fsUuid; 256 } 257 getNormalizedFsUuid()258 public @Nullable String getNormalizedFsUuid() { 259 return fsUuid != null ? fsUuid.toLowerCase(Locale.US) : null; 260 } 261 262 @UnsupportedAppUsage getMountUserId()263 public int getMountUserId() { 264 return mountUserId; 265 } 266 267 @UnsupportedAppUsage getDescription()268 public @Nullable String getDescription() { 269 if (ID_PRIVATE_INTERNAL.equals(id) || id.startsWith(ID_EMULATED_INTERNAL + ";")) { 270 return Resources.getSystem().getString(com.android.internal.R.string.storage_internal); 271 } else if (!TextUtils.isEmpty(fsLabel)) { 272 return fsLabel; 273 } else { 274 return null; 275 } 276 } 277 278 @UnsupportedAppUsage isMountedReadable()279 public boolean isMountedReadable() { 280 return state == STATE_MOUNTED || state == STATE_MOUNTED_READ_ONLY; 281 } 282 283 @UnsupportedAppUsage isMountedWritable()284 public boolean isMountedWritable() { 285 return state == STATE_MOUNTED; 286 } 287 288 @UnsupportedAppUsage isPrimary()289 public boolean isPrimary() { 290 return (mountFlags & MOUNT_FLAG_PRIMARY) != 0; 291 } 292 293 @UnsupportedAppUsage isPrimaryPhysical()294 public boolean isPrimaryPhysical() { 295 return isPrimary() && (getType() == TYPE_PUBLIC); 296 } 297 298 @UnsupportedAppUsage isVisible()299 public boolean isVisible() { 300 return (mountFlags & MOUNT_FLAG_VISIBLE) != 0; 301 } 302 isVisibleForUser(int userId)303 public boolean isVisibleForUser(int userId) { 304 if ((type == TYPE_PUBLIC || type == TYPE_STUB || type == TYPE_EMULATED) 305 && mountUserId == userId) { 306 return isVisible(); 307 } 308 return false; 309 } 310 311 /** 312 * Returns {@code true} if this volume is the primary emulated volume for {@code userId}, 313 * {@code false} otherwise. 314 */ 315 @UnsupportedAppUsage isPrimaryEmulatedForUser(int userId)316 public boolean isPrimaryEmulatedForUser(int userId) { 317 return id.equals(ID_EMULATED_INTERNAL + ";" + userId); 318 } 319 isVisibleForRead(int userId)320 public boolean isVisibleForRead(int userId) { 321 return isVisibleForUser(userId); 322 } 323 324 @UnsupportedAppUsage isVisibleForWrite(int userId)325 public boolean isVisibleForWrite(int userId) { 326 return isVisibleForUser(userId); 327 } 328 329 @UnsupportedAppUsage getPath()330 public File getPath() { 331 return (path != null) ? new File(path) : null; 332 } 333 334 @UnsupportedAppUsage getInternalPath()335 public File getInternalPath() { 336 return (internalPath != null) ? new File(internalPath) : null; 337 } 338 339 @UnsupportedAppUsage getPathForUser(int userId)340 public File getPathForUser(int userId) { 341 if (path == null) { 342 return null; 343 } else if (type == TYPE_PUBLIC || type == TYPE_STUB) { 344 return new File(path); 345 } else if (type == TYPE_EMULATED) { 346 return new File(path, Integer.toString(userId)); 347 } else { 348 return null; 349 } 350 } 351 352 /** 353 * Path which is accessible to apps holding 354 * {@link android.Manifest.permission#WRITE_MEDIA_STORAGE}. 355 */ 356 @UnsupportedAppUsage getInternalPathForUser(int userId)357 public File getInternalPathForUser(int userId) { 358 if (path == null) { 359 return null; 360 } else if (type == TYPE_PUBLIC || type == TYPE_STUB) { 361 // TODO: plumb through cleaner path from vold 362 return new File(path.replace("/storage/", "/mnt/media_rw/")); 363 } else { 364 return getPathForUser(userId); 365 } 366 } 367 368 @UnsupportedAppUsage buildStorageVolume(Context context, int userId, boolean reportUnmounted)369 public StorageVolume buildStorageVolume(Context context, int userId, boolean reportUnmounted) { 370 final StorageManager storage = context.getSystemService(StorageManager.class); 371 372 final boolean removable; 373 final boolean emulated; 374 final boolean allowMassStorage = false; 375 final String envState = reportUnmounted 376 ? Environment.MEDIA_UNMOUNTED : getEnvironmentForState(state); 377 378 File userPath = getPathForUser(userId); 379 if (userPath == null) { 380 userPath = new File("/dev/null"); 381 } 382 File internalPath = getInternalPathForUser(userId); 383 if (internalPath == null) { 384 internalPath = new File("/dev/null"); 385 } 386 387 String description = null; 388 String derivedFsUuid = fsUuid; 389 long maxFileSize = 0; 390 391 if (type == TYPE_EMULATED) { 392 emulated = true; 393 394 final VolumeInfo privateVol = storage.findPrivateForEmulated(this); 395 if (privateVol != null) { 396 description = storage.getBestVolumeDescription(privateVol); 397 derivedFsUuid = privateVol.fsUuid; 398 } 399 400 if (isPrimaryEmulatedForUser(userId)) { 401 removable = false; 402 } else { 403 removable = true; 404 } 405 406 } else if (type == TYPE_PUBLIC || type == TYPE_STUB) { 407 emulated = false; 408 removable = true; 409 410 description = storage.getBestVolumeDescription(this); 411 412 if ("vfat".equals(fsType)) { 413 maxFileSize = 4294967295L; 414 } 415 416 } else { 417 throw new IllegalStateException("Unexpected volume type " + type); 418 } 419 420 if (description == null) { 421 description = context.getString(android.R.string.unknownName); 422 } 423 424 return new StorageVolume(id, userPath, internalPath, description, isPrimary(), removable, 425 emulated, allowMassStorage, maxFileSize, new UserHandle(userId), 426 derivedFsUuid, envState); 427 } 428 429 @UnsupportedAppUsage buildStableMtpStorageId(String fsUuid)430 public static int buildStableMtpStorageId(String fsUuid) { 431 if (TextUtils.isEmpty(fsUuid)) { 432 return StorageVolume.STORAGE_ID_INVALID; 433 } else { 434 int hash = 0; 435 for (int i = 0; i < fsUuid.length(); ++i) { 436 hash = 31 * hash + fsUuid.charAt(i); 437 } 438 hash = (hash ^ (hash << 16)) & 0xffff0000; 439 // Work around values that the spec doesn't allow, or that we've 440 // reserved for primary 441 if (hash == 0x00000000) hash = 0x00020000; 442 if (hash == 0x00010000) hash = 0x00020000; 443 if (hash == 0xffff0000) hash = 0xfffe0000; 444 return hash | 0x0001; 445 } 446 } 447 448 // TODO: avoid this layering violation 449 private static final String DOCUMENT_AUTHORITY = "com.android.externalstorage.documents"; 450 private static final String DOCUMENT_ROOT_PRIMARY_EMULATED = "primary"; 451 452 /** 453 * Build an intent to browse the contents of this volume. Only valid for 454 * {@link #TYPE_EMULATED} or {@link #TYPE_PUBLIC}. 455 */ 456 @UnsupportedAppUsage buildBrowseIntent()457 public @Nullable Intent buildBrowseIntent() { 458 return buildBrowseIntentForUser(UserHandle.myUserId()); 459 } 460 buildBrowseIntentForUser(int userId)461 public @Nullable Intent buildBrowseIntentForUser(int userId) { 462 final Uri uri; 463 if ((type == VolumeInfo.TYPE_PUBLIC || type == VolumeInfo.TYPE_STUB) 464 && mountUserId == userId) { 465 uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY, fsUuid); 466 } else if (type == VolumeInfo.TYPE_EMULATED && isPrimary()) { 467 uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY, 468 DOCUMENT_ROOT_PRIMARY_EMULATED); 469 } else { 470 return null; 471 } 472 473 final Intent intent = new Intent(Intent.ACTION_VIEW); 474 intent.addCategory(Intent.CATEGORY_DEFAULT); 475 intent.setDataAndType(uri, DocumentsContract.Root.MIME_TYPE_ITEM); 476 477 // note that docsui treats this as *force* show advanced. So sending 478 // false permits advanced to be shown based on user preferences. 479 intent.putExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, isPrimary()); 480 return intent; 481 } 482 483 @Override toString()484 public String toString() { 485 final CharArrayWriter writer = new CharArrayWriter(); 486 dump(new IndentingPrintWriter(writer, " ", 80)); 487 return writer.toString(); 488 } 489 dump(IndentingPrintWriter pw)490 public void dump(IndentingPrintWriter pw) { 491 pw.println("VolumeInfo{" + id + "}:"); 492 pw.increaseIndent(); 493 pw.printPair("type", DebugUtils.valueToString(getClass(), "TYPE_", type)); 494 pw.printPair("diskId", getDiskId()); 495 pw.printPair("partGuid", partGuid); 496 pw.printPair("mountFlags", DebugUtils.flagsToString(getClass(), "MOUNT_FLAG_", mountFlags)); 497 pw.printPair("mountUserId", mountUserId); 498 pw.printPair("state", DebugUtils.valueToString(getClass(), "STATE_", state)); 499 pw.println(); 500 pw.printPair("fsType", fsType); 501 pw.printPair("fsUuid", fsUuid); 502 pw.printPair("fsLabel", fsLabel); 503 pw.println(); 504 pw.printPair("path", path); 505 pw.printPair("internalPath", internalPath); 506 pw.decreaseIndent(); 507 pw.println(); 508 } 509 510 @Override clone()511 public VolumeInfo clone() { 512 final Parcel temp = Parcel.obtain(); 513 try { 514 writeToParcel(temp, 0); 515 temp.setDataPosition(0); 516 return CREATOR.createFromParcel(temp); 517 } finally { 518 temp.recycle(); 519 } 520 } 521 522 @Override equals(Object o)523 public boolean equals(Object o) { 524 if (o instanceof VolumeInfo) { 525 return Objects.equals(id, ((VolumeInfo) o).id); 526 } else { 527 return false; 528 } 529 } 530 531 @Override hashCode()532 public int hashCode() { 533 return id.hashCode(); 534 } 535 536 @UnsupportedAppUsage 537 public static final @android.annotation.NonNull Creator<VolumeInfo> CREATOR = new Creator<VolumeInfo>() { 538 @Override 539 public VolumeInfo createFromParcel(Parcel in) { 540 return new VolumeInfo(in); 541 } 542 543 @Override 544 public VolumeInfo[] newArray(int size) { 545 return new VolumeInfo[size]; 546 } 547 }; 548 549 @Override describeContents()550 public int describeContents() { 551 return 0; 552 } 553 554 @Override writeToParcel(Parcel parcel, int flags)555 public void writeToParcel(Parcel parcel, int flags) { 556 parcel.writeString8(id); 557 parcel.writeInt(type); 558 if (disk != null) { 559 parcel.writeInt(1); 560 disk.writeToParcel(parcel, flags); 561 } else { 562 parcel.writeInt(0); 563 } 564 parcel.writeString8(partGuid); 565 parcel.writeInt(mountFlags); 566 parcel.writeInt(mountUserId); 567 parcel.writeInt(state); 568 parcel.writeString8(fsType); 569 parcel.writeString8(fsUuid); 570 parcel.writeString8(fsLabel); 571 parcel.writeString8(path); 572 parcel.writeString8(internalPath); 573 } 574 } 575