blob: d3877cac17b08fe0fa4b1e3c814257d8fc3841ac [file] [log] [blame]
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -07001/*
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
Jeff Sharkey7151a9a2015-04-04 15:22:37 -070019import android.annotation.NonNull;
20import android.annotation.Nullable;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070021import android.content.Context;
22import android.content.Intent;
Jeff Sharkey59d577a2015-04-11 21:27:21 -070023import android.content.res.Resources;
Jeff Sharkey56bd3122015-04-14 10:30:34 -070024import android.net.Uri;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070025import android.os.Environment;
Jeff Sharkeyace874b2017-09-07 15:27:33 -060026import android.os.IVold;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070027import android.os.Parcel;
28import android.os.Parcelable;
29import android.os.UserHandle;
Jeff Sharkey56bd3122015-04-14 10:30:34 -070030import android.provider.DocumentsContract;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070031import android.text.TextUtils;
32import android.util.ArrayMap;
33import android.util.DebugUtils;
34import android.util.SparseArray;
Jeff Sharkey5fc24732015-06-10 14:21:27 -070035import android.util.SparseIntArray;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070036
Jeff Sharkey5fc24732015-06-10 14:21:27 -070037import com.android.internal.R;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070038import com.android.internal.util.IndentingPrintWriter;
39import com.android.internal.util.Preconditions;
40
Jeff Sharkey7151a9a2015-04-04 15:22:37 -070041import java.io.CharArrayWriter;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070042import java.io.File;
Jeff Sharkeye2d45be2015-04-15 17:14:12 -070043import java.util.Comparator;
44import java.util.Objects;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070045
46/**
47 * Information about a storage volume that may be mounted. A volume may be a
48 * partition on a physical {@link DiskInfo}, an emulated volume above some other
49 * storage medium, or a standalone container like an ASEC or OBB.
Jeff Sharkey46349872015-07-28 10:49:47 -070050 * <p>
51 * Volumes may be mounted with various flags:
52 * <ul>
53 * <li>{@link #MOUNT_FLAG_PRIMARY} means the volume provides primary external
54 * storage, historically found at {@code /sdcard}.
55 * <li>{@link #MOUNT_FLAG_VISIBLE} means the volume is visible to third-party
56 * apps for direct filesystem access. The system should send out relevant
57 * storage broadcasts and index any media on visible volumes. Visible volumes
58 * are considered a more stable part of the device, which is why we take the
59 * time to index them. In particular, transient volumes like USB OTG devices
60 * <em>should not</em> be marked as visible; their contents should be surfaced
61 * to apps through the Storage Access Framework.
62 * </ul>
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070063 *
64 * @hide
65 */
66public class VolumeInfo implements Parcelable {
Jeff Sharkeye6c04f92015-04-18 21:38:05 -070067 public static final String ACTION_VOLUME_STATE_CHANGED =
68 "android.os.storage.action.VOLUME_STATE_CHANGED";
69 public static final String EXTRA_VOLUME_ID =
70 "android.os.storage.extra.VOLUME_ID";
Jeff Sharkeyc7acac62015-06-12 16:16:56 -070071 public static final String EXTRA_VOLUME_STATE =
72 "android.os.storage.extra.VOLUME_STATE";
Jeff Sharkey56bd3122015-04-14 10:30:34 -070073
Jeff Sharkey59d577a2015-04-11 21:27:21 -070074 /** Stub volume representing internal private storage */
75 public static final String ID_PRIVATE_INTERNAL = "private";
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -070076 /** Real volume representing internal emulated storage */
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070077 public static final String ID_EMULATED_INTERNAL = "emulated";
78
Jeff Sharkey8058fe62017-09-13 11:50:33 -060079 public static final int TYPE_PUBLIC = IVold.VOLUME_TYPE_PUBLIC;
80 public static final int TYPE_PRIVATE = IVold.VOLUME_TYPE_PRIVATE;
81 public static final int TYPE_EMULATED = IVold.VOLUME_TYPE_EMULATED;
82 public static final int TYPE_ASEC = IVold.VOLUME_TYPE_ASEC;
83 public static final int TYPE_OBB = IVold.VOLUME_TYPE_OBB;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070084
Jeff Sharkey8058fe62017-09-13 11:50:33 -060085 public static final int STATE_UNMOUNTED = IVold.VOLUME_STATE_UNMOUNTED;
86 public static final int STATE_CHECKING = IVold.VOLUME_STATE_CHECKING;
87 public static final int STATE_MOUNTED = IVold.VOLUME_STATE_MOUNTED;
88 public static final int STATE_MOUNTED_READ_ONLY = IVold.VOLUME_STATE_MOUNTED_READ_ONLY;
89 public static final int STATE_FORMATTING = IVold.VOLUME_STATE_FORMATTING;
90 public static final int STATE_EJECTING = IVold.VOLUME_STATE_EJECTING;
91 public static final int STATE_UNMOUNTABLE = IVold.VOLUME_STATE_UNMOUNTABLE;
92 public static final int STATE_REMOVED = IVold.VOLUME_STATE_REMOVED;
93 public static final int STATE_BAD_REMOVAL = IVold.VOLUME_STATE_BAD_REMOVAL;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070094
Jeff Sharkeyace874b2017-09-07 15:27:33 -060095 public static final int MOUNT_FLAG_PRIMARY = IVold.MOUNT_FLAG_PRIMARY;
96 public static final int MOUNT_FLAG_VISIBLE = IVold.MOUNT_FLAG_VISIBLE;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070097
Jeff Sharkey7151a9a2015-04-04 15:22:37 -070098 private static SparseArray<String> sStateToEnvironment = new SparseArray<>();
99 private static ArrayMap<String, String> sEnvironmentToBroadcast = new ArrayMap<>();
Jeff Sharkey5fc24732015-06-10 14:21:27 -0700100 private static SparseIntArray sStateToDescrip = new SparseIntArray();
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700101
Jeff Sharkeye2d45be2015-04-15 17:14:12 -0700102 private static final Comparator<VolumeInfo>
103 sDescriptionComparator = new Comparator<VolumeInfo>() {
104 @Override
105 public int compare(VolumeInfo lhs, VolumeInfo rhs) {
106 if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(lhs.getId())) {
107 return -1;
108 } else if (lhs.getDescription() == null) {
109 return 1;
110 } else if (rhs.getDescription() == null) {
111 return -1;
112 } else {
113 return lhs.getDescription().compareTo(rhs.getDescription());
114 }
115 }
116 };
117
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700118 static {
119 sStateToEnvironment.put(VolumeInfo.STATE_UNMOUNTED, Environment.MEDIA_UNMOUNTED);
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700120 sStateToEnvironment.put(VolumeInfo.STATE_CHECKING, Environment.MEDIA_CHECKING);
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700121 sStateToEnvironment.put(VolumeInfo.STATE_MOUNTED, Environment.MEDIA_MOUNTED);
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700122 sStateToEnvironment.put(VolumeInfo.STATE_MOUNTED_READ_ONLY, Environment.MEDIA_MOUNTED_READ_ONLY);
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700123 sStateToEnvironment.put(VolumeInfo.STATE_FORMATTING, Environment.MEDIA_UNMOUNTED);
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700124 sStateToEnvironment.put(VolumeInfo.STATE_EJECTING, Environment.MEDIA_EJECTING);
Jeff Sharkey16c9c242015-04-04 21:37:20 -0700125 sStateToEnvironment.put(VolumeInfo.STATE_UNMOUNTABLE, Environment.MEDIA_UNMOUNTABLE);
Jeff Sharkey59d577a2015-04-11 21:27:21 -0700126 sStateToEnvironment.put(VolumeInfo.STATE_REMOVED, Environment.MEDIA_REMOVED);
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700127 sStateToEnvironment.put(VolumeInfo.STATE_BAD_REMOVAL, Environment.MEDIA_BAD_REMOVAL);
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700128
129 sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTED, Intent.ACTION_MEDIA_UNMOUNTED);
130 sEnvironmentToBroadcast.put(Environment.MEDIA_CHECKING, Intent.ACTION_MEDIA_CHECKING);
131 sEnvironmentToBroadcast.put(Environment.MEDIA_MOUNTED, Intent.ACTION_MEDIA_MOUNTED);
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700132 sEnvironmentToBroadcast.put(Environment.MEDIA_MOUNTED_READ_ONLY, Intent.ACTION_MEDIA_MOUNTED);
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700133 sEnvironmentToBroadcast.put(Environment.MEDIA_EJECTING, Intent.ACTION_MEDIA_EJECT);
Jeff Sharkey16c9c242015-04-04 21:37:20 -0700134 sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTABLE, Intent.ACTION_MEDIA_UNMOUNTABLE);
Jeff Sharkey59d577a2015-04-11 21:27:21 -0700135 sEnvironmentToBroadcast.put(Environment.MEDIA_REMOVED, Intent.ACTION_MEDIA_REMOVED);
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700136 sEnvironmentToBroadcast.put(Environment.MEDIA_BAD_REMOVAL, Intent.ACTION_MEDIA_BAD_REMOVAL);
Jeff Sharkey5fc24732015-06-10 14:21:27 -0700137
138 sStateToDescrip.put(VolumeInfo.STATE_UNMOUNTED, R.string.ext_media_status_unmounted);
139 sStateToDescrip.put(VolumeInfo.STATE_CHECKING, R.string.ext_media_status_checking);
140 sStateToDescrip.put(VolumeInfo.STATE_MOUNTED, R.string.ext_media_status_mounted);
141 sStateToDescrip.put(VolumeInfo.STATE_MOUNTED_READ_ONLY, R.string.ext_media_status_mounted_ro);
142 sStateToDescrip.put(VolumeInfo.STATE_FORMATTING, R.string.ext_media_status_formatting);
143 sStateToDescrip.put(VolumeInfo.STATE_EJECTING, R.string.ext_media_status_ejecting);
144 sStateToDescrip.put(VolumeInfo.STATE_UNMOUNTABLE, R.string.ext_media_status_unmountable);
145 sStateToDescrip.put(VolumeInfo.STATE_REMOVED, R.string.ext_media_status_removed);
146 sStateToDescrip.put(VolumeInfo.STATE_BAD_REMOVAL, R.string.ext_media_status_bad_removal);
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700147 }
148
149 /** vold state */
150 public final String id;
151 public final int type;
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700152 public final DiskInfo disk;
Jeff Sharkey5cc0df22015-06-17 19:44:05 -0700153 public final String partGuid;
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700154 public int mountFlags = 0;
155 public int mountUserId = -1;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700156 public int state = STATE_UNMOUNTED;
157 public String fsType;
158 public String fsUuid;
159 public String fsLabel;
160 public String path;
Jeff Sharkey50a05452015-04-29 11:24:52 -0700161 public String internalPath;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700162
Jeff Sharkey5af1835d2015-07-07 17:26:59 -0700163 public VolumeInfo(String id, int type, DiskInfo disk, String partGuid) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700164 this.id = Preconditions.checkNotNull(id);
165 this.type = type;
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700166 this.disk = disk;
Jeff Sharkey5cc0df22015-06-17 19:44:05 -0700167 this.partGuid = partGuid;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700168 }
169
170 public VolumeInfo(Parcel parcel) {
171 id = parcel.readString();
172 type = parcel.readInt();
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700173 if (parcel.readInt() != 0) {
174 disk = DiskInfo.CREATOR.createFromParcel(parcel);
175 } else {
176 disk = null;
177 }
Jeff Sharkey5cc0df22015-06-17 19:44:05 -0700178 partGuid = parcel.readString();
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700179 mountFlags = parcel.readInt();
180 mountUserId = parcel.readInt();
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700181 state = parcel.readInt();
182 fsType = parcel.readString();
183 fsUuid = parcel.readString();
184 fsLabel = parcel.readString();
185 path = parcel.readString();
Jeff Sharkey50a05452015-04-29 11:24:52 -0700186 internalPath = parcel.readString();
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700187 }
188
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700189 public static @NonNull String getEnvironmentForState(int state) {
190 final String envState = sStateToEnvironment.get(state);
191 if (envState != null) {
192 return envState;
193 } else {
194 return Environment.MEDIA_UNKNOWN;
195 }
196 }
197
198 public static @Nullable String getBroadcastForEnvironment(String envState) {
199 return sEnvironmentToBroadcast.get(envState);
200 }
201
202 public static @Nullable String getBroadcastForState(int state) {
203 return getBroadcastForEnvironment(getEnvironmentForState(state));
204 }
205
Jeff Sharkeye2d45be2015-04-15 17:14:12 -0700206 public static @NonNull Comparator<VolumeInfo> getDescriptionComparator() {
207 return sDescriptionComparator;
208 }
209
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700210 public @NonNull String getId() {
211 return id;
212 }
213
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700214 public @Nullable DiskInfo getDisk() {
215 return disk;
216 }
217
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700218 public @Nullable String getDiskId() {
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700219 return (disk != null) ? disk.id : null;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700220 }
221
222 public int getType() {
223 return type;
224 }
225
226 public int getState() {
227 return state;
228 }
229
Jeff Sharkey5fc24732015-06-10 14:21:27 -0700230 public int getStateDescription() {
231 return sStateToDescrip.get(state, 0);
232 }
233
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700234 public @Nullable String getFsUuid() {
235 return fsUuid;
236 }
237
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700238 public int getMountUserId() {
239 return mountUserId;
240 }
241
Jeff Sharkey59d577a2015-04-11 21:27:21 -0700242 public @Nullable String getDescription() {
Jeff Sharkey7a788a82015-07-07 14:33:55 -0700243 if (ID_PRIVATE_INTERNAL.equals(id) || ID_EMULATED_INTERNAL.equals(id)) {
Jeff Sharkey59d577a2015-04-11 21:27:21 -0700244 return Resources.getSystem().getString(com.android.internal.R.string.storage_internal);
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700245 } else if (!TextUtils.isEmpty(fsLabel)) {
246 return fsLabel;
247 } else {
248 return null;
249 }
250 }
251
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700252 public boolean isMountedReadable() {
253 return state == STATE_MOUNTED || state == STATE_MOUNTED_READ_ONLY;
254 }
255
256 public boolean isMountedWritable() {
257 return state == STATE_MOUNTED;
258 }
259
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700260 public boolean isPrimary() {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700261 return (mountFlags & MOUNT_FLAG_PRIMARY) != 0;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700262 }
263
Jeff Sharkey620b32b2015-04-23 19:36:02 -0700264 public boolean isPrimaryPhysical() {
265 return isPrimary() && (getType() == TYPE_PUBLIC);
266 }
267
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700268 public boolean isVisible() {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700269 return (mountFlags & MOUNT_FLAG_VISIBLE) != 0;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700270 }
271
Jeff Sharkey46349872015-07-28 10:49:47 -0700272 public boolean isVisibleForRead(int userId) {
273 if (type == TYPE_PUBLIC) {
274 if (isPrimary() && mountUserId != userId) {
275 // Primary physical is only visible to single user
276 return false;
277 } else {
278 return isVisible();
279 }
280 } else if (type == TYPE_EMULATED) {
281 return isVisible();
282 } else {
283 return false;
284 }
285 }
286
287 public boolean isVisibleForWrite(int userId) {
288 if (type == TYPE_PUBLIC && mountUserId == userId) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700289 return isVisible();
290 } else if (type == TYPE_EMULATED) {
291 return isVisible();
292 } else {
293 return false;
294 }
295 }
296
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700297 public File getPath() {
Jeff Sharkey50a05452015-04-29 11:24:52 -0700298 return (path != null) ? new File(path) : null;
299 }
300
301 public File getInternalPath() {
302 return (internalPath != null) ? new File(internalPath) : null;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700303 }
304
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700305 public File getPathForUser(int userId) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700306 if (path == null) {
307 return null;
Jeff Sharkey46349872015-07-28 10:49:47 -0700308 } else if (type == TYPE_PUBLIC) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700309 return new File(path);
310 } else if (type == TYPE_EMULATED) {
311 return new File(path, Integer.toString(userId));
312 } else {
313 return null;
314 }
315 }
316
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700317 /**
318 * Path which is accessible to apps holding
319 * {@link android.Manifest.permission#WRITE_MEDIA_STORAGE}.
320 */
321 public File getInternalPathForUser(int userId) {
322 if (type == TYPE_PUBLIC) {
323 // TODO: plumb through cleaner path from vold
324 return new File(path.replace("/storage/", "/mnt/media_rw/"));
325 } else {
326 return getPathForUser(userId);
327 }
328 }
329
Svet Ganov6ee871e2015-07-10 14:29:33 -0700330 public StorageVolume buildStorageVolume(Context context, int userId, boolean reportUnmounted) {
Jeff Sharkeya83bf192015-07-08 09:18:20 -0700331 final StorageManager storage = context.getSystemService(StorageManager.class);
332
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700333 final boolean removable;
334 final boolean emulated;
335 final boolean allowMassStorage = false;
Svet Ganov6ee871e2015-07-10 14:29:33 -0700336 final String envState = reportUnmounted
337 ? Environment.MEDIA_UNMOUNTED : getEnvironmentForState(state);
Jeff Sharkey46349872015-07-28 10:49:47 -0700338
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700339 File userPath = getPathForUser(userId);
340 if (userPath == null) {
341 userPath = new File("/dev/null");
342 }
343
Jeff Sharkeya83bf192015-07-08 09:18:20 -0700344 String description = null;
Jeff Sharkey8e2ea2a2015-08-19 14:15:33 -0700345 String derivedFsUuid = fsUuid;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700346 long maxFileSize = 0;
347
348 if (type == TYPE_EMULATED) {
349 emulated = true;
Jeff Sharkey5af1835d2015-07-07 17:26:59 -0700350
Jeff Sharkeya83bf192015-07-08 09:18:20 -0700351 final VolumeInfo privateVol = storage.findPrivateForEmulated(this);
352 if (privateVol != null) {
353 description = storage.getBestVolumeDescription(privateVol);
Jeff Sharkey8e2ea2a2015-08-19 14:15:33 -0700354 derivedFsUuid = privateVol.fsUuid;
Jeff Sharkeya83bf192015-07-08 09:18:20 -0700355 }
356
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700357 if (ID_EMULATED_INTERNAL.equals(id)) {
358 removable = false;
359 } else {
360 removable = true;
361 }
362
363 } else if (type == TYPE_PUBLIC) {
364 emulated = false;
365 removable = true;
366
Jeff Sharkeya83bf192015-07-08 09:18:20 -0700367 description = storage.getBestVolumeDescription(this);
368
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700369 if ("vfat".equals(fsType)) {
370 maxFileSize = 4294967295L;
371 }
372
373 } else {
374 throw new IllegalStateException("Unexpected volume type " + type);
375 }
376
Jeff Sharkeya83bf192015-07-08 09:18:20 -0700377 if (description == null) {
378 description = context.getString(android.R.string.unknownName);
379 }
380
Jerry Zhangf9c5c252017-08-16 18:07:51 -0700381 return new StorageVolume(id, userPath, description, isPrimary(), removable,
382 emulated, allowMassStorage, maxFileSize, new UserHandle(userId),
Jeff Sharkey8e2ea2a2015-08-19 14:15:33 -0700383 derivedFsUuid, envState);
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700384 }
385
Jeff Sharkey5af1835d2015-07-07 17:26:59 -0700386 public static int buildStableMtpStorageId(String fsUuid) {
387 if (TextUtils.isEmpty(fsUuid)) {
388 return StorageVolume.STORAGE_ID_INVALID;
389 } else {
390 int hash = 0;
391 for (int i = 0; i < fsUuid.length(); ++i) {
392 hash = 31 * hash + fsUuid.charAt(i);
393 }
394 hash = (hash ^ (hash << 16)) & 0xffff0000;
395 // Work around values that the spec doesn't allow, or that we've
396 // reserved for primary
397 if (hash == 0x00000000) hash = 0x00020000;
398 if (hash == 0x00010000) hash = 0x00020000;
399 if (hash == 0xffff0000) hash = 0xfffe0000;
400 return hash | 0x0001;
401 }
402 }
403
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700404 // TODO: avoid this layering violation
405 private static final String DOCUMENT_AUTHORITY = "com.android.externalstorage.documents";
406 private static final String DOCUMENT_ROOT_PRIMARY_EMULATED = "primary";
407
408 /**
409 * Build an intent to browse the contents of this volume. Only valid for
410 * {@link #TYPE_EMULATED} or {@link #TYPE_PUBLIC}.
411 */
412 public Intent buildBrowseIntent() {
413 final Uri uri;
414 if (type == VolumeInfo.TYPE_PUBLIC) {
415 uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY, fsUuid);
Jeff Sharkey50a05452015-04-29 11:24:52 -0700416 } else if (type == VolumeInfo.TYPE_EMULATED && isPrimary()) {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700417 uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY,
418 DOCUMENT_ROOT_PRIMARY_EMULATED);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700419 } else {
Jeff Sharkey50a05452015-04-29 11:24:52 -0700420 return null;
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700421 }
422
Garfield Tan5d3b37b2017-03-01 11:01:05 -0800423 final Intent intent = new Intent(Intent.ACTION_VIEW);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700424 intent.addCategory(Intent.CATEGORY_DEFAULT);
Jeff Sharkey42a4aaa2016-10-10 15:26:14 -0600425 intent.setDataAndType(uri, DocumentsContract.Root.MIME_TYPE_ITEM);
Aga Wronska1719b352016-03-21 11:28:03 -0700426
427 // note that docsui treats this as *force* show advanced. So sending
428 // false permits advanced to be shown based on user preferences.
429 intent.putExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, isPrimary());
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700430 return intent;
431 }
432
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700433 @Override
434 public String toString() {
435 final CharArrayWriter writer = new CharArrayWriter();
436 dump(new IndentingPrintWriter(writer, " ", 80));
437 return writer.toString();
438 }
439
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700440 public void dump(IndentingPrintWriter pw) {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700441 pw.println("VolumeInfo{" + id + "}:");
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700442 pw.increaseIndent();
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700443 pw.printPair("type", DebugUtils.valueToString(getClass(), "TYPE_", type));
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700444 pw.printPair("diskId", getDiskId());
Jeff Sharkey5cc0df22015-06-17 19:44:05 -0700445 pw.printPair("partGuid", partGuid);
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700446 pw.printPair("mountFlags", DebugUtils.flagsToString(getClass(), "MOUNT_FLAG_", mountFlags));
447 pw.printPair("mountUserId", mountUserId);
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700448 pw.printPair("state", DebugUtils.valueToString(getClass(), "STATE_", state));
449 pw.println();
450 pw.printPair("fsType", fsType);
451 pw.printPair("fsUuid", fsUuid);
452 pw.printPair("fsLabel", fsLabel);
453 pw.println();
454 pw.printPair("path", path);
Jeff Sharkey50a05452015-04-29 11:24:52 -0700455 pw.printPair("internalPath", internalPath);
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700456 pw.decreaseIndent();
457 pw.println();
458 }
459
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700460 @Override
461 public VolumeInfo clone() {
462 final Parcel temp = Parcel.obtain();
463 try {
464 writeToParcel(temp, 0);
465 temp.setDataPosition(0);
466 return CREATOR.createFromParcel(temp);
467 } finally {
468 temp.recycle();
469 }
470 }
471
Jeff Sharkeye2d45be2015-04-15 17:14:12 -0700472 @Override
473 public boolean equals(Object o) {
474 if (o instanceof VolumeInfo) {
475 return Objects.equals(id, ((VolumeInfo) o).id);
476 } else {
477 return false;
478 }
479 }
480
481 @Override
482 public int hashCode() {
483 return id.hashCode();
484 }
485
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700486 public static final Creator<VolumeInfo> CREATOR = new Creator<VolumeInfo>() {
487 @Override
488 public VolumeInfo createFromParcel(Parcel in) {
489 return new VolumeInfo(in);
490 }
491
492 @Override
493 public VolumeInfo[] newArray(int size) {
494 return new VolumeInfo[size];
495 }
496 };
497
498 @Override
499 public int describeContents() {
500 return 0;
501 }
502
503 @Override
504 public void writeToParcel(Parcel parcel, int flags) {
505 parcel.writeString(id);
506 parcel.writeInt(type);
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700507 if (disk != null) {
508 parcel.writeInt(1);
509 disk.writeToParcel(parcel, flags);
510 } else {
511 parcel.writeInt(0);
512 }
Jeff Sharkey5cc0df22015-06-17 19:44:05 -0700513 parcel.writeString(partGuid);
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700514 parcel.writeInt(mountFlags);
515 parcel.writeInt(mountUserId);
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700516 parcel.writeInt(state);
517 parcel.writeString(fsType);
518 parcel.writeString(fsUuid);
519 parcel.writeString(fsLabel);
520 parcel.writeString(path);
Jeff Sharkey50a05452015-04-29 11:24:52 -0700521 parcel.writeString(internalPath);
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700522 }
523}