|
|
|
|
@@ -16,11 +16,17 @@
|
|
|
|
|
|
|
|
|
|
package android.os.storage;
|
|
|
|
|
|
|
|
|
|
import android.annotation.NonNull;
|
|
|
|
|
import android.annotation.Nullable;
|
|
|
|
|
import android.content.Context;
|
|
|
|
|
import android.content.Intent;
|
|
|
|
|
import android.net.TrafficStats;
|
|
|
|
|
import android.net.Uri;
|
|
|
|
|
import android.os.Environment;
|
|
|
|
|
import android.os.Parcel;
|
|
|
|
|
import android.os.Parcelable;
|
|
|
|
|
import android.os.UserHandle;
|
|
|
|
|
import android.provider.DocumentsContract;
|
|
|
|
|
|
|
|
|
|
import com.android.internal.util.IndentingPrintWriter;
|
|
|
|
|
import com.android.internal.util.Preconditions;
|
|
|
|
|
@@ -29,14 +35,46 @@ import java.io.CharArrayWriter;
|
|
|
|
|
import java.io.File;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Information about a storage volume that may be mounted. This is a legacy
|
|
|
|
|
* specialization of {@link VolumeInfo} which describes the volume for a
|
|
|
|
|
* specific user.
|
|
|
|
|
* <p>
|
|
|
|
|
* This class may be deprecated in the future.
|
|
|
|
|
* Information about a shared/external storage volume for a specific user.
|
|
|
|
|
*
|
|
|
|
|
* @hide
|
|
|
|
|
* <p>
|
|
|
|
|
* A device always has one (and one only) primary storage volume, but it could have extra volumes,
|
|
|
|
|
* like SD cards and USB drives. This object represents the logical view of a storage
|
|
|
|
|
* volume for a specific user: different users might have different views for the same physical
|
|
|
|
|
* volume (for example, if the volume is a built-in emulated storage).
|
|
|
|
|
*
|
|
|
|
|
* <p>
|
|
|
|
|
* The storage volume is not necessarily mounted, applications should use {@link #getState()} to
|
|
|
|
|
* verify its state.
|
|
|
|
|
*
|
|
|
|
|
* <p>
|
|
|
|
|
* Applications willing to read or write to this storage volume needs to get a permission from the
|
|
|
|
|
* user first, which can be achieved in the following ways:
|
|
|
|
|
*
|
|
|
|
|
* <ul>
|
|
|
|
|
* <li>To get access to standard directories (like the {@link Environment#DIRECTORY_PICTURES}), they
|
|
|
|
|
* can use the {@link #createAccessIntent(String)}. This is the recommend way, since it provides a
|
|
|
|
|
* simpler API and narrows the access to the given directory (and its descendants).
|
|
|
|
|
* <li>To get access to any directory (and its descendants), they can use the Storage Area Framework
|
|
|
|
|
* APIs (such as {@link Intent#ACTION_OPEN_DOCUMENT} and {@link Intent#ACTION_OPEN_DOCUMENT_TREE},
|
|
|
|
|
* although these APIs do not guarantee the user will select this specific volume.
|
|
|
|
|
* <li>To get read and write access to the primary storage volume, applications can declare the
|
|
|
|
|
* {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} and
|
|
|
|
|
* {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} permissions respectively, with the
|
|
|
|
|
* latter including the former. This approach is discouraged, since users may be hesitant to grant
|
|
|
|
|
* broad access to all files contained on a storage device.
|
|
|
|
|
* </ul>
|
|
|
|
|
*
|
|
|
|
|
* <p>It can be obtained through {@link StorageManager#getVolumeList()} and
|
|
|
|
|
* {@link StorageManager#getPrimaryVolume()} and also as an extra in some broadcasts
|
|
|
|
|
* (see {@link #EXTRA_STORAGE_VOLUME}).
|
|
|
|
|
*
|
|
|
|
|
* <p>
|
|
|
|
|
* See {@link Environment#getExternalStorageDirectory()} for more info about shared/external
|
|
|
|
|
* storage semantics.
|
|
|
|
|
*/
|
|
|
|
|
// NOTE: This is a legacy specialization of VolumeInfo which describes the volume for a specific
|
|
|
|
|
// user, but is now part of the public API.
|
|
|
|
|
public class StorageVolume implements Parcelable {
|
|
|
|
|
|
|
|
|
|
private final String mId;
|
|
|
|
|
@@ -53,14 +91,23 @@ public class StorageVolume implements Parcelable {
|
|
|
|
|
private final String mFsUuid;
|
|
|
|
|
private final String mState;
|
|
|
|
|
|
|
|
|
|
// StorageVolume extra for ACTION_MEDIA_REMOVED, ACTION_MEDIA_UNMOUNTED, ACTION_MEDIA_CHECKING,
|
|
|
|
|
// ACTION_MEDIA_NOFS, ACTION_MEDIA_MOUNTED, ACTION_MEDIA_SHARED, ACTION_MEDIA_UNSHARED,
|
|
|
|
|
// ACTION_MEDIA_BAD_REMOVAL, ACTION_MEDIA_UNMOUNTABLE and ACTION_MEDIA_EJECT broadcasts.
|
|
|
|
|
public static final String EXTRA_STORAGE_VOLUME = "storage_volume";
|
|
|
|
|
/**
|
|
|
|
|
* Name of the {@link Parcelable} extra in the {@link Intent#ACTION_MEDIA_REMOVED},
|
|
|
|
|
* {@link Intent#ACTION_MEDIA_UNMOUNTED}, {@link Intent#ACTION_MEDIA_CHECKING},
|
|
|
|
|
* {@link Intent#ACTION_MEDIA_NOFS}, {@link Intent#ACTION_MEDIA_MOUNTED},
|
|
|
|
|
* {@link Intent#ACTION_MEDIA_SHARED}, {@link Intent#ACTION_MEDIA_BAD_REMOVAL},
|
|
|
|
|
* {@link Intent#ACTION_MEDIA_UNMOUNTABLE}, and {@link Intent#ACTION_MEDIA_EJECT} broadcast that
|
|
|
|
|
* contains a {@link StorageVolume}.
|
|
|
|
|
*/
|
|
|
|
|
// Also sent on ACTION_MEDIA_UNSHARED, which is @hide
|
|
|
|
|
public static final String EXTRA_STORAGE_VOLUME = "android.os.storage.extra.STORAGE_VOLUME";
|
|
|
|
|
|
|
|
|
|
/** {@hide} */
|
|
|
|
|
public static final int STORAGE_ID_INVALID = 0x00000000;
|
|
|
|
|
/** {@hide} */
|
|
|
|
|
public static final int STORAGE_ID_PRIMARY = 0x00010001;
|
|
|
|
|
|
|
|
|
|
/** {@hide} */
|
|
|
|
|
public StorageVolume(String id, int storageId, File path, String description, boolean primary,
|
|
|
|
|
boolean removable, boolean emulated, long mtpReserveSize, boolean allowMassStorage,
|
|
|
|
|
long maxFileSize, UserHandle owner, String fsUuid, String state) {
|
|
|
|
|
@@ -95,6 +142,7 @@ public class StorageVolume implements Parcelable {
|
|
|
|
|
mState = in.readString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** {@hide} */
|
|
|
|
|
public String getId() {
|
|
|
|
|
return mId;
|
|
|
|
|
}
|
|
|
|
|
@@ -103,17 +151,19 @@ public class StorageVolume implements Parcelable {
|
|
|
|
|
* Returns the mount path for the volume.
|
|
|
|
|
*
|
|
|
|
|
* @return the mount path
|
|
|
|
|
* @hide
|
|
|
|
|
*/
|
|
|
|
|
public String getPath() {
|
|
|
|
|
return mPath.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** {@hide} */
|
|
|
|
|
public File getPathFile() {
|
|
|
|
|
return mPath;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns a user visible description of the volume.
|
|
|
|
|
* Returns a user-visible description of the volume.
|
|
|
|
|
*
|
|
|
|
|
* @return the volume description
|
|
|
|
|
*/
|
|
|
|
|
@@ -121,6 +171,10 @@ public class StorageVolume implements Parcelable {
|
|
|
|
|
return mDescription;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns true if the volume is the primary shared/external storage, which is the volume
|
|
|
|
|
* backed by {@link Environment#getExternalStorageDirectory()}.
|
|
|
|
|
*/
|
|
|
|
|
public boolean isPrimary() {
|
|
|
|
|
return mPrimary;
|
|
|
|
|
}
|
|
|
|
|
@@ -148,6 +202,7 @@ public class StorageVolume implements Parcelable {
|
|
|
|
|
* this is also used for the storage_id column in the media provider.
|
|
|
|
|
*
|
|
|
|
|
* @return MTP storage ID
|
|
|
|
|
* @hide
|
|
|
|
|
*/
|
|
|
|
|
public int getStorageId() {
|
|
|
|
|
return mStorageId;
|
|
|
|
|
@@ -164,6 +219,7 @@ public class StorageVolume implements Parcelable {
|
|
|
|
|
* too close to full.
|
|
|
|
|
*
|
|
|
|
|
* @return MTP reserve space
|
|
|
|
|
* @hide
|
|
|
|
|
*/
|
|
|
|
|
public int getMtpReserveSpace() {
|
|
|
|
|
return (int) (mMtpReserveSize / TrafficStats.MB_IN_BYTES);
|
|
|
|
|
@@ -173,6 +229,7 @@ public class StorageVolume implements Parcelable {
|
|
|
|
|
* Returns true if this volume can be shared via USB mass storage.
|
|
|
|
|
*
|
|
|
|
|
* @return whether mass storage is allowed
|
|
|
|
|
* @hide
|
|
|
|
|
*/
|
|
|
|
|
public boolean allowMassStorage() {
|
|
|
|
|
return mAllowMassStorage;
|
|
|
|
|
@@ -182,22 +239,28 @@ public class StorageVolume implements Parcelable {
|
|
|
|
|
* Returns maximum file size for the volume, or zero if it is unbounded.
|
|
|
|
|
*
|
|
|
|
|
* @return maximum file size
|
|
|
|
|
* @hide
|
|
|
|
|
*/
|
|
|
|
|
public long getMaxFileSize() {
|
|
|
|
|
return mMaxFileSize;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** {@hide} */
|
|
|
|
|
public UserHandle getOwner() {
|
|
|
|
|
return mOwner;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public String getUuid() {
|
|
|
|
|
/**
|
|
|
|
|
* Gets the volume UUID, if any.
|
|
|
|
|
*/
|
|
|
|
|
public @Nullable String getUuid() {
|
|
|
|
|
return mFsUuid;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Parse and return volume UUID as FAT volume ID, or return -1 if unable to
|
|
|
|
|
* parse or UUID is unknown.
|
|
|
|
|
* @hide
|
|
|
|
|
*/
|
|
|
|
|
public int getFatVolumeId() {
|
|
|
|
|
if (mFsUuid == null || mFsUuid.length() != 9) {
|
|
|
|
|
@@ -210,14 +273,56 @@ public class StorageVolume implements Parcelable {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** {@hide} */
|
|
|
|
|
public String getUserLabel() {
|
|
|
|
|
return mDescription;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Returns the current state of the volume.
|
|
|
|
|
*
|
|
|
|
|
* @return one of {@link Environment#MEDIA_UNKNOWN}, {@link Environment#MEDIA_REMOVED},
|
|
|
|
|
* {@link Environment#MEDIA_UNMOUNTED}, {@link Environment#MEDIA_CHECKING},
|
|
|
|
|
* {@link Environment#MEDIA_NOFS}, {@link Environment#MEDIA_MOUNTED},
|
|
|
|
|
* {@link Environment#MEDIA_MOUNTED_READ_ONLY}, {@link Environment#MEDIA_SHARED},
|
|
|
|
|
* {@link Environment#MEDIA_BAD_REMOVAL}, or {@link Environment#MEDIA_UNMOUNTABLE}.
|
|
|
|
|
*/
|
|
|
|
|
public String getState() {
|
|
|
|
|
return mState;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Builds an intent to give access to a standard storage directory after obtaining the user's
|
|
|
|
|
* approval.
|
|
|
|
|
* <p>
|
|
|
|
|
* When invoked, the system will ask the user to grant access to the requested directory (and
|
|
|
|
|
* its descendants). The result of the request will be returned to the activity through the
|
|
|
|
|
* {@code onActivityResult} method.
|
|
|
|
|
* <p>
|
|
|
|
|
* To gain access to descendants (child, grandchild, etc) documents, use
|
|
|
|
|
* {@link DocumentsContract#buildDocumentUriUsingTree(Uri, String)}, or
|
|
|
|
|
* {@link DocumentsContract#buildChildDocumentsUriUsingTree(Uri, String)} with the returned URI.
|
|
|
|
|
*
|
|
|
|
|
* <b>If your application only needs to store internal data, consider using
|
|
|
|
|
* {@link Context#getExternalFilesDirs(String) Context.getExternalFilesDirs},
|
|
|
|
|
* {@link Context#getExternalCacheDirs()}, or
|
|
|
|
|
* {@link Context#getExternalMediaDirs()}, which require no permissions to read or write.
|
|
|
|
|
*
|
|
|
|
|
* @param directoryName must be one of
|
|
|
|
|
* {@link Environment#DIRECTORY_MUSIC}, {@link Environment#DIRECTORY_PODCASTS},
|
|
|
|
|
* {@link Environment#DIRECTORY_RINGTONES}, {@link Environment#DIRECTORY_ALARMS},
|
|
|
|
|
* {@link Environment#DIRECTORY_NOTIFICATIONS}, {@link Environment#DIRECTORY_PICTURES},
|
|
|
|
|
* {@link Environment#DIRECTORY_MOVIES}, {@link Environment#DIRECTORY_DOWNLOADS},
|
|
|
|
|
* {@link Environment#DIRECTORY_DCIM}, or {@link Environment#DIRECTORY_DOCUMENTS}
|
|
|
|
|
*
|
|
|
|
|
* @see DocumentsContract
|
|
|
|
|
*/
|
|
|
|
|
public Intent createAccessIntent(@NonNull String directoryName) {
|
|
|
|
|
final Intent intent = new Intent(Intent.ACTION_OPEN_EXTERNAL_DIRECTORY);
|
|
|
|
|
intent.setData(Uri.fromFile(new File(mPath, directoryName)));
|
|
|
|
|
return intent;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public boolean equals(Object obj) {
|
|
|
|
|
if (obj instanceof StorageVolume && mPath != null) {
|
|
|
|
|
@@ -234,11 +339,23 @@ public class StorageVolume implements Parcelable {
|
|
|
|
|
|
|
|
|
|
@Override
|
|
|
|
|
public String toString() {
|
|
|
|
|
final StringBuilder buffer = new StringBuilder("StorageVolume: ").append(mDescription);
|
|
|
|
|
if (mFsUuid != null) {
|
|
|
|
|
buffer.append(" (").append(mFsUuid).append(")");
|
|
|
|
|
}
|
|
|
|
|
return buffer.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** {@hide} */
|
|
|
|
|
// TODO(b/26742218): find out where toString() is called internally and replace these calls by
|
|
|
|
|
// dump().
|
|
|
|
|
public String dump() {
|
|
|
|
|
final CharArrayWriter writer = new CharArrayWriter();
|
|
|
|
|
dump(new IndentingPrintWriter(writer, " ", 80));
|
|
|
|
|
return writer.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** {@hide} */
|
|
|
|
|
public void dump(IndentingPrintWriter pw) {
|
|
|
|
|
pw.println("StorageVolume:");
|
|
|
|
|
pw.increaseIndent();
|
|
|
|
|
|