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