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