1 /* 2 * Copyright (C) 2011 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.annotation.SuppressLint; 22 import android.annotation.SystemApi; 23 import android.annotation.TestApi; 24 import android.compat.annotation.UnsupportedAppUsage; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.net.Uri; 28 import android.os.Build; 29 import android.os.Environment; 30 import android.os.Parcel; 31 import android.os.Parcelable; 32 import android.os.UserHandle; 33 import android.provider.DocumentsContract; 34 import android.provider.MediaStore; 35 36 import com.android.internal.util.IndentingPrintWriter; 37 import com.android.internal.util.Preconditions; 38 39 import java.io.CharArrayWriter; 40 import java.io.File; 41 import java.util.Locale; 42 import java.util.UUID; 43 44 /** 45 * Information about a shared/external storage volume for a specific user. 46 * 47 * <p> 48 * A device always has one (and one only) primary storage volume, but it could have extra volumes, 49 * like SD cards and USB drives. This object represents the logical view of a storage 50 * volume for a specific user: different users might have different views for the same physical 51 * volume (for example, if the volume is a built-in emulated storage). 52 * 53 * <p> 54 * The storage volume is not necessarily mounted, applications should use {@link #getState()} to 55 * verify its state. 56 * 57 * <p> 58 * Applications willing to read or write to this storage volume needs to get a permission from the 59 * user first, which can be achieved in the following ways: 60 * 61 * <ul> 62 * <li>To get access to standard directories (like the {@link Environment#DIRECTORY_PICTURES}), they 63 * can use the {@link #createAccessIntent(String)}. This is the recommend way, since it provides a 64 * simpler API and narrows the access to the given directory (and its descendants). 65 * <li>To get access to any directory (and its descendants), they can use the Storage Access 66 * Framework APIs (such as {@link Intent#ACTION_OPEN_DOCUMENT} and 67 * {@link Intent#ACTION_OPEN_DOCUMENT_TREE}, although these APIs do not guarantee the user will 68 * select this specific volume. 69 * <li>To get read and write access to the primary storage volume, applications can declare the 70 * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} and 71 * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} permissions respectively, with the 72 * latter including the former. This approach is discouraged, since users may be hesitant to grant 73 * broad access to all files contained on a storage device. 74 * </ul> 75 * 76 * <p>It can be obtained through {@link StorageManager#getStorageVolumes()} and 77 * {@link StorageManager#getPrimaryStorageVolume()} and also as an extra in some broadcasts 78 * (see {@link #EXTRA_STORAGE_VOLUME}). 79 * 80 * <p> 81 * See {@link Environment#getExternalStorageDirectory()} for more info about shared/external 82 * storage semantics. 83 */ 84 // NOTE: This is a legacy specialization of VolumeInfo which describes the volume for a specific 85 // user, but is now part of the public API. 86 public final class StorageVolume implements Parcelable { 87 88 @UnsupportedAppUsage 89 private final String mId; 90 @UnsupportedAppUsage 91 private final File mPath; 92 private final File mInternalPath; 93 @UnsupportedAppUsage 94 private final String mDescription; 95 @UnsupportedAppUsage 96 private final boolean mPrimary; 97 @UnsupportedAppUsage 98 private final boolean mRemovable; 99 private final boolean mEmulated; 100 private final boolean mExternallyManaged; 101 private final boolean mAllowMassStorage; 102 private final long mMaxFileSize; 103 private final UserHandle mOwner; 104 private final UUID mUuid; 105 private final String mFsUuid; 106 private final String mState; 107 108 /** 109 * Name of the {@link Parcelable} extra in the {@link Intent#ACTION_MEDIA_REMOVED}, 110 * {@link Intent#ACTION_MEDIA_UNMOUNTED}, {@link Intent#ACTION_MEDIA_CHECKING}, 111 * {@link Intent#ACTION_MEDIA_NOFS}, {@link Intent#ACTION_MEDIA_MOUNTED}, 112 * {@link Intent#ACTION_MEDIA_SHARED}, {@link Intent#ACTION_MEDIA_BAD_REMOVAL}, 113 * {@link Intent#ACTION_MEDIA_UNMOUNTABLE}, and {@link Intent#ACTION_MEDIA_EJECT} broadcast that 114 * contains a {@link StorageVolume}. 115 */ 116 // Also sent on ACTION_MEDIA_UNSHARED, which is @hide 117 public static final String EXTRA_STORAGE_VOLUME = "android.os.storage.extra.STORAGE_VOLUME"; 118 119 /** 120 * Name of the String extra used by {@link #createAccessIntent(String) createAccessIntent}. 121 * 122 * @hide 123 */ 124 public static final String EXTRA_DIRECTORY_NAME = "android.os.storage.extra.DIRECTORY_NAME"; 125 126 /** 127 * Name of the intent used by {@link #createAccessIntent(String) createAccessIntent}. 128 */ 129 private static final String ACTION_OPEN_EXTERNAL_DIRECTORY = 130 "android.os.storage.action.OPEN_EXTERNAL_DIRECTORY"; 131 132 /** {@hide} */ 133 public static final int STORAGE_ID_INVALID = 0x00000000; 134 /** {@hide} */ 135 public static final int STORAGE_ID_PRIMARY = 0x00010001; 136 137 /** {@hide} */ StorageVolume(String id, File path, File internalPath, String description, boolean primary, boolean removable, boolean emulated, boolean externallyManaged, boolean allowMassStorage, long maxFileSize, UserHandle owner, UUID uuid, String fsUuid, String state)138 public StorageVolume(String id, File path, File internalPath, String description, 139 boolean primary, boolean removable, boolean emulated, boolean externallyManaged, 140 boolean allowMassStorage, long maxFileSize, UserHandle owner, UUID uuid, String fsUuid, 141 String state) { 142 mId = Preconditions.checkNotNull(id); 143 mPath = Preconditions.checkNotNull(path); 144 mInternalPath = Preconditions.checkNotNull(internalPath); 145 mDescription = Preconditions.checkNotNull(description); 146 mPrimary = primary; 147 mRemovable = removable; 148 mEmulated = emulated; 149 mExternallyManaged = externallyManaged; 150 mAllowMassStorage = allowMassStorage; 151 mMaxFileSize = maxFileSize; 152 mOwner = Preconditions.checkNotNull(owner); 153 mUuid = uuid; 154 mFsUuid = fsUuid; 155 mState = Preconditions.checkNotNull(state); 156 } 157 StorageVolume(Parcel in)158 private StorageVolume(Parcel in) { 159 mId = in.readString8(); 160 mPath = new File(in.readString8()); 161 mInternalPath = new File(in.readString8()); 162 mDescription = in.readString8(); 163 mPrimary = in.readInt() != 0; 164 mRemovable = in.readInt() != 0; 165 mEmulated = in.readInt() != 0; 166 mExternallyManaged = in.readInt() != 0; 167 mAllowMassStorage = in.readInt() != 0; 168 mMaxFileSize = in.readLong(); 169 mOwner = in.readParcelable(null, android.os.UserHandle.class); 170 if (in.readInt() != 0) { 171 mUuid = StorageManager.convert(in.readString8()); 172 } else { 173 mUuid = null; 174 } 175 mFsUuid = in.readString8(); 176 mState = in.readString8(); 177 } 178 179 /** 180 * Return an opaque ID that can be used to identify this volume. 181 * 182 * @hide 183 */ 184 @SystemApi getId()185 public @NonNull String getId() { 186 return mId; 187 } 188 189 /** 190 * Returns the mount path for the volume. 191 * 192 * @return the mount path 193 * @hide 194 */ 195 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "{@link StorageVolume#getDirectory()}") 196 @TestApi getPath()197 public String getPath() { 198 return mPath.toString(); 199 } 200 201 /** 202 * Returns the path of the underlying filesystem. 203 * 204 * @return the internal path 205 * @hide 206 */ getInternalPath()207 public String getInternalPath() { 208 return mInternalPath.toString(); 209 } 210 211 /** {@hide} */ 212 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.Q, publicAlternatives = "{@link StorageVolume#getDirectory()}") getPathFile()213 public File getPathFile() { 214 return mPath; 215 } 216 217 /** 218 * Returns the directory where this volume is currently mounted. 219 * <p> 220 * Direct filesystem access via this path has significant emulation 221 * overhead, and apps are instead strongly encouraged to interact with media 222 * on storage volumes via the {@link MediaStore} APIs. 223 * <p> 224 * This directory does not give apps any additional access beyond what they 225 * already have via {@link MediaStore}. 226 * 227 * @return directory where this volume is mounted, or {@code null} if the 228 * volume is not currently mounted. 229 */ getDirectory()230 public @Nullable File getDirectory() { 231 switch (mState) { 232 case Environment.MEDIA_MOUNTED: 233 case Environment.MEDIA_MOUNTED_READ_ONLY: 234 return mPath; 235 default: 236 return null; 237 } 238 } 239 240 /** 241 * Returns a user-visible description of the volume. 242 * 243 * @return the volume description 244 */ getDescription(Context context)245 public String getDescription(Context context) { 246 return mDescription; 247 } 248 249 /** 250 * Returns true if the volume is the primary shared/external storage, which is the volume 251 * backed by {@link Environment#getExternalStorageDirectory()}. 252 */ isPrimary()253 public boolean isPrimary() { 254 return mPrimary; 255 } 256 257 /** 258 * Returns true if the volume is removable. 259 * 260 * @return is removable 261 */ isRemovable()262 public boolean isRemovable() { 263 return mRemovable; 264 } 265 266 /** 267 * Returns true if the volume is emulated. 268 * 269 * @return is emulated 270 */ isEmulated()271 public boolean isEmulated() { 272 return mEmulated; 273 } 274 275 /** 276 * Returns true if the volume is managed from outside Android. 277 * 278 * @hide 279 */ 280 @SystemApi isExternallyManaged()281 public boolean isExternallyManaged() { 282 return mExternallyManaged; 283 } 284 285 /** 286 * Returns true if this volume can be shared via USB mass storage. 287 * 288 * @return whether mass storage is allowed 289 * @hide 290 */ 291 @UnsupportedAppUsage allowMassStorage()292 public boolean allowMassStorage() { 293 return mAllowMassStorage; 294 } 295 296 /** 297 * Returns maximum file size for the volume, or zero if it is unbounded. 298 * 299 * @return maximum file size 300 * @hide 301 */ 302 @UnsupportedAppUsage getMaxFileSize()303 public long getMaxFileSize() { 304 return mMaxFileSize; 305 } 306 307 /** 308 * Returns the user that owns this volume 309 */ 310 // TODO(b/193460475) : Android Lint handle API change from systemApi to public Api incorrectly 311 @SuppressLint("NewApi") getOwner()312 public @NonNull UserHandle getOwner() { 313 return mOwner; 314 } 315 316 /** 317 * Gets the converted volume UUID. If a valid UUID is returned, it is compatible with other 318 * APIs that make use of {@link UUID} like {@link StorageManager#allocateBytes} and 319 * {@link android.content.pm.ApplicationInfo#storageUuid} 320 * 321 * @return the UUID for the volume or {@code null} for "portable" storage devices which haven't 322 * been adopted. 323 * 324 * @see <a href="https://source.android.com/devices/storage/adoptable">Adoptable storage</a> 325 */ getStorageUuid()326 public @Nullable UUID getStorageUuid() { 327 return mUuid; 328 } 329 330 /** 331 * Gets the volume UUID, if any. 332 */ getUuid()333 public @Nullable String getUuid() { 334 return mFsUuid; 335 } 336 337 /** 338 * Return the volume name that can be used to interact with this storage 339 * device through {@link MediaStore}. 340 * 341 * @return opaque volume name, or {@code null} if this volume is not indexed 342 * by {@link MediaStore}. 343 * @see android.provider.MediaStore.Audio.Media#getContentUri(String) 344 * @see android.provider.MediaStore.Video.Media#getContentUri(String) 345 * @see android.provider.MediaStore.Images.Media#getContentUri(String) 346 */ getMediaStoreVolumeName()347 public @Nullable String getMediaStoreVolumeName() { 348 if (isPrimary()) { 349 return MediaStore.VOLUME_EXTERNAL_PRIMARY; 350 } else { 351 return getNormalizedUuid(); 352 } 353 } 354 355 /** {@hide} */ normalizeUuid(@ullable String fsUuid)356 public static @Nullable String normalizeUuid(@Nullable String fsUuid) { 357 return fsUuid != null ? fsUuid.toLowerCase(Locale.US) : null; 358 } 359 360 /** {@hide} */ getNormalizedUuid()361 public @Nullable String getNormalizedUuid() { 362 return normalizeUuid(mFsUuid); 363 } 364 365 /** 366 * Parse and return volume UUID as FAT volume ID, or return -1 if unable to 367 * parse or UUID is unknown. 368 * @hide 369 */ 370 @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) getFatVolumeId()371 public int getFatVolumeId() { 372 if (mFsUuid == null || mFsUuid.length() != 9) { 373 return -1; 374 } 375 try { 376 return (int) Long.parseLong(mFsUuid.replace("-", ""), 16); 377 } catch (NumberFormatException e) { 378 return -1; 379 } 380 } 381 382 /** {@hide} */ 383 @UnsupportedAppUsage getUserLabel()384 public String getUserLabel() { 385 return mDescription; 386 } 387 388 /** 389 * Returns the current state of the volume. 390 * 391 * @return one of {@link Environment#MEDIA_UNKNOWN}, {@link Environment#MEDIA_REMOVED}, 392 * {@link Environment#MEDIA_UNMOUNTED}, {@link Environment#MEDIA_CHECKING}, 393 * {@link Environment#MEDIA_NOFS}, {@link Environment#MEDIA_MOUNTED}, 394 * {@link Environment#MEDIA_MOUNTED_READ_ONLY}, {@link Environment#MEDIA_SHARED}, 395 * {@link Environment#MEDIA_BAD_REMOVAL}, or {@link Environment#MEDIA_UNMOUNTABLE}. 396 */ getState()397 public String getState() { 398 return mState; 399 } 400 401 /** 402 * Builds an intent to give access to a standard storage directory or entire volume after 403 * obtaining the user's approval. 404 * <p> 405 * When invoked, the system will ask the user to grant access to the requested directory (and 406 * its descendants). The result of the request will be returned to the activity through the 407 * {@code onActivityResult} method. 408 * <p> 409 * To gain access to descendants (child, grandchild, etc) documents, use 410 * {@link DocumentsContract#buildDocumentUriUsingTree(Uri, String)}, or 411 * {@link DocumentsContract#buildChildDocumentsUriUsingTree(Uri, String)} with the returned URI. 412 * <p> 413 * If your application only needs to store internal data, consider using 414 * {@link Context#getExternalFilesDirs(String) Context.getExternalFilesDirs}, 415 * {@link Context#getExternalCacheDirs()}, or {@link Context#getExternalMediaDirs()}, which 416 * require no permissions to read or write. 417 * <p> 418 * Access to the entire volume is only available for non-primary volumes (for the primary 419 * volume, apps can use the {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} and 420 * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} permissions) and should be used 421 * with caution, since users are more likely to deny access when asked for entire volume access 422 * rather than specific directories. 423 * 424 * @param directoryName must be one of {@link Environment#DIRECTORY_MUSIC}, 425 * {@link Environment#DIRECTORY_PODCASTS}, {@link Environment#DIRECTORY_RINGTONES}, 426 * {@link Environment#DIRECTORY_ALARMS}, {@link Environment#DIRECTORY_NOTIFICATIONS}, 427 * {@link Environment#DIRECTORY_PICTURES}, {@link Environment#DIRECTORY_MOVIES}, 428 * {@link Environment#DIRECTORY_DOWNLOADS}, {@link Environment#DIRECTORY_DCIM}, or 429 * {@link Environment#DIRECTORY_DOCUMENTS}, or {@code null} to request access to the 430 * entire volume. 431 * @return intent to request access, or {@code null} if the requested directory is invalid for 432 * that volume. 433 * @see DocumentsContract 434 * @deprecated Callers should migrate to using {@link Intent#ACTION_OPEN_DOCUMENT_TREE} instead. 435 * Launching this {@link Intent} on devices running 436 * {@link android.os.Build.VERSION_CODES#Q} or higher, will immediately finish 437 * with a result code of {@link android.app.Activity#RESULT_CANCELED}. 438 */ 439 @Deprecated createAccessIntent(String directoryName)440 public @Nullable Intent createAccessIntent(String directoryName) { 441 if ((isPrimary() && directoryName == null) || 442 (directoryName != null && !Environment.isStandardDirectory(directoryName))) { 443 return null; 444 } 445 final Intent intent = new Intent(ACTION_OPEN_EXTERNAL_DIRECTORY); 446 intent.putExtra(EXTRA_STORAGE_VOLUME, this); 447 intent.putExtra(EXTRA_DIRECTORY_NAME, directoryName); 448 return intent; 449 } 450 451 /** 452 * Builds an {@link Intent#ACTION_OPEN_DOCUMENT_TREE} to allow the user to grant access to any 453 * directory subtree (or entire volume) from the {@link android.provider.DocumentsProvider}s 454 * available on the device. The initial location of the document navigation will be the root of 455 * this {@link StorageVolume}. 456 * 457 * Note that the returned {@link Intent} simply suggests that the user picks this {@link 458 * StorageVolume} by default, but the user may select a different location. Callers must respect 459 * the user's chosen location, even if it is different from the originally requested location. 460 * 461 * @return intent to {@link Intent#ACTION_OPEN_DOCUMENT_TREE} initially showing the contents 462 * of this {@link StorageVolume} 463 * @see Intent#ACTION_OPEN_DOCUMENT_TREE 464 */ createOpenDocumentTreeIntent()465 @NonNull public Intent createOpenDocumentTreeIntent() { 466 final String rootId = isEmulated() 467 ? DocumentsContract.EXTERNAL_STORAGE_PRIMARY_EMULATED_ROOT_ID 468 : mFsUuid; 469 final Uri rootUri = DocumentsContract.buildRootUri( 470 DocumentsContract.EXTERNAL_STORAGE_PROVIDER_AUTHORITY, rootId); 471 final Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT_TREE) 472 .putExtra(DocumentsContract.EXTRA_INITIAL_URI, rootUri) 473 .putExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, true); 474 return intent; 475 } 476 477 @Override equals(@ullable Object obj)478 public boolean equals(@Nullable Object obj) { 479 if (obj instanceof StorageVolume && mPath != null) { 480 StorageVolume volume = (StorageVolume)obj; 481 return (mPath.equals(volume.mPath)); 482 } 483 return false; 484 } 485 486 @Override hashCode()487 public int hashCode() { 488 return mPath.hashCode(); 489 } 490 491 @Override toString()492 public String toString() { 493 final StringBuilder buffer = new StringBuilder("StorageVolume: ").append(mDescription); 494 if (mFsUuid != null) { 495 buffer.append(" (").append(mFsUuid).append(")"); 496 } 497 return buffer.toString(); 498 } 499 500 /** {@hide} */ 501 // TODO: find out where toString() is called internally and replace these calls by dump(). dump()502 public String dump() { 503 final CharArrayWriter writer = new CharArrayWriter(); 504 dump(new IndentingPrintWriter(writer, " ", 80)); 505 return writer.toString(); 506 } 507 508 /** {@hide} */ dump(IndentingPrintWriter pw)509 public void dump(IndentingPrintWriter pw) { 510 pw.println("StorageVolume:"); 511 pw.increaseIndent(); 512 pw.printPair("mId", mId); 513 pw.printPair("mPath", mPath); 514 pw.printPair("mInternalPath", mInternalPath); 515 pw.printPair("mDescription", mDescription); 516 pw.printPair("mPrimary", mPrimary); 517 pw.printPair("mRemovable", mRemovable); 518 pw.printPair("mEmulated", mEmulated); 519 pw.printPair("mExternallyManaged", mExternallyManaged); 520 pw.printPair("mAllowMassStorage", mAllowMassStorage); 521 pw.printPair("mMaxFileSize", mMaxFileSize); 522 pw.printPair("mOwner", mOwner); 523 pw.printPair("mFsUuid", mFsUuid); 524 pw.printPair("mState", mState); 525 pw.decreaseIndent(); 526 } 527 528 public static final @android.annotation.NonNull Creator<StorageVolume> CREATOR = new Creator<StorageVolume>() { 529 @Override 530 public StorageVolume createFromParcel(Parcel in) { 531 return new StorageVolume(in); 532 } 533 534 @Override 535 public StorageVolume[] newArray(int size) { 536 return new StorageVolume[size]; 537 } 538 }; 539 540 @Override describeContents()541 public int describeContents() { 542 return 0; 543 } 544 545 @Override writeToParcel(Parcel parcel, int flags)546 public void writeToParcel(Parcel parcel, int flags) { 547 parcel.writeString8(mId); 548 parcel.writeString8(mPath.toString()); 549 parcel.writeString8(mInternalPath.toString()); 550 parcel.writeString8(mDescription); 551 parcel.writeInt(mPrimary ? 1 : 0); 552 parcel.writeInt(mRemovable ? 1 : 0); 553 parcel.writeInt(mEmulated ? 1 : 0); 554 parcel.writeInt(mExternallyManaged ? 1 : 0); 555 parcel.writeInt(mAllowMassStorage ? 1 : 0); 556 parcel.writeLong(mMaxFileSize); 557 parcel.writeParcelable(mOwner, flags); 558 if (mUuid != null) { 559 parcel.writeInt(1); 560 parcel.writeString8(StorageManager.convert(mUuid)); 561 } else { 562 parcel.writeInt(0); 563 } 564 parcel.writeString8(mFsUuid); 565 parcel.writeString8(mState); 566 } 567 568 /** @hide */ 569 // This class is used by the mainline test suite, so we have to keep these APIs around across 570 // releases. Consider making this class public to help external developers to write tests as 571 // well. 572 @TestApi 573 public static final class Builder { 574 private String mId; 575 private File mPath; 576 private String mDescription; 577 private boolean mPrimary; 578 private boolean mRemovable; 579 private boolean mEmulated; 580 private UserHandle mOwner; 581 private UUID mStorageUuid; 582 private String mUuid; 583 private String mState; 584 585 @SuppressLint("StreamFiles") Builder( @onNull String id, @NonNull File path, @NonNull String description, @NonNull UserHandle owner, @NonNull String state)586 public Builder( 587 @NonNull String id, @NonNull File path, @NonNull String description, 588 @NonNull UserHandle owner, @NonNull String state) { 589 mId = id; 590 mPath = path; 591 mDescription = description; 592 mOwner = owner; 593 mState = state; 594 } 595 596 @NonNull setStorageUuid(@ullable UUID storageUuid)597 public Builder setStorageUuid(@Nullable UUID storageUuid) { 598 mStorageUuid = storageUuid; 599 return this; 600 } 601 602 @NonNull setUuid(@ullable String uuid)603 public Builder setUuid(@Nullable String uuid) { 604 mUuid = uuid; 605 return this; 606 } 607 608 @NonNull setPrimary(boolean primary)609 public Builder setPrimary(boolean primary) { 610 mPrimary = primary; 611 return this; 612 } 613 614 @NonNull setRemovable(boolean removable)615 public Builder setRemovable(boolean removable) { 616 mRemovable = removable; 617 return this; 618 } 619 620 @NonNull setEmulated(boolean emulated)621 public Builder setEmulated(boolean emulated) { 622 mEmulated = emulated; 623 return this; 624 } 625 626 @NonNull build()627 public StorageVolume build() { 628 return new StorageVolume( 629 mId, 630 mPath, 631 /* internalPath= */ mPath, 632 mDescription, 633 mPrimary, 634 mRemovable, 635 mEmulated, 636 /* externallyManaged= */ false, 637 /* allowMassStorage= */ false, 638 /* maxFileSize= */ 0, 639 mOwner, 640 mStorageUuid, 641 mUuid, 642 mState); 643 } 644 } 645 646 } 647