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