VolumeInfo.java revision b2b9ab8354da1485178cd8d8e9d89ac915b3f269
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.mtp.MtpStorage; 24import android.os.Environment; 25import android.os.Parcel; 26import android.os.Parcelable; 27import android.os.UserHandle; 28import android.text.TextUtils; 29import android.util.ArrayMap; 30import android.util.DebugUtils; 31import android.util.SparseArray; 32 33import com.android.internal.util.IndentingPrintWriter; 34import com.android.internal.util.Preconditions; 35 36import java.io.CharArrayWriter; 37import java.io.File; 38 39/** 40 * Information about a storage volume that may be mounted. A volume may be a 41 * partition on a physical {@link DiskInfo}, an emulated volume above some other 42 * storage medium, or a standalone container like an ASEC or OBB. 43 * 44 * @hide 45 */ 46public class VolumeInfo implements Parcelable { 47 /** Real volume representing internal emulated storage */ 48 public static final String ID_EMULATED_INTERNAL = "emulated"; 49 50 public static final int TYPE_PUBLIC = 0; 51 public static final int TYPE_PRIVATE = 1; 52 public static final int TYPE_EMULATED = 2; 53 public static final int TYPE_ASEC = 3; 54 public static final int TYPE_OBB = 4; 55 56 public static final int STATE_UNMOUNTED = 0; 57 public static final int STATE_MOUNTING = 1; 58 public static final int STATE_MOUNTED = 2; 59 public static final int STATE_FORMATTING = 3; 60 public static final int STATE_UNMOUNTING = 4; 61 public static final int STATE_UNMOUNTABLE = 5; 62 63 public static final int FLAG_PRIMARY = 1 << 0; 64 public static final int FLAG_VISIBLE = 1 << 1; 65 66 private static SparseArray<String> sStateToEnvironment = new SparseArray<>(); 67 private static ArrayMap<String, String> sEnvironmentToBroadcast = new ArrayMap<>(); 68 69 static { 70 sStateToEnvironment.put(VolumeInfo.STATE_UNMOUNTED, Environment.MEDIA_UNMOUNTED); 71 sStateToEnvironment.put(VolumeInfo.STATE_MOUNTING, Environment.MEDIA_CHECKING); 72 sStateToEnvironment.put(VolumeInfo.STATE_MOUNTED, Environment.MEDIA_MOUNTED); 73 sStateToEnvironment.put(VolumeInfo.STATE_FORMATTING, Environment.MEDIA_UNMOUNTED); 74 sStateToEnvironment.put(VolumeInfo.STATE_UNMOUNTING, Environment.MEDIA_EJECTING); 75 sStateToEnvironment.put(VolumeInfo.STATE_UNMOUNTABLE, Environment.MEDIA_UNMOUNTABLE); 76 77 sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTED, Intent.ACTION_MEDIA_UNMOUNTED); 78 sEnvironmentToBroadcast.put(Environment.MEDIA_CHECKING, Intent.ACTION_MEDIA_CHECKING); 79 sEnvironmentToBroadcast.put(Environment.MEDIA_MOUNTED, Intent.ACTION_MEDIA_MOUNTED); 80 sEnvironmentToBroadcast.put(Environment.MEDIA_EJECTING, Intent.ACTION_MEDIA_EJECT); 81 sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTABLE, Intent.ACTION_MEDIA_UNMOUNTABLE); 82 } 83 84 /** vold state */ 85 public final String id; 86 public final int type; 87 public int flags = 0; 88 public int userId = -1; 89 public int state = STATE_UNMOUNTED; 90 public String fsType; 91 public String fsUuid; 92 public String fsLabel; 93 public String path; 94 95 /** Framework state */ 96 public final int mtpIndex; 97 public String nickname; 98 99 public DiskInfo disk; 100 101 public VolumeInfo(String id, int type, int mtpIndex) { 102 this.id = Preconditions.checkNotNull(id); 103 this.type = type; 104 this.mtpIndex = mtpIndex; 105 } 106 107 public VolumeInfo(Parcel parcel) { 108 id = parcel.readString(); 109 type = parcel.readInt(); 110 flags = parcel.readInt(); 111 userId = parcel.readInt(); 112 state = parcel.readInt(); 113 fsType = parcel.readString(); 114 fsUuid = parcel.readString(); 115 fsLabel = parcel.readString(); 116 path = parcel.readString(); 117 mtpIndex = parcel.readInt(); 118 nickname = parcel.readString(); 119 } 120 121 public static @NonNull String getEnvironmentForState(int state) { 122 final String envState = sStateToEnvironment.get(state); 123 if (envState != null) { 124 return envState; 125 } else { 126 return Environment.MEDIA_UNKNOWN; 127 } 128 } 129 130 public static @Nullable String getBroadcastForEnvironment(String envState) { 131 return sEnvironmentToBroadcast.get(envState); 132 } 133 134 public static @Nullable String getBroadcastForState(int state) { 135 return getBroadcastForEnvironment(getEnvironmentForState(state)); 136 } 137 138 public String getDescription(Context context) { 139 if (ID_EMULATED_INTERNAL.equals(id)) { 140 return context.getString(com.android.internal.R.string.storage_internal); 141 } else if (!TextUtils.isEmpty(nickname)) { 142 return nickname; 143 } else if (!TextUtils.isEmpty(fsLabel)) { 144 return fsLabel; 145 } else { 146 return null; 147 } 148 } 149 150 public boolean isPrimary() { 151 return (flags & FLAG_PRIMARY) != 0; 152 } 153 154 public boolean isVisible() { 155 return (flags & FLAG_VISIBLE) != 0; 156 } 157 158 public boolean isVisibleToUser(int userId) { 159 if (type == TYPE_PUBLIC && userId == this.userId) { 160 return isVisible(); 161 } else if (type == TYPE_EMULATED) { 162 return isVisible(); 163 } else { 164 return false; 165 } 166 } 167 168 public File getPathForUser(int userId) { 169 if (path == null) { 170 return null; 171 } else if (type == TYPE_PUBLIC && userId == this.userId) { 172 return new File(path); 173 } else if (type == TYPE_EMULATED) { 174 return new File(path, Integer.toString(userId)); 175 } else { 176 return null; 177 } 178 } 179 180 public StorageVolume buildStorageVolume(Context context, int userId) { 181 final boolean removable; 182 final boolean emulated; 183 final boolean allowMassStorage = false; 184 final int mtpStorageId = MtpStorage.getStorageIdForIndex(mtpIndex); 185 final String envState = getEnvironmentForState(state); 186 187 File userPath = getPathForUser(userId); 188 if (userPath == null) { 189 userPath = new File("/dev/null"); 190 } 191 192 String description = getDescription(context); 193 if (description == null) { 194 description = context.getString(android.R.string.unknownName); 195 } 196 197 long mtpReserveSize = 0; 198 long maxFileSize = 0; 199 200 if (type == TYPE_EMULATED) { 201 emulated = true; 202 mtpReserveSize = StorageManager.from(context).getStorageLowBytes(userPath); 203 204 if (ID_EMULATED_INTERNAL.equals(id)) { 205 removable = false; 206 } else { 207 removable = true; 208 } 209 210 } else if (type == TYPE_PUBLIC) { 211 emulated = false; 212 removable = true; 213 214 if ("vfat".equals(fsType)) { 215 maxFileSize = 4294967295L; 216 } 217 218 } else { 219 throw new IllegalStateException("Unexpected volume type " + type); 220 } 221 222 return new StorageVolume(id, mtpStorageId, userPath, description, isPrimary(), removable, 223 emulated, mtpReserveSize, allowMassStorage, maxFileSize, new UserHandle(userId), 224 fsUuid, envState); 225 } 226 227 @Override 228 public String toString() { 229 final CharArrayWriter writer = new CharArrayWriter(); 230 dump(new IndentingPrintWriter(writer, " ", 80)); 231 return writer.toString(); 232 } 233 234 public void dump(IndentingPrintWriter pw) { 235 pw.println("VolumeInfo:"); 236 pw.increaseIndent(); 237 pw.printPair("id", id); 238 pw.printPair("type", DebugUtils.valueToString(getClass(), "TYPE_", type)); 239 pw.printPair("flags", DebugUtils.flagsToString(getClass(), "FLAG_", flags)); 240 pw.printPair("userId", userId); 241 pw.printPair("state", DebugUtils.valueToString(getClass(), "STATE_", state)); 242 pw.println(); 243 pw.printPair("fsType", fsType); 244 pw.printPair("fsUuid", fsUuid); 245 pw.printPair("fsLabel", fsLabel); 246 pw.println(); 247 pw.printPair("path", path); 248 pw.printPair("mtpIndex", mtpIndex); 249 pw.decreaseIndent(); 250 pw.println(); 251 } 252 253 @Override 254 public VolumeInfo clone() { 255 final Parcel temp = Parcel.obtain(); 256 try { 257 writeToParcel(temp, 0); 258 temp.setDataPosition(0); 259 return CREATOR.createFromParcel(temp); 260 } finally { 261 temp.recycle(); 262 } 263 } 264 265 public static final Creator<VolumeInfo> CREATOR = new Creator<VolumeInfo>() { 266 @Override 267 public VolumeInfo createFromParcel(Parcel in) { 268 return new VolumeInfo(in); 269 } 270 271 @Override 272 public VolumeInfo[] newArray(int size) { 273 return new VolumeInfo[size]; 274 } 275 }; 276 277 @Override 278 public int describeContents() { 279 return 0; 280 } 281 282 @Override 283 public void writeToParcel(Parcel parcel, int flags) { 284 parcel.writeString(id); 285 parcel.writeInt(type); 286 parcel.writeInt(flags); 287 parcel.writeInt(userId); 288 parcel.writeInt(state); 289 parcel.writeString(fsType); 290 parcel.writeString(fsUuid); 291 parcel.writeString(fsLabel); 292 parcel.writeString(path); 293 parcel.writeInt(mtpIndex); 294 parcel.writeString(nickname); 295 } 296} 297