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