StorageVolume.java revision 34a9d5271915fb82c22811e6d17691a34b6c52c1
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 17package android.os.storage; 18 19import android.annotation.NonNull; 20import android.annotation.Nullable; 21import android.content.Context; 22import android.content.Intent; 23import android.net.TrafficStats; 24import android.net.Uri; 25import android.os.Environment; 26import android.os.Parcel; 27import android.os.Parcelable; 28import android.os.UserHandle; 29import android.provider.DocumentsContract; 30 31import com.android.internal.util.IndentingPrintWriter; 32import com.android.internal.util.Preconditions; 33 34import java.io.CharArrayWriter; 35import java.io.File; 36 37/** 38 * Information about a shared/external storage volume for a specific user. 39 * 40 * <p> 41 * A device always has one (and one only) primary storage volume, but it could have extra volumes, 42 * like SD cards and USB drives. This object represents the logical view of a storage 43 * volume for a specific user: different users might have different views for the same physical 44 * volume (for example, if the volume is a built-in emulated storage). 45 * 46 * <p> 47 * The storage volume is not necessarily mounted, applications should use {@link #getState()} to 48 * verify its state. 49 * 50 * <p> 51 * Applications willing to read or write to this storage volume needs to get a permission from the 52 * user first, which can be achieved in the following ways: 53 * 54 * <ul> 55 * <li>To get access to standard directories (like the {@link Environment#DIRECTORY_PICTURES}), they 56 * can use the {@link #createAccessIntent(String)}. This is the recommend way, since it provides a 57 * simpler API and narrows the access to the given directory (and its descendants). 58 * <li>To get access to any directory (and its descendants), they can use the Storage Area Framework 59 * APIs (such as {@link Intent#ACTION_OPEN_DOCUMENT} and {@link Intent#ACTION_OPEN_DOCUMENT_TREE}, 60 * although these APIs do not guarantee the user will select this specific volume. 61 * <li>To get read and write access to the primary storage volume, applications can declare the 62 * {@link android.Manifest.permission#READ_EXTERNAL_STORAGE} and 63 * {@link android.Manifest.permission#WRITE_EXTERNAL_STORAGE} permissions respectively, with the 64 * latter including the former. This approach is discouraged, since users may be hesitant to grant 65 * broad access to all files contained on a storage device. 66 * </ul> 67 * 68 * <p>It can be obtained through {@link StorageManager#getVolumeList()} and 69 * {@link StorageManager#getPrimaryVolume()} and also as an extra in some broadcasts 70 * (see {@link #EXTRA_STORAGE_VOLUME}). 71 * 72 * <p> 73 * See {@link Environment#getExternalStorageDirectory()} for more info about shared/external 74 * storage semantics. 75 */ 76// NOTE: This is a legacy specialization of VolumeInfo which describes the volume for a specific 77// user, but is now part of the public API. 78public class StorageVolume implements Parcelable { 79 80 private final String mId; 81 private final int mStorageId; 82 private final File mPath; 83 private final String mDescription; 84 private final boolean mPrimary; 85 private final boolean mRemovable; 86 private final boolean mEmulated; 87 private final long mMtpReserveSize; 88 private final boolean mAllowMassStorage; 89 private final long mMaxFileSize; 90 private final UserHandle mOwner; 91 private final String mFsUuid; 92 private final String mState; 93 94 /** 95 * Name of the {@link Parcelable} extra in the {@link Intent#ACTION_MEDIA_REMOVED}, 96 * {@link Intent#ACTION_MEDIA_UNMOUNTED}, {@link Intent#ACTION_MEDIA_CHECKING}, 97 * {@link Intent#ACTION_MEDIA_NOFS}, {@link Intent#ACTION_MEDIA_MOUNTED}, 98 * {@link Intent#ACTION_MEDIA_SHARED}, {@link Intent#ACTION_MEDIA_BAD_REMOVAL}, 99 * {@link Intent#ACTION_MEDIA_UNMOUNTABLE}, and {@link Intent#ACTION_MEDIA_EJECT} broadcast that 100 * contains a {@link StorageVolume}. 101 */ 102 // Also sent on ACTION_MEDIA_UNSHARED, which is @hide 103 public static final String EXTRA_STORAGE_VOLUME = "android.os.storage.extra.STORAGE_VOLUME"; 104 105 /** 106 * Name of the String extra used by {@link #createAccessIntent(String) createAccessIntent}. 107 * 108 * @hide 109 */ 110 public static final String EXTRA_DIRECTORY_NAME = "android.os.storage.extra.DIRECTORY_NAME"; 111 112 /** 113 * Name of the intent used by {@link #createAccessIntent(String) createAccessIntent}. 114 */ 115 private static final String ACTION_OPEN_EXTERNAL_DIRECTORY = 116 "android.os.storage.action.OPEN_EXTERNAL_DIRECTORY"; 117 118 /** {@hide} */ 119 public static final int STORAGE_ID_INVALID = 0x00000000; 120 /** {@hide} */ 121 public static final int STORAGE_ID_PRIMARY = 0x00010001; 122 123 /** {@hide} */ 124 public StorageVolume(String id, int storageId, File path, String description, boolean primary, 125 boolean removable, boolean emulated, long mtpReserveSize, boolean allowMassStorage, 126 long maxFileSize, UserHandle owner, String fsUuid, String state) { 127 mId = Preconditions.checkNotNull(id); 128 mStorageId = storageId; 129 mPath = Preconditions.checkNotNull(path); 130 mDescription = Preconditions.checkNotNull(description); 131 mPrimary = primary; 132 mRemovable = removable; 133 mEmulated = emulated; 134 mMtpReserveSize = mtpReserveSize; 135 mAllowMassStorage = allowMassStorage; 136 mMaxFileSize = maxFileSize; 137 mOwner = Preconditions.checkNotNull(owner); 138 mFsUuid = fsUuid; 139 mState = Preconditions.checkNotNull(state); 140 } 141 142 private StorageVolume(Parcel in) { 143 mId = in.readString(); 144 mStorageId = in.readInt(); 145 mPath = new File(in.readString()); 146 mDescription = in.readString(); 147 mPrimary = in.readInt() != 0; 148 mRemovable = in.readInt() != 0; 149 mEmulated = in.readInt() != 0; 150 mMtpReserveSize = in.readLong(); 151 mAllowMassStorage = in.readInt() != 0; 152 mMaxFileSize = in.readLong(); 153 mOwner = in.readParcelable(null); 154 mFsUuid = in.readString(); 155 mState = in.readString(); 156 } 157 158 /** {@hide} */ 159 public String getId() { 160 return mId; 161 } 162 163 /** 164 * Returns the mount path for the volume. 165 * 166 * @return the mount path 167 * @hide 168 */ 169 public String getPath() { 170 return mPath.toString(); 171 } 172 173 /** {@hide} */ 174 public File getPathFile() { 175 return mPath; 176 } 177 178 /** 179 * Returns a user-visible description of the volume. 180 * 181 * @return the volume description 182 */ 183 public String getDescription(Context context) { 184 return mDescription; 185 } 186 187 /** 188 * Returns true if the volume is the primary shared/external storage, which is the volume 189 * backed by {@link Environment#getExternalStorageDirectory()}. 190 */ 191 public boolean isPrimary() { 192 return mPrimary; 193 } 194 195 /** 196 * Returns true if the volume is removable. 197 * 198 * @return is removable 199 */ 200 public boolean isRemovable() { 201 return mRemovable; 202 } 203 204 /** 205 * Returns true if the volume is emulated. 206 * 207 * @return is removable 208 */ 209 public boolean isEmulated() { 210 return mEmulated; 211 } 212 213 /** 214 * Returns the MTP storage ID for the volume. 215 * this is also used for the storage_id column in the media provider. 216 * 217 * @return MTP storage ID 218 * @hide 219 */ 220 public int getStorageId() { 221 return mStorageId; 222 } 223 224 /** 225 * Number of megabytes of space to leave unallocated by MTP. 226 * MTP will subtract this value from the free space it reports back 227 * to the host via GetStorageInfo, and will not allow new files to 228 * be added via MTP if there is less than this amount left free in the storage. 229 * If MTP has dedicated storage this value should be zero, but if MTP is 230 * sharing storage with the rest of the system, set this to a positive value 231 * to ensure that MTP activity does not result in the storage being 232 * too close to full. 233 * 234 * @return MTP reserve space 235 * @hide 236 */ 237 public int getMtpReserveSpace() { 238 return (int) (mMtpReserveSize / TrafficStats.MB_IN_BYTES); 239 } 240 241 /** 242 * Returns true if this volume can be shared via USB mass storage. 243 * 244 * @return whether mass storage is allowed 245 * @hide 246 */ 247 public boolean allowMassStorage() { 248 return mAllowMassStorage; 249 } 250 251 /** 252 * Returns maximum file size for the volume, or zero if it is unbounded. 253 * 254 * @return maximum file size 255 * @hide 256 */ 257 public long getMaxFileSize() { 258 return mMaxFileSize; 259 } 260 261 /** {@hide} */ 262 public UserHandle getOwner() { 263 return mOwner; 264 } 265 266 /** 267 * Gets the volume UUID, if any. 268 */ 269 public @Nullable String getUuid() { 270 return mFsUuid; 271 } 272 273 /** 274 * Parse and return volume UUID as FAT volume ID, or return -1 if unable to 275 * parse or UUID is unknown. 276 * @hide 277 */ 278 public int getFatVolumeId() { 279 if (mFsUuid == null || mFsUuid.length() != 9) { 280 return -1; 281 } 282 try { 283 return (int) Long.parseLong(mFsUuid.replace("-", ""), 16); 284 } catch (NumberFormatException e) { 285 return -1; 286 } 287 } 288 289 /** {@hide} */ 290 public String getUserLabel() { 291 return mDescription; 292 } 293 294 /** 295 * Returns the current state of the volume. 296 * 297 * @return one of {@link Environment#MEDIA_UNKNOWN}, {@link Environment#MEDIA_REMOVED}, 298 * {@link Environment#MEDIA_UNMOUNTED}, {@link Environment#MEDIA_CHECKING}, 299 * {@link Environment#MEDIA_NOFS}, {@link Environment#MEDIA_MOUNTED}, 300 * {@link Environment#MEDIA_MOUNTED_READ_ONLY}, {@link Environment#MEDIA_SHARED}, 301 * {@link Environment#MEDIA_BAD_REMOVAL}, or {@link Environment#MEDIA_UNMOUNTABLE}. 302 */ 303 public String getState() { 304 return mState; 305 } 306 307 /** 308 * Builds an intent to give access to a standard storage directory after obtaining the user's 309 * approval. 310 * <p> 311 * When invoked, the system will ask the user to grant access to the requested directory (and 312 * its descendants). The result of the request will be returned to the activity through the 313 * {@code onActivityResult} method. 314 * <p> 315 * To gain access to descendants (child, grandchild, etc) documents, use 316 * {@link DocumentsContract#buildDocumentUriUsingTree(Uri, String)}, or 317 * {@link DocumentsContract#buildChildDocumentsUriUsingTree(Uri, String)} with the returned URI. 318 * 319 * <b>If your application only needs to store internal data, consider using 320 * {@link Context#getExternalFilesDirs(String) Context.getExternalFilesDirs}, 321 * {@link Context#getExternalCacheDirs()}, or 322 * {@link Context#getExternalMediaDirs()}, which require no permissions to read or write. 323 * 324 * @param directoryName must be one of 325 * {@link Environment#DIRECTORY_MUSIC}, {@link Environment#DIRECTORY_PODCASTS}, 326 * {@link Environment#DIRECTORY_RINGTONES}, {@link Environment#DIRECTORY_ALARMS}, 327 * {@link Environment#DIRECTORY_NOTIFICATIONS}, {@link Environment#DIRECTORY_PICTURES}, 328 * {@link Environment#DIRECTORY_MOVIES}, {@link Environment#DIRECTORY_DOWNLOADS}, 329 * {@link Environment#DIRECTORY_DCIM}, or {@link Environment#DIRECTORY_DOCUMENTS} 330 * 331 * @see DocumentsContract 332 */ 333 public Intent createAccessIntent(@NonNull String directoryName) { 334 final Intent intent = new Intent(ACTION_OPEN_EXTERNAL_DIRECTORY); 335 intent.putExtra(EXTRA_STORAGE_VOLUME, this); 336 intent.putExtra(EXTRA_DIRECTORY_NAME, directoryName); 337 return intent; 338 } 339 340 @Override 341 public boolean equals(Object obj) { 342 if (obj instanceof StorageVolume && mPath != null) { 343 StorageVolume volume = (StorageVolume)obj; 344 return (mPath.equals(volume.mPath)); 345 } 346 return false; 347 } 348 349 @Override 350 public int hashCode() { 351 return mPath.hashCode(); 352 } 353 354 @Override 355 public String toString() { 356 final StringBuilder buffer = new StringBuilder("StorageVolume: ").append(mDescription); 357 if (mFsUuid != null) { 358 buffer.append(" (").append(mFsUuid).append(")"); 359 } 360 return buffer.toString(); 361 } 362 363 /** {@hide} */ 364 // TODO(b/26742218): find out where toString() is called internally and replace these calls by 365 // dump(). 366 public String dump() { 367 final CharArrayWriter writer = new CharArrayWriter(); 368 dump(new IndentingPrintWriter(writer, " ", 80)); 369 return writer.toString(); 370 } 371 372 /** {@hide} */ 373 public void dump(IndentingPrintWriter pw) { 374 pw.println("StorageVolume:"); 375 pw.increaseIndent(); 376 pw.printPair("mId", mId); 377 pw.printPair("mStorageId", mStorageId); 378 pw.printPair("mPath", mPath); 379 pw.printPair("mDescription", mDescription); 380 pw.printPair("mPrimary", mPrimary); 381 pw.printPair("mRemovable", mRemovable); 382 pw.printPair("mEmulated", mEmulated); 383 pw.printPair("mMtpReserveSize", mMtpReserveSize); 384 pw.printPair("mAllowMassStorage", mAllowMassStorage); 385 pw.printPair("mMaxFileSize", mMaxFileSize); 386 pw.printPair("mOwner", mOwner); 387 pw.printPair("mFsUuid", mFsUuid); 388 pw.printPair("mState", mState); 389 pw.decreaseIndent(); 390 } 391 392 public static final Creator<StorageVolume> CREATOR = new Creator<StorageVolume>() { 393 @Override 394 public StorageVolume createFromParcel(Parcel in) { 395 return new StorageVolume(in); 396 } 397 398 @Override 399 public StorageVolume[] newArray(int size) { 400 return new StorageVolume[size]; 401 } 402 }; 403 404 @Override 405 public int describeContents() { 406 return 0; 407 } 408 409 @Override 410 public void writeToParcel(Parcel parcel, int flags) { 411 parcel.writeString(mId); 412 parcel.writeInt(mStorageId); 413 parcel.writeString(mPath.toString()); 414 parcel.writeString(mDescription); 415 parcel.writeInt(mPrimary ? 1 : 0); 416 parcel.writeInt(mRemovable ? 1 : 0); 417 parcel.writeInt(mEmulated ? 1 : 0); 418 parcel.writeLong(mMtpReserveSize); 419 parcel.writeInt(mAllowMassStorage ? 1 : 0); 420 parcel.writeLong(mMaxFileSize); 421 parcel.writeParcelable(mOwner, flags); 422 parcel.writeString(mFsUuid); 423 parcel.writeString(mState); 424 } 425} 426