1/*
2 * Copyright (C) 2011 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.content.Context;
20import android.os.Parcel;
21import android.os.Parcelable;
22import android.os.UserHandle;
23
24import com.android.internal.util.IndentingPrintWriter;
25
26import java.io.CharArrayWriter;
27import java.io.File;
28
29/**
30 * Description of a storage volume and its capabilities, including the
31 * filesystem path where it may be mounted.
32 *
33 * @hide
34 */
35public class StorageVolume implements Parcelable {
36
37    // TODO: switch to more durable token
38    private int mStorageId;
39
40    private final File mPath;
41    private final int mDescriptionId;
42    private final boolean mPrimary;
43    private final boolean mRemovable;
44    private final boolean mEmulated;
45    private final int mMtpReserveSpace;
46    private final boolean mAllowMassStorage;
47    /** Maximum file size for the storage, or zero for no limit */
48    private final long mMaxFileSize;
49    /** When set, indicates exclusive ownership of this volume */
50    private final UserHandle mOwner;
51
52    private String mUuid;
53    private String mUserLabel;
54    private String mState;
55
56    // StorageVolume extra for ACTION_MEDIA_REMOVED, ACTION_MEDIA_UNMOUNTED, ACTION_MEDIA_CHECKING,
57    // ACTION_MEDIA_NOFS, ACTION_MEDIA_MOUNTED, ACTION_MEDIA_SHARED, ACTION_MEDIA_UNSHARED,
58    // ACTION_MEDIA_BAD_REMOVAL, ACTION_MEDIA_UNMOUNTABLE and ACTION_MEDIA_EJECT broadcasts.
59    public static final String EXTRA_STORAGE_VOLUME = "storage_volume";
60
61    public StorageVolume(File path, int descriptionId, boolean primary, boolean removable,
62            boolean emulated, int mtpReserveSpace, boolean allowMassStorage, long maxFileSize,
63            UserHandle owner) {
64        mPath = path;
65        mDescriptionId = descriptionId;
66        mPrimary = primary;
67        mRemovable = removable;
68        mEmulated = emulated;
69        mMtpReserveSpace = mtpReserveSpace;
70        mAllowMassStorage = allowMassStorage;
71        mMaxFileSize = maxFileSize;
72        mOwner = owner;
73    }
74
75    private StorageVolume(Parcel in) {
76        mStorageId = in.readInt();
77        mPath = new File(in.readString());
78        mDescriptionId = in.readInt();
79        mPrimary = in.readInt() != 0;
80        mRemovable = in.readInt() != 0;
81        mEmulated = in.readInt() != 0;
82        mMtpReserveSpace = in.readInt();
83        mAllowMassStorage = in.readInt() != 0;
84        mMaxFileSize = in.readLong();
85        mOwner = in.readParcelable(null);
86        mUuid = in.readString();
87        mUserLabel = in.readString();
88        mState = in.readString();
89    }
90
91    public static StorageVolume fromTemplate(StorageVolume template, File path, UserHandle owner) {
92        return new StorageVolume(path, template.mDescriptionId, template.mPrimary,
93                template.mRemovable, template.mEmulated, template.mMtpReserveSpace,
94                template.mAllowMassStorage, template.mMaxFileSize, owner);
95    }
96
97    /**
98     * Returns the mount path for the volume.
99     *
100     * @return the mount path
101     */
102    public String getPath() {
103        return mPath.toString();
104    }
105
106    public File getPathFile() {
107        return mPath;
108    }
109
110    /**
111     * Returns a user visible description of the volume.
112     *
113     * @return the volume description
114     */
115    public String getDescription(Context context) {
116        return context.getResources().getString(mDescriptionId);
117    }
118
119    public int getDescriptionId() {
120        return mDescriptionId;
121    }
122
123    public boolean isPrimary() {
124        return mPrimary;
125    }
126
127    /**
128     * Returns true if the volume is removable.
129     *
130     * @return is removable
131     */
132    public boolean isRemovable() {
133        return mRemovable;
134    }
135
136    /**
137     * Returns true if the volume is emulated.
138     *
139     * @return is removable
140     */
141    public boolean isEmulated() {
142        return mEmulated;
143    }
144
145    /**
146     * Returns the MTP storage ID for the volume.
147     * this is also used for the storage_id column in the media provider.
148     *
149     * @return MTP storage ID
150     */
151    public int getStorageId() {
152        return mStorageId;
153    }
154
155    /**
156     * Do not call this unless you are MountService
157     */
158    public void setStorageId(int index) {
159        // storage ID is 0x00010001 for primary storage,
160        // then 0x00020001, 0x00030001, etc. for secondary storages
161        mStorageId = ((index + 1) << 16) + 1;
162    }
163
164    /**
165     * Number of megabytes of space to leave unallocated by MTP.
166     * MTP will subtract this value from the free space it reports back
167     * to the host via GetStorageInfo, and will not allow new files to
168     * be added via MTP if there is less than this amount left free in the storage.
169     * If MTP has dedicated storage this value should be zero, but if MTP is
170     * sharing storage with the rest of the system, set this to a positive value
171     * to ensure that MTP activity does not result in the storage being
172     * too close to full.
173     *
174     * @return MTP reserve space
175     */
176    public int getMtpReserveSpace() {
177        return mMtpReserveSpace;
178    }
179
180    /**
181     * Returns true if this volume can be shared via USB mass storage.
182     *
183     * @return whether mass storage is allowed
184     */
185    public boolean allowMassStorage() {
186        return mAllowMassStorage;
187    }
188
189    /**
190     * Returns maximum file size for the volume, or zero if it is unbounded.
191     *
192     * @return maximum file size
193     */
194    public long getMaxFileSize() {
195        return mMaxFileSize;
196    }
197
198    public UserHandle getOwner() {
199        return mOwner;
200    }
201
202    public void setUuid(String uuid) {
203        mUuid = uuid;
204    }
205
206    public String getUuid() {
207        return mUuid;
208    }
209
210    /**
211     * Parse and return volume UUID as FAT volume ID, or return -1 if unable to
212     * parse or UUID is unknown.
213     */
214    public int getFatVolumeId() {
215        if (mUuid == null || mUuid.length() != 9) {
216            return -1;
217        }
218        try {
219            return (int)Long.parseLong(mUuid.replace("-", ""), 16);
220        } catch (NumberFormatException e) {
221            return -1;
222        }
223    }
224
225    public void setUserLabel(String userLabel) {
226        mUserLabel = userLabel;
227    }
228
229    public String getUserLabel() {
230        return mUserLabel;
231    }
232
233    public void setState(String state) {
234        mState = state;
235    }
236
237    public String getState() {
238        return mState;
239    }
240
241    @Override
242    public boolean equals(Object obj) {
243        if (obj instanceof StorageVolume && mPath != null) {
244            StorageVolume volume = (StorageVolume)obj;
245            return (mPath.equals(volume.mPath));
246        }
247        return false;
248    }
249
250    @Override
251    public int hashCode() {
252        return mPath.hashCode();
253    }
254
255    @Override
256    public String toString() {
257        final CharArrayWriter writer = new CharArrayWriter();
258        dump(new IndentingPrintWriter(writer, "    ", 80));
259        return writer.toString();
260    }
261
262    public void dump(IndentingPrintWriter pw) {
263        pw.println("StorageVolume:");
264        pw.increaseIndent();
265        pw.printPair("mStorageId", mStorageId);
266        pw.printPair("mPath", mPath);
267        pw.printPair("mDescriptionId", mDescriptionId);
268        pw.printPair("mPrimary", mPrimary);
269        pw.printPair("mRemovable", mRemovable);
270        pw.printPair("mEmulated", mEmulated);
271        pw.printPair("mMtpReserveSpace", mMtpReserveSpace);
272        pw.printPair("mAllowMassStorage", mAllowMassStorage);
273        pw.printPair("mMaxFileSize", mMaxFileSize);
274        pw.printPair("mOwner", mOwner);
275        pw.printPair("mUuid", mUuid);
276        pw.printPair("mUserLabel", mUserLabel);
277        pw.printPair("mState", mState);
278        pw.decreaseIndent();
279    }
280
281    public static final Creator<StorageVolume> CREATOR = new Creator<StorageVolume>() {
282        @Override
283        public StorageVolume createFromParcel(Parcel in) {
284            return new StorageVolume(in);
285        }
286
287        @Override
288        public StorageVolume[] newArray(int size) {
289            return new StorageVolume[size];
290        }
291    };
292
293    @Override
294    public int describeContents() {
295        return 0;
296    }
297
298    @Override
299    public void writeToParcel(Parcel parcel, int flags) {
300        parcel.writeInt(mStorageId);
301        parcel.writeString(mPath.toString());
302        parcel.writeInt(mDescriptionId);
303        parcel.writeInt(mPrimary ? 1 : 0);
304        parcel.writeInt(mRemovable ? 1 : 0);
305        parcel.writeInt(mEmulated ? 1 : 0);
306        parcel.writeInt(mMtpReserveSpace);
307        parcel.writeInt(mAllowMassStorage ? 1 : 0);
308        parcel.writeLong(mMaxFileSize);
309        parcel.writeParcelable(mOwner, flags);
310        parcel.writeString(mUuid);
311        parcel.writeString(mUserLabel);
312        parcel.writeString(mState);
313    }
314}
315