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