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