VolumeInfo.java revision d95d3bfb2b28a4f21f3fdcd740160c9a61eb0363
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 17package android.os.storage; 18 19import android.annotation.NonNull; 20import android.annotation.Nullable; 21import android.content.Context; 22import android.content.Intent; 23import android.content.res.Resources; 24import android.mtp.MtpStorage; 25import android.net.Uri; 26import android.os.Environment; 27import android.os.Parcel; 28import android.os.Parcelable; 29import android.os.UserHandle; 30import android.provider.DocumentsContract; 31import android.text.TextUtils; 32import android.util.ArrayMap; 33import android.util.DebugUtils; 34import android.util.SparseArray; 35 36import com.android.internal.util.IndentingPrintWriter; 37import com.android.internal.util.Preconditions; 38 39import java.io.CharArrayWriter; 40import java.io.File; 41 42/** 43 * Information about a storage volume that may be mounted. A volume may be a 44 * partition on a physical {@link DiskInfo}, an emulated volume above some other 45 * storage medium, or a standalone container like an ASEC or OBB. 46 * 47 * @hide 48 */ 49public class VolumeInfo implements Parcelable { 50 public static final String EXTRA_VOLUME_ID = "android.os.storage.extra.VOLUME_ID"; 51 52 /** Stub volume representing internal private storage */ 53 public static final String ID_PRIVATE_INTERNAL = "private"; 54 /** Real volume representing internal emulated storage */ 55 public static final String ID_EMULATED_INTERNAL = "emulated"; 56 57 public static final int TYPE_PUBLIC = 0; 58 public static final int TYPE_PRIVATE = 1; 59 public static final int TYPE_EMULATED = 2; 60 public static final int TYPE_ASEC = 3; 61 public static final int TYPE_OBB = 4; 62 63 public static final int STATE_UNMOUNTED = 0; 64 public static final int STATE_MOUNTING = 1; 65 public static final int STATE_MOUNTED = 2; 66 public static final int STATE_FORMATTING = 3; 67 public static final int STATE_UNMOUNTING = 4; 68 public static final int STATE_UNMOUNTABLE = 5; 69 public static final int STATE_REMOVED = 6; 70 71 public static final int FLAG_PRIMARY = 1 << 0; 72 public static final int FLAG_VISIBLE = 1 << 1; 73 74 public static final int USER_FLAG_INITED = 1 << 0; 75 public static final int USER_FLAG_SNOOZED = 1 << 1; 76 77 private static SparseArray<String> sStateToEnvironment = new SparseArray<>(); 78 private static ArrayMap<String, String> sEnvironmentToBroadcast = new ArrayMap<>(); 79 80 static { 81 sStateToEnvironment.put(VolumeInfo.STATE_UNMOUNTED, Environment.MEDIA_UNMOUNTED); 82 sStateToEnvironment.put(VolumeInfo.STATE_MOUNTING, Environment.MEDIA_CHECKING); 83 sStateToEnvironment.put(VolumeInfo.STATE_MOUNTED, Environment.MEDIA_MOUNTED); 84 sStateToEnvironment.put(VolumeInfo.STATE_FORMATTING, Environment.MEDIA_UNMOUNTED); 85 sStateToEnvironment.put(VolumeInfo.STATE_UNMOUNTING, Environment.MEDIA_EJECTING); 86 sStateToEnvironment.put(VolumeInfo.STATE_UNMOUNTABLE, Environment.MEDIA_UNMOUNTABLE); 87 sStateToEnvironment.put(VolumeInfo.STATE_REMOVED, Environment.MEDIA_REMOVED); 88 89 sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTED, Intent.ACTION_MEDIA_UNMOUNTED); 90 sEnvironmentToBroadcast.put(Environment.MEDIA_CHECKING, Intent.ACTION_MEDIA_CHECKING); 91 sEnvironmentToBroadcast.put(Environment.MEDIA_MOUNTED, Intent.ACTION_MEDIA_MOUNTED); 92 sEnvironmentToBroadcast.put(Environment.MEDIA_EJECTING, Intent.ACTION_MEDIA_EJECT); 93 sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTABLE, Intent.ACTION_MEDIA_UNMOUNTABLE); 94 sEnvironmentToBroadcast.put(Environment.MEDIA_REMOVED, Intent.ACTION_MEDIA_REMOVED); 95 } 96 97 /** vold state */ 98 public final String id; 99 public final int type; 100 public int flags = 0; 101 public int userId = -1; 102 public int state = STATE_UNMOUNTED; 103 public String fsType; 104 public String fsUuid; 105 public String fsLabel; 106 public String path; 107 108 /** Framework state */ 109 public final int mtpIndex; 110 public String diskId; 111 public String nickname; 112 public int userFlags = 0; 113 114 public VolumeInfo(String id, int type, int mtpIndex) { 115 this.id = Preconditions.checkNotNull(id); 116 this.type = type; 117 this.mtpIndex = mtpIndex; 118 } 119 120 public VolumeInfo(Parcel parcel) { 121 id = parcel.readString(); 122 type = parcel.readInt(); 123 flags = parcel.readInt(); 124 userId = parcel.readInt(); 125 state = parcel.readInt(); 126 fsType = parcel.readString(); 127 fsUuid = parcel.readString(); 128 fsLabel = parcel.readString(); 129 path = parcel.readString(); 130 mtpIndex = parcel.readInt(); 131 diskId = parcel.readString(); 132 nickname = parcel.readString(); 133 userFlags = parcel.readInt(); 134 } 135 136 public static @NonNull String getEnvironmentForState(int state) { 137 final String envState = sStateToEnvironment.get(state); 138 if (envState != null) { 139 return envState; 140 } else { 141 return Environment.MEDIA_UNKNOWN; 142 } 143 } 144 145 public static @Nullable String getBroadcastForEnvironment(String envState) { 146 return sEnvironmentToBroadcast.get(envState); 147 } 148 149 public static @Nullable String getBroadcastForState(int state) { 150 return getBroadcastForEnvironment(getEnvironmentForState(state)); 151 } 152 153 public @NonNull String getId() { 154 return id; 155 } 156 157 public @Nullable String getDiskId() { 158 return diskId; 159 } 160 161 public int getType() { 162 return type; 163 } 164 165 public int getState() { 166 return state; 167 } 168 169 public @Nullable String getFsUuid() { 170 return fsUuid; 171 } 172 173 public @Nullable String getNickname() { 174 return nickname; 175 } 176 177 public @Nullable String getDescription() { 178 if (ID_PRIVATE_INTERNAL.equals(id)) { 179 return Resources.getSystem().getString(com.android.internal.R.string.storage_internal); 180 } else if (!TextUtils.isEmpty(nickname)) { 181 return nickname; 182 } else if (!TextUtils.isEmpty(fsLabel)) { 183 return fsLabel; 184 } else { 185 return null; 186 } 187 } 188 189 public boolean isPrimary() { 190 return (flags & FLAG_PRIMARY) != 0; 191 } 192 193 public boolean isVisible() { 194 return (flags & FLAG_VISIBLE) != 0; 195 } 196 197 public boolean isInited() { 198 return (userFlags & USER_FLAG_INITED) != 0; 199 } 200 201 public boolean isSnoozed() { 202 return (userFlags & USER_FLAG_SNOOZED) != 0; 203 } 204 205 public boolean isVisibleToUser(int userId) { 206 if (type == TYPE_PUBLIC && userId == this.userId) { 207 return isVisible(); 208 } else if (type == TYPE_EMULATED) { 209 return isVisible(); 210 } else { 211 return false; 212 } 213 } 214 215 public File getPath() { 216 return new File(path); 217 } 218 219 public File getPathForUser(int userId) { 220 if (path == null) { 221 return null; 222 } else if (type == TYPE_PUBLIC && userId == this.userId) { 223 return new File(path); 224 } else if (type == TYPE_EMULATED) { 225 return new File(path, Integer.toString(userId)); 226 } else { 227 return null; 228 } 229 } 230 231 public StorageVolume buildStorageVolume(Context context, int userId) { 232 final boolean removable; 233 final boolean emulated; 234 final boolean allowMassStorage = false; 235 final int mtpStorageId = MtpStorage.getStorageIdForIndex(mtpIndex); 236 final String envState = getEnvironmentForState(state); 237 238 File userPath = getPathForUser(userId); 239 if (userPath == null) { 240 userPath = new File("/dev/null"); 241 } 242 243 String description = getDescription(); 244 if (description == null) { 245 description = context.getString(android.R.string.unknownName); 246 } 247 248 long mtpReserveSize = 0; 249 long maxFileSize = 0; 250 251 if (type == TYPE_EMULATED) { 252 emulated = true; 253 mtpReserveSize = StorageManager.from(context).getStorageLowBytes(userPath); 254 255 if (ID_EMULATED_INTERNAL.equals(id)) { 256 removable = false; 257 } else { 258 removable = true; 259 } 260 261 } else if (type == TYPE_PUBLIC) { 262 emulated = false; 263 removable = true; 264 265 if ("vfat".equals(fsType)) { 266 maxFileSize = 4294967295L; 267 } 268 269 } else { 270 throw new IllegalStateException("Unexpected volume type " + type); 271 } 272 273 return new StorageVolume(id, mtpStorageId, userPath, description, isPrimary(), removable, 274 emulated, mtpReserveSize, allowMassStorage, maxFileSize, new UserHandle(userId), 275 fsUuid, envState); 276 } 277 278 // TODO: avoid this layering violation 279 private static final String DOCUMENT_AUTHORITY = "com.android.externalstorage.documents"; 280 private static final String DOCUMENT_ROOT_PRIMARY_EMULATED = "primary"; 281 282 /** 283 * Build an intent to browse the contents of this volume. Only valid for 284 * {@link #TYPE_EMULATED} or {@link #TYPE_PUBLIC}. 285 */ 286 public Intent buildBrowseIntent() { 287 final Uri uri; 288 if (type == VolumeInfo.TYPE_PUBLIC) { 289 uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY, fsUuid); 290 } else if (VolumeInfo.ID_EMULATED_INTERNAL.equals(id)) { 291 uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY, 292 DOCUMENT_ROOT_PRIMARY_EMULATED); 293 } else if (type == VolumeInfo.TYPE_EMULATED) { 294 // TODO: build intent once supported 295 uri = null; 296 } else { 297 throw new IllegalArgumentException(); 298 } 299 300 final Intent intent = new Intent(DocumentsContract.ACTION_BROWSE_DOCUMENT_ROOT); 301 intent.addCategory(Intent.CATEGORY_DEFAULT); 302 intent.setData(uri); 303 return intent; 304 } 305 306 @Override 307 public String toString() { 308 final CharArrayWriter writer = new CharArrayWriter(); 309 dump(new IndentingPrintWriter(writer, " ", 80)); 310 return writer.toString(); 311 } 312 313 public void dump(IndentingPrintWriter pw) { 314 pw.println("VolumeInfo:"); 315 pw.increaseIndent(); 316 pw.printPair("id", id); 317 pw.printPair("type", DebugUtils.valueToString(getClass(), "TYPE_", type)); 318 pw.printPair("flags", DebugUtils.flagsToString(getClass(), "FLAG_", flags)); 319 pw.printPair("userId", userId); 320 pw.printPair("state", DebugUtils.valueToString(getClass(), "STATE_", state)); 321 pw.println(); 322 pw.printPair("fsType", fsType); 323 pw.printPair("fsUuid", fsUuid); 324 pw.printPair("fsLabel", fsLabel); 325 pw.println(); 326 pw.printPair("path", path); 327 pw.printPair("mtpIndex", mtpIndex); 328 pw.printPair("diskId", diskId); 329 pw.printPair("nickname", nickname); 330 pw.printPair("userFlags", DebugUtils.flagsToString(getClass(), "USER_FLAG_", userFlags)); 331 pw.decreaseIndent(); 332 pw.println(); 333 } 334 335 @Override 336 public VolumeInfo clone() { 337 final Parcel temp = Parcel.obtain(); 338 try { 339 writeToParcel(temp, 0); 340 temp.setDataPosition(0); 341 return CREATOR.createFromParcel(temp); 342 } finally { 343 temp.recycle(); 344 } 345 } 346 347 public static final Creator<VolumeInfo> CREATOR = new Creator<VolumeInfo>() { 348 @Override 349 public VolumeInfo createFromParcel(Parcel in) { 350 return new VolumeInfo(in); 351 } 352 353 @Override 354 public VolumeInfo[] newArray(int size) { 355 return new VolumeInfo[size]; 356 } 357 }; 358 359 @Override 360 public int describeContents() { 361 return 0; 362 } 363 364 @Override 365 public void writeToParcel(Parcel parcel, int flags) { 366 parcel.writeString(id); 367 parcel.writeInt(type); 368 parcel.writeInt(this.flags); 369 parcel.writeInt(userId); 370 parcel.writeInt(state); 371 parcel.writeString(fsType); 372 parcel.writeString(fsUuid); 373 parcel.writeString(fsLabel); 374 parcel.writeString(path); 375 parcel.writeInt(mtpIndex); 376 parcel.writeString(diskId); 377 parcel.writeString(nickname); 378 parcel.writeInt(userFlags); 379 } 380} 381