blob: 7699a0529826823f9298aa7a8bae1bf64083735a [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;
Mathew Inwood98e9ad12018-08-30 13:11:50 +010021import android.annotation.UnsupportedAppUsage;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070022import android.content.Context;
23import android.content.Intent;
Jeff Sharkey59d577a2015-04-11 21:27:21 -070024import android.content.res.Resources;
Jeff Sharkey56bd3122015-04-14 10:30:34 -070025import android.net.Uri;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070026import android.os.Environment;
Jeff Sharkeyace874b2017-09-07 15:27:33 -060027import android.os.IVold;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070028import android.os.Parcel;
29import android.os.Parcelable;
30import android.os.UserHandle;
Jeff Sharkey56bd3122015-04-14 10:30:34 -070031import android.provider.DocumentsContract;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070032import android.text.TextUtils;
33import android.util.ArrayMap;
34import android.util.DebugUtils;
35import android.util.SparseArray;
Jeff Sharkey5fc24732015-06-10 14:21:27 -070036import android.util.SparseIntArray;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070037
Jeff Sharkey5fc24732015-06-10 14:21:27 -070038import com.android.internal.R;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070039import com.android.internal.util.IndentingPrintWriter;
40import com.android.internal.util.Preconditions;
41
Jeff Sharkey7151a9a2015-04-04 15:22:37 -070042import java.io.CharArrayWriter;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070043import java.io.File;
Jeff Sharkeye2d45be2015-04-15 17:14:12 -070044import java.util.Comparator;
Jeff Sharkey3f64ec52019-01-22 13:19:40 -070045import java.util.Locale;
Jeff Sharkeye2d45be2015-04-15 17:14:12 -070046import java.util.Objects;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070047
48/**
49 * Information about a storage volume that may be mounted. A volume may be a
50 * partition on a physical {@link DiskInfo}, an emulated volume above some other
51 * storage medium, or a standalone container like an ASEC or OBB.
Jeff Sharkey46349872015-07-28 10:49:47 -070052 * <p>
53 * Volumes may be mounted with various flags:
54 * <ul>
55 * <li>{@link #MOUNT_FLAG_PRIMARY} means the volume provides primary external
56 * storage, historically found at {@code /sdcard}.
57 * <li>{@link #MOUNT_FLAG_VISIBLE} means the volume is visible to third-party
58 * apps for direct filesystem access. The system should send out relevant
59 * storage broadcasts and index any media on visible volumes. Visible volumes
60 * are considered a more stable part of the device, which is why we take the
61 * time to index them. In particular, transient volumes like USB OTG devices
62 * <em>should not</em> be marked as visible; their contents should be surfaced
63 * to apps through the Storage Access Framework.
64 * </ul>
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070065 *
66 * @hide
67 */
68public class VolumeInfo implements Parcelable {
Jeff Sharkeye6c04f92015-04-18 21:38:05 -070069 public static final String ACTION_VOLUME_STATE_CHANGED =
70 "android.os.storage.action.VOLUME_STATE_CHANGED";
71 public static final String EXTRA_VOLUME_ID =
72 "android.os.storage.extra.VOLUME_ID";
Jeff Sharkeyc7acac62015-06-12 16:16:56 -070073 public static final String EXTRA_VOLUME_STATE =
74 "android.os.storage.extra.VOLUME_STATE";
Jeff Sharkey56bd3122015-04-14 10:30:34 -070075
Jeff Sharkey59d577a2015-04-11 21:27:21 -070076 /** Stub volume representing internal private storage */
77 public static final String ID_PRIVATE_INTERNAL = "private";
Jeff Sharkeyb2b9ab82015-04-05 21:10:42 -070078 /** Real volume representing internal emulated storage */
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070079 public static final String ID_EMULATED_INTERNAL = "emulated";
80
Mathew Inwood98e9ad12018-08-30 13:11:50 +010081 @UnsupportedAppUsage
Jeff Sharkey8058fe62017-09-13 11:50:33 -060082 public static final int TYPE_PUBLIC = IVold.VOLUME_TYPE_PUBLIC;
83 public static final int TYPE_PRIVATE = IVold.VOLUME_TYPE_PRIVATE;
Mathew Inwood98e9ad12018-08-30 13:11:50 +010084 @UnsupportedAppUsage
Jeff Sharkey8058fe62017-09-13 11:50:33 -060085 public static final int TYPE_EMULATED = IVold.VOLUME_TYPE_EMULATED;
86 public static final int TYPE_ASEC = IVold.VOLUME_TYPE_ASEC;
87 public static final int TYPE_OBB = IVold.VOLUME_TYPE_OBB;
Risan05c41e62018-10-29 08:57:43 +090088 public static final int TYPE_STUB = IVold.VOLUME_TYPE_STUB;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070089
Jeff Sharkey8058fe62017-09-13 11:50:33 -060090 public static final int STATE_UNMOUNTED = IVold.VOLUME_STATE_UNMOUNTED;
91 public static final int STATE_CHECKING = IVold.VOLUME_STATE_CHECKING;
92 public static final int STATE_MOUNTED = IVold.VOLUME_STATE_MOUNTED;
93 public static final int STATE_MOUNTED_READ_ONLY = IVold.VOLUME_STATE_MOUNTED_READ_ONLY;
94 public static final int STATE_FORMATTING = IVold.VOLUME_STATE_FORMATTING;
95 public static final int STATE_EJECTING = IVold.VOLUME_STATE_EJECTING;
96 public static final int STATE_UNMOUNTABLE = IVold.VOLUME_STATE_UNMOUNTABLE;
97 public static final int STATE_REMOVED = IVold.VOLUME_STATE_REMOVED;
98 public static final int STATE_BAD_REMOVAL = IVold.VOLUME_STATE_BAD_REMOVAL;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -070099
Jeff Sharkeyace874b2017-09-07 15:27:33 -0600100 public static final int MOUNT_FLAG_PRIMARY = IVold.MOUNT_FLAG_PRIMARY;
101 public static final int MOUNT_FLAG_VISIBLE = IVold.MOUNT_FLAG_VISIBLE;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700102
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700103 private static SparseArray<String> sStateToEnvironment = new SparseArray<>();
104 private static ArrayMap<String, String> sEnvironmentToBroadcast = new ArrayMap<>();
Jeff Sharkey5fc24732015-06-10 14:21:27 -0700105 private static SparseIntArray sStateToDescrip = new SparseIntArray();
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700106
Jeff Sharkeye2d45be2015-04-15 17:14:12 -0700107 private static final Comparator<VolumeInfo>
108 sDescriptionComparator = new Comparator<VolumeInfo>() {
109 @Override
110 public int compare(VolumeInfo lhs, VolumeInfo rhs) {
111 if (VolumeInfo.ID_PRIVATE_INTERNAL.equals(lhs.getId())) {
112 return -1;
113 } else if (lhs.getDescription() == null) {
114 return 1;
115 } else if (rhs.getDescription() == null) {
116 return -1;
117 } else {
118 return lhs.getDescription().compareTo(rhs.getDescription());
119 }
120 }
121 };
122
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700123 static {
124 sStateToEnvironment.put(VolumeInfo.STATE_UNMOUNTED, Environment.MEDIA_UNMOUNTED);
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700125 sStateToEnvironment.put(VolumeInfo.STATE_CHECKING, Environment.MEDIA_CHECKING);
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700126 sStateToEnvironment.put(VolumeInfo.STATE_MOUNTED, Environment.MEDIA_MOUNTED);
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700127 sStateToEnvironment.put(VolumeInfo.STATE_MOUNTED_READ_ONLY, Environment.MEDIA_MOUNTED_READ_ONLY);
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700128 sStateToEnvironment.put(VolumeInfo.STATE_FORMATTING, Environment.MEDIA_UNMOUNTED);
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700129 sStateToEnvironment.put(VolumeInfo.STATE_EJECTING, Environment.MEDIA_EJECTING);
Jeff Sharkey16c9c242015-04-04 21:37:20 -0700130 sStateToEnvironment.put(VolumeInfo.STATE_UNMOUNTABLE, Environment.MEDIA_UNMOUNTABLE);
Jeff Sharkey59d577a2015-04-11 21:27:21 -0700131 sStateToEnvironment.put(VolumeInfo.STATE_REMOVED, Environment.MEDIA_REMOVED);
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700132 sStateToEnvironment.put(VolumeInfo.STATE_BAD_REMOVAL, Environment.MEDIA_BAD_REMOVAL);
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700133
134 sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTED, Intent.ACTION_MEDIA_UNMOUNTED);
135 sEnvironmentToBroadcast.put(Environment.MEDIA_CHECKING, Intent.ACTION_MEDIA_CHECKING);
136 sEnvironmentToBroadcast.put(Environment.MEDIA_MOUNTED, Intent.ACTION_MEDIA_MOUNTED);
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700137 sEnvironmentToBroadcast.put(Environment.MEDIA_MOUNTED_READ_ONLY, Intent.ACTION_MEDIA_MOUNTED);
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700138 sEnvironmentToBroadcast.put(Environment.MEDIA_EJECTING, Intent.ACTION_MEDIA_EJECT);
Jeff Sharkey16c9c242015-04-04 21:37:20 -0700139 sEnvironmentToBroadcast.put(Environment.MEDIA_UNMOUNTABLE, Intent.ACTION_MEDIA_UNMOUNTABLE);
Jeff Sharkey59d577a2015-04-11 21:27:21 -0700140 sEnvironmentToBroadcast.put(Environment.MEDIA_REMOVED, Intent.ACTION_MEDIA_REMOVED);
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700141 sEnvironmentToBroadcast.put(Environment.MEDIA_BAD_REMOVAL, Intent.ACTION_MEDIA_BAD_REMOVAL);
Jeff Sharkey5fc24732015-06-10 14:21:27 -0700142
143 sStateToDescrip.put(VolumeInfo.STATE_UNMOUNTED, R.string.ext_media_status_unmounted);
144 sStateToDescrip.put(VolumeInfo.STATE_CHECKING, R.string.ext_media_status_checking);
145 sStateToDescrip.put(VolumeInfo.STATE_MOUNTED, R.string.ext_media_status_mounted);
146 sStateToDescrip.put(VolumeInfo.STATE_MOUNTED_READ_ONLY, R.string.ext_media_status_mounted_ro);
147 sStateToDescrip.put(VolumeInfo.STATE_FORMATTING, R.string.ext_media_status_formatting);
148 sStateToDescrip.put(VolumeInfo.STATE_EJECTING, R.string.ext_media_status_ejecting);
149 sStateToDescrip.put(VolumeInfo.STATE_UNMOUNTABLE, R.string.ext_media_status_unmountable);
150 sStateToDescrip.put(VolumeInfo.STATE_REMOVED, R.string.ext_media_status_removed);
151 sStateToDescrip.put(VolumeInfo.STATE_BAD_REMOVAL, R.string.ext_media_status_bad_removal);
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700152 }
153
154 /** vold state */
155 public final String id;
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100156 @UnsupportedAppUsage
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700157 public final int type;
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100158 @UnsupportedAppUsage
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700159 public final DiskInfo disk;
Jeff Sharkey5cc0df22015-06-17 19:44:05 -0700160 public final String partGuid;
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700161 public int mountFlags = 0;
Sudheer Shanka3f0645b2018-09-18 13:07:59 -0700162 public int mountUserId = UserHandle.USER_NULL;
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100163 @UnsupportedAppUsage
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700164 public int state = STATE_UNMOUNTED;
165 public String fsType;
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100166 @UnsupportedAppUsage
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700167 public String fsUuid;
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100168 @UnsupportedAppUsage
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700169 public String fsLabel;
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100170 @UnsupportedAppUsage
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700171 public String path;
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100172 @UnsupportedAppUsage
Jeff Sharkey50a05452015-04-29 11:24:52 -0700173 public String internalPath;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700174
Jeff Sharkey5af1835d2015-07-07 17:26:59 -0700175 public VolumeInfo(String id, int type, DiskInfo disk, String partGuid) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700176 this.id = Preconditions.checkNotNull(id);
177 this.type = type;
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700178 this.disk = disk;
Jeff Sharkey5cc0df22015-06-17 19:44:05 -0700179 this.partGuid = partGuid;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700180 }
181
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100182 @UnsupportedAppUsage
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700183 public VolumeInfo(Parcel parcel) {
184 id = parcel.readString();
185 type = parcel.readInt();
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700186 if (parcel.readInt() != 0) {
187 disk = DiskInfo.CREATOR.createFromParcel(parcel);
188 } else {
189 disk = null;
190 }
Jeff Sharkey5cc0df22015-06-17 19:44:05 -0700191 partGuid = parcel.readString();
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700192 mountFlags = parcel.readInt();
193 mountUserId = parcel.readInt();
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700194 state = parcel.readInt();
195 fsType = parcel.readString();
196 fsUuid = parcel.readString();
197 fsLabel = parcel.readString();
198 path = parcel.readString();
Jeff Sharkey50a05452015-04-29 11:24:52 -0700199 internalPath = parcel.readString();
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700200 }
201
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100202 @UnsupportedAppUsage
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700203 public static @NonNull String getEnvironmentForState(int state) {
204 final String envState = sStateToEnvironment.get(state);
205 if (envState != null) {
206 return envState;
207 } else {
208 return Environment.MEDIA_UNKNOWN;
209 }
210 }
211
212 public static @Nullable String getBroadcastForEnvironment(String envState) {
213 return sEnvironmentToBroadcast.get(envState);
214 }
215
216 public static @Nullable String getBroadcastForState(int state) {
217 return getBroadcastForEnvironment(getEnvironmentForState(state));
218 }
219
Jeff Sharkeye2d45be2015-04-15 17:14:12 -0700220 public static @NonNull Comparator<VolumeInfo> getDescriptionComparator() {
221 return sDescriptionComparator;
222 }
223
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100224 @UnsupportedAppUsage
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700225 public @NonNull String getId() {
226 return id;
227 }
228
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100229 @UnsupportedAppUsage
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700230 public @Nullable DiskInfo getDisk() {
231 return disk;
232 }
233
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100234 @UnsupportedAppUsage
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700235 public @Nullable String getDiskId() {
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700236 return (disk != null) ? disk.id : null;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700237 }
238
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100239 @UnsupportedAppUsage
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700240 public int getType() {
241 return type;
242 }
243
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100244 @UnsupportedAppUsage
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700245 public int getState() {
246 return state;
247 }
248
Jeff Sharkey5fc24732015-06-10 14:21:27 -0700249 public int getStateDescription() {
250 return sStateToDescrip.get(state, 0);
251 }
252
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100253 @UnsupportedAppUsage
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700254 public @Nullable String getFsUuid() {
255 return fsUuid;
256 }
257
Jeff Sharkey3f64ec52019-01-22 13:19:40 -0700258 public @Nullable String getNormalizedFsUuid() {
259 return fsUuid != null ? fsUuid.toLowerCase(Locale.US) : null;
260 }
261
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100262 @UnsupportedAppUsage
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700263 public int getMountUserId() {
264 return mountUserId;
265 }
266
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100267 @UnsupportedAppUsage
Jeff Sharkey59d577a2015-04-11 21:27:21 -0700268 public @Nullable String getDescription() {
Jeff Sharkey7a788a82015-07-07 14:33:55 -0700269 if (ID_PRIVATE_INTERNAL.equals(id) || ID_EMULATED_INTERNAL.equals(id)) {
Jeff Sharkey59d577a2015-04-11 21:27:21 -0700270 return Resources.getSystem().getString(com.android.internal.R.string.storage_internal);
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700271 } else if (!TextUtils.isEmpty(fsLabel)) {
272 return fsLabel;
273 } else {
274 return null;
275 }
276 }
277
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100278 @UnsupportedAppUsage
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700279 public boolean isMountedReadable() {
280 return state == STATE_MOUNTED || state == STATE_MOUNTED_READ_ONLY;
281 }
282
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100283 @UnsupportedAppUsage
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700284 public boolean isMountedWritable() {
285 return state == STATE_MOUNTED;
286 }
287
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100288 @UnsupportedAppUsage
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700289 public boolean isPrimary() {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700290 return (mountFlags & MOUNT_FLAG_PRIMARY) != 0;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700291 }
292
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100293 @UnsupportedAppUsage
Jeff Sharkey620b32b2015-04-23 19:36:02 -0700294 public boolean isPrimaryPhysical() {
295 return isPrimary() && (getType() == TYPE_PUBLIC);
296 }
297
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100298 @UnsupportedAppUsage
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700299 public boolean isVisible() {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700300 return (mountFlags & MOUNT_FLAG_VISIBLE) != 0;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700301 }
302
Jeff Sharkeyd1257462018-03-27 11:30:53 -0600303 public boolean isVisibleForUser(int userId) {
Risan05c41e62018-10-29 08:57:43 +0900304 if ((type == TYPE_PUBLIC || type == TYPE_STUB) && mountUserId == userId) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700305 return isVisible();
306 } else if (type == TYPE_EMULATED) {
307 return isVisible();
308 } else {
309 return false;
310 }
311 }
312
Jeff Sharkeyd1257462018-03-27 11:30:53 -0600313 public boolean isVisibleForRead(int userId) {
314 return isVisibleForUser(userId);
315 }
316
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100317 @UnsupportedAppUsage
Jeff Sharkeyd1257462018-03-27 11:30:53 -0600318 public boolean isVisibleForWrite(int userId) {
319 return isVisibleForUser(userId);
320 }
321
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100322 @UnsupportedAppUsage
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700323 public File getPath() {
Jeff Sharkey50a05452015-04-29 11:24:52 -0700324 return (path != null) ? new File(path) : null;
325 }
326
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100327 @UnsupportedAppUsage
Jeff Sharkey50a05452015-04-29 11:24:52 -0700328 public File getInternalPath() {
329 return (internalPath != null) ? new File(internalPath) : null;
Jeff Sharkeyd95d3bf2015-04-14 21:39:44 -0700330 }
331
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100332 @UnsupportedAppUsage
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700333 public File getPathForUser(int userId) {
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700334 if (path == null) {
335 return null;
Risan05c41e62018-10-29 08:57:43 +0900336 } else if (type == TYPE_PUBLIC || type == TYPE_STUB) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700337 return new File(path);
338 } else if (type == TYPE_EMULATED) {
339 return new File(path, Integer.toString(userId));
340 } else {
341 return null;
342 }
343 }
344
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700345 /**
346 * Path which is accessible to apps holding
347 * {@link android.Manifest.permission#WRITE_MEDIA_STORAGE}.
348 */
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100349 @UnsupportedAppUsage
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700350 public File getInternalPathForUser(int userId) {
Jerry Zhang6f6154b2018-06-06 11:04:46 -0700351 if (path == null) {
352 return null;
Risan05c41e62018-10-29 08:57:43 +0900353 } else if (type == TYPE_PUBLIC || type == TYPE_STUB) {
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700354 // TODO: plumb through cleaner path from vold
355 return new File(path.replace("/storage/", "/mnt/media_rw/"));
356 } else {
357 return getPathForUser(userId);
358 }
359 }
360
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100361 @UnsupportedAppUsage
Svet Ganov6ee871e2015-07-10 14:29:33 -0700362 public StorageVolume buildStorageVolume(Context context, int userId, boolean reportUnmounted) {
Jeff Sharkeya83bf192015-07-08 09:18:20 -0700363 final StorageManager storage = context.getSystemService(StorageManager.class);
364
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700365 final boolean removable;
366 final boolean emulated;
367 final boolean allowMassStorage = false;
Svet Ganov6ee871e2015-07-10 14:29:33 -0700368 final String envState = reportUnmounted
369 ? Environment.MEDIA_UNMOUNTED : getEnvironmentForState(state);
Jeff Sharkey46349872015-07-28 10:49:47 -0700370
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700371 File userPath = getPathForUser(userId);
372 if (userPath == null) {
373 userPath = new File("/dev/null");
374 }
Jerry Zhang71938e12018-05-10 18:28:29 -0700375 File internalPath = getInternalPathForUser(userId);
376 if (internalPath == null) {
377 internalPath = new File("/dev/null");
378 }
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700379
Jeff Sharkeya83bf192015-07-08 09:18:20 -0700380 String description = null;
Jeff Sharkey8e2ea2a2015-08-19 14:15:33 -0700381 String derivedFsUuid = fsUuid;
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700382 long maxFileSize = 0;
383
384 if (type == TYPE_EMULATED) {
385 emulated = true;
Jeff Sharkey5af1835d2015-07-07 17:26:59 -0700386
Jeff Sharkeya83bf192015-07-08 09:18:20 -0700387 final VolumeInfo privateVol = storage.findPrivateForEmulated(this);
388 if (privateVol != null) {
389 description = storage.getBestVolumeDescription(privateVol);
Jeff Sharkey8e2ea2a2015-08-19 14:15:33 -0700390 derivedFsUuid = privateVol.fsUuid;
Jeff Sharkeya83bf192015-07-08 09:18:20 -0700391 }
392
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700393 if (ID_EMULATED_INTERNAL.equals(id)) {
394 removable = false;
395 } else {
396 removable = true;
397 }
398
Risan05c41e62018-10-29 08:57:43 +0900399 } else if (type == TYPE_PUBLIC || type == TYPE_STUB) {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700400 emulated = false;
401 removable = true;
402
Jeff Sharkeya83bf192015-07-08 09:18:20 -0700403 description = storage.getBestVolumeDescription(this);
404
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700405 if ("vfat".equals(fsType)) {
406 maxFileSize = 4294967295L;
407 }
408
409 } else {
410 throw new IllegalStateException("Unexpected volume type " + type);
411 }
412
Jeff Sharkeya83bf192015-07-08 09:18:20 -0700413 if (description == null) {
414 description = context.getString(android.R.string.unknownName);
415 }
416
Jerry Zhang71938e12018-05-10 18:28:29 -0700417 return new StorageVolume(id, userPath, internalPath, description, isPrimary(), removable,
Jerry Zhangf9c5c252017-08-16 18:07:51 -0700418 emulated, allowMassStorage, maxFileSize, new UserHandle(userId),
Jeff Sharkey8e2ea2a2015-08-19 14:15:33 -0700419 derivedFsUuid, envState);
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700420 }
421
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100422 @UnsupportedAppUsage
Jeff Sharkey5af1835d2015-07-07 17:26:59 -0700423 public static int buildStableMtpStorageId(String fsUuid) {
424 if (TextUtils.isEmpty(fsUuid)) {
425 return StorageVolume.STORAGE_ID_INVALID;
426 } else {
427 int hash = 0;
428 for (int i = 0; i < fsUuid.length(); ++i) {
429 hash = 31 * hash + fsUuid.charAt(i);
430 }
431 hash = (hash ^ (hash << 16)) & 0xffff0000;
432 // Work around values that the spec doesn't allow, or that we've
433 // reserved for primary
434 if (hash == 0x00000000) hash = 0x00020000;
435 if (hash == 0x00010000) hash = 0x00020000;
436 if (hash == 0xffff0000) hash = 0xfffe0000;
437 return hash | 0x0001;
438 }
439 }
440
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700441 // TODO: avoid this layering violation
442 private static final String DOCUMENT_AUTHORITY = "com.android.externalstorage.documents";
443 private static final String DOCUMENT_ROOT_PRIMARY_EMULATED = "primary";
444
445 /**
446 * Build an intent to browse the contents of this volume. Only valid for
447 * {@link #TYPE_EMULATED} or {@link #TYPE_PUBLIC}.
448 */
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100449 @UnsupportedAppUsage
Jeff Sharkey0000d8a2018-03-27 11:35:32 -0600450 public @Nullable Intent buildBrowseIntent() {
Jeff Sharkeyb4ee5d72018-05-22 12:01:31 -0600451 return buildBrowseIntentForUser(UserHandle.myUserId());
452 }
453
454 public @Nullable Intent buildBrowseIntentForUser(int userId) {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700455 final Uri uri;
Risan05c41e62018-10-29 08:57:43 +0900456 if ((type == VolumeInfo.TYPE_PUBLIC || type == VolumeInfo.TYPE_STUB)
457 && mountUserId == userId) {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700458 uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY, fsUuid);
Jeff Sharkey50a05452015-04-29 11:24:52 -0700459 } else if (type == VolumeInfo.TYPE_EMULATED && isPrimary()) {
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700460 uri = DocumentsContract.buildRootUri(DOCUMENT_AUTHORITY,
461 DOCUMENT_ROOT_PRIMARY_EMULATED);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700462 } else {
Jeff Sharkey50a05452015-04-29 11:24:52 -0700463 return null;
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700464 }
465
Garfield Tan5d3b37b2017-03-01 11:01:05 -0800466 final Intent intent = new Intent(Intent.ACTION_VIEW);
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700467 intent.addCategory(Intent.CATEGORY_DEFAULT);
Jeff Sharkey42a4aaa2016-10-10 15:26:14 -0600468 intent.setDataAndType(uri, DocumentsContract.Root.MIME_TYPE_ITEM);
Aga Wronska1719b352016-03-21 11:28:03 -0700469
470 // note that docsui treats this as *force* show advanced. So sending
471 // false permits advanced to be shown based on user preferences.
472 intent.putExtra(DocumentsContract.EXTRA_SHOW_ADVANCED, isPrimary());
Jeff Sharkey56bd3122015-04-14 10:30:34 -0700473 return intent;
474 }
475
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700476 @Override
477 public String toString() {
478 final CharArrayWriter writer = new CharArrayWriter();
479 dump(new IndentingPrintWriter(writer, " ", 80));
480 return writer.toString();
481 }
482
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700483 public void dump(IndentingPrintWriter pw) {
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700484 pw.println("VolumeInfo{" + id + "}:");
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700485 pw.increaseIndent();
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700486 pw.printPair("type", DebugUtils.valueToString(getClass(), "TYPE_", type));
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700487 pw.printPair("diskId", getDiskId());
Jeff Sharkey5cc0df22015-06-17 19:44:05 -0700488 pw.printPair("partGuid", partGuid);
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700489 pw.printPair("mountFlags", DebugUtils.flagsToString(getClass(), "MOUNT_FLAG_", mountFlags));
490 pw.printPair("mountUserId", mountUserId);
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700491 pw.printPair("state", DebugUtils.valueToString(getClass(), "STATE_", state));
492 pw.println();
493 pw.printPair("fsType", fsType);
494 pw.printPair("fsUuid", fsUuid);
495 pw.printPair("fsLabel", fsLabel);
496 pw.println();
497 pw.printPair("path", path);
Jeff Sharkey50a05452015-04-29 11:24:52 -0700498 pw.printPair("internalPath", internalPath);
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700499 pw.decreaseIndent();
500 pw.println();
501 }
502
Jeff Sharkey7151a9a2015-04-04 15:22:37 -0700503 @Override
504 public VolumeInfo clone() {
505 final Parcel temp = Parcel.obtain();
506 try {
507 writeToParcel(temp, 0);
508 temp.setDataPosition(0);
509 return CREATOR.createFromParcel(temp);
510 } finally {
511 temp.recycle();
512 }
513 }
514
Jeff Sharkeye2d45be2015-04-15 17:14:12 -0700515 @Override
516 public boolean equals(Object o) {
517 if (o instanceof VolumeInfo) {
518 return Objects.equals(id, ((VolumeInfo) o).id);
519 } else {
520 return false;
521 }
522 }
523
524 @Override
525 public int hashCode() {
526 return id.hashCode();
527 }
528
Mathew Inwood98e9ad12018-08-30 13:11:50 +0100529 @UnsupportedAppUsage
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700530 public static final @android.annotation.NonNull Creator<VolumeInfo> CREATOR = new Creator<VolumeInfo>() {
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700531 @Override
532 public VolumeInfo createFromParcel(Parcel in) {
533 return new VolumeInfo(in);
534 }
535
536 @Override
537 public VolumeInfo[] newArray(int size) {
538 return new VolumeInfo[size];
539 }
540 };
541
542 @Override
543 public int describeContents() {
544 return 0;
545 }
546
547 @Override
548 public void writeToParcel(Parcel parcel, int flags) {
549 parcel.writeString(id);
550 parcel.writeInt(type);
Jeff Sharkey27de30d2015-04-18 16:20:27 -0700551 if (disk != null) {
552 parcel.writeInt(1);
553 disk.writeToParcel(parcel, flags);
554 } else {
555 parcel.writeInt(0);
556 }
Jeff Sharkey5cc0df22015-06-17 19:44:05 -0700557 parcel.writeString(partGuid);
Jeff Sharkey7e92ef32015-04-17 17:35:07 -0700558 parcel.writeInt(mountFlags);
559 parcel.writeInt(mountUserId);
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700560 parcel.writeInt(state);
561 parcel.writeString(fsType);
562 parcel.writeString(fsUuid);
563 parcel.writeString(fsLabel);
564 parcel.writeString(path);
Jeff Sharkey50a05452015-04-29 11:24:52 -0700565 parcel.writeString(internalPath);
Jeff Sharkey1b8ef7e2015-04-03 17:14:45 -0700566 }
567}