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