1 /*
2  * Copyright (C) 2021 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 
17 package com.android.settings.deviceinfo.storage;
18 
19 import android.content.Context;
20 import android.os.Parcel;
21 import android.os.Parcelable;
22 import android.os.storage.DiskInfo;
23 import android.os.storage.StorageManager;
24 import android.os.storage.VolumeInfo;
25 import android.os.storage.VolumeRecord;
26 import android.text.TextUtils;
27 
28 import androidx.annotation.NonNull;
29 
30 import com.android.settings.R;
31 
32 import java.io.File;
33 
34 /**
35  * This object contains a {@link VolumeInfo} for a mountable storage or a {@link DiskInfo} for an
36  * unsupported disk which is not able to be mounted automatically.
37  */
38 public class StorageEntry implements Comparable<StorageEntry>, Parcelable {
39 
40     private final VolumeInfo mVolumeInfo;
41     private final DiskInfo mUnsupportedDiskInfo;
42     private final VolumeRecord mMissingVolumeRecord;
43 
44     private final String mVolumeInfoDescription;
45 
StorageEntry(@onNull Context context, @NonNull VolumeInfo volumeInfo)46     public StorageEntry(@NonNull Context context, @NonNull VolumeInfo volumeInfo) {
47         mVolumeInfo = volumeInfo;
48         mUnsupportedDiskInfo = null;
49         mMissingVolumeRecord = null;
50 
51         if (isDefaultInternalStorage()) {
52             // Shows "This device" for default internal storage.
53             mVolumeInfoDescription = context.getResources()
54                     .getString(R.string.storage_default_internal_storage);
55         } else {
56             mVolumeInfoDescription = context.getSystemService(StorageManager.class)
57                     .getBestVolumeDescription(mVolumeInfo);
58         }
59     }
60 
StorageEntry(@onNull DiskInfo diskInfo)61     public StorageEntry(@NonNull DiskInfo diskInfo) {
62         mVolumeInfo = null;
63         mUnsupportedDiskInfo = diskInfo;
64         mMissingVolumeRecord = null;
65         mVolumeInfoDescription = null;
66     }
67 
StorageEntry(@onNull VolumeRecord volumeRecord)68     public StorageEntry(@NonNull VolumeRecord volumeRecord) {
69         mVolumeInfo = null;
70         mUnsupportedDiskInfo = null;
71         mMissingVolumeRecord = volumeRecord;
72         mVolumeInfoDescription = null;
73     }
74 
StorageEntry(Parcel in)75     private StorageEntry(Parcel in) {
76         mVolumeInfo = in.readParcelable(VolumeInfo.class.getClassLoader());
77         mUnsupportedDiskInfo = in.readParcelable(DiskInfo.class.getClassLoader());
78         mMissingVolumeRecord = in.readParcelable(VolumeRecord.class.getClassLoader());
79         mVolumeInfoDescription = in.readString();
80     }
81 
82     @Override
describeContents()83     public int describeContents() {
84         return 0;
85     }
86 
87     @Override
writeToParcel(Parcel out, int flags)88     public void writeToParcel(Parcel out, int flags) {
89         out.writeParcelable(mVolumeInfo, 0 /* parcelableFlags */);
90         out.writeParcelable(mUnsupportedDiskInfo, 0 /* parcelableFlags */);
91         out.writeParcelable(mMissingVolumeRecord , 0 /* parcelableFlags */);
92         out.writeString(mVolumeInfoDescription);
93     }
94 
95     public static final Parcelable.Creator<StorageEntry> CREATOR =
96             new Parcelable.Creator<StorageEntry>() {
97                 public StorageEntry createFromParcel(Parcel in) {
98                     return new StorageEntry(in);
99                 }
100 
101                 public StorageEntry[] newArray(int size) {
102                     return new StorageEntry[size];
103                 }
104             };
105 
106     @Override
equals(Object o)107     public boolean equals(Object o) {
108         if (o == this) {
109             return true;
110         }
111         if (!(o instanceof StorageEntry)) {
112             return false;
113         }
114 
115         final StorageEntry StorageEntry = (StorageEntry) o;
116         if (isVolumeInfo()) {
117             return mVolumeInfo.equals(StorageEntry.mVolumeInfo);
118         }
119         if (isDiskInfoUnsupported()) {
120             return mUnsupportedDiskInfo.equals(StorageEntry.mUnsupportedDiskInfo);
121         }
122         return mMissingVolumeRecord.equals(StorageEntry.mMissingVolumeRecord);
123     }
124 
125     @Override
hashCode()126     public int hashCode() {
127         if (isVolumeInfo()) {
128             return mVolumeInfo.hashCode();
129         }
130         if (isDiskInfoUnsupported()) {
131             return mUnsupportedDiskInfo.hashCode();
132         }
133         return mMissingVolumeRecord.hashCode();
134     }
135 
136     @Override
toString()137     public String toString() {
138         if (isVolumeInfo()) {
139             return mVolumeInfo.toString();
140         }
141         if (isDiskInfoUnsupported()) {
142             return mUnsupportedDiskInfo.toString();
143         }
144         return mMissingVolumeRecord.toString();
145     }
146 
147     @Override
compareTo(StorageEntry other)148     public int compareTo(StorageEntry other) {
149         if (isDefaultInternalStorage() && !other.isDefaultInternalStorage()) {
150             return -1;
151         }
152         if (!isDefaultInternalStorage() && other.isDefaultInternalStorage()) {
153             return 1;
154         }
155 
156         if (isVolumeInfo() && !other.isVolumeInfo()) {
157             return -1;
158         }
159         if (!isVolumeInfo() && other.isVolumeInfo()) {
160             return 1;
161         }
162 
163         if (isPrivate() && !other.isPrivate()) {
164             return -1;
165         }
166         if (!isPrivate() && other.isPrivate()) {
167             return 1;
168         }
169 
170         if (isMounted() && !other.isMounted()) {
171             return -1;
172         }
173         if (!isMounted() && other.isMounted()) {
174             return 1;
175         }
176 
177         if (!isVolumeRecordMissed() && other.isVolumeRecordMissed()) {
178             return -1;
179         }
180         if (isVolumeRecordMissed() && !other.isVolumeRecordMissed()) {
181             return 1;
182         }
183 
184         if (getDescription() == null) {
185             return 1;
186         }
187         if (other.getDescription() == null) {
188             return -1;
189         }
190         return getDescription().compareTo(other.getDescription());
191     }
192 
193     /** Returns default internal storage. */
getDefaultInternalStorageEntry(Context context)194     public static StorageEntry getDefaultInternalStorageEntry(Context context) {
195         return new StorageEntry(context, context.getSystemService(StorageManager.class)
196                 .findVolumeById(VolumeInfo.ID_PRIVATE_INTERNAL));
197     }
198 
199     /** If it's a VolumeInfo. */
isVolumeInfo()200     public boolean isVolumeInfo() {
201         return mVolumeInfo != null;
202     }
203 
204     /** If it's an unsupported DiskInfo. */
isDiskInfoUnsupported()205     public boolean isDiskInfoUnsupported() {
206         return mUnsupportedDiskInfo != null;
207     }
208 
209     /** If it's a missing VolumeRecord. */
isVolumeRecordMissed()210     public boolean isVolumeRecordMissed() {
211         return mMissingVolumeRecord != null;
212     }
213 
214     /** If it's a default internal storage. */
isDefaultInternalStorage()215     public boolean isDefaultInternalStorage() {
216         if (isVolumeInfo()) {
217             return mVolumeInfo.getType() == VolumeInfo.TYPE_PRIVATE
218                     && TextUtils.equals(mVolumeInfo.getId(), VolumeInfo.ID_PRIVATE_INTERNAL);
219         }
220         return false;
221     }
222 
223     /** If it's a mounted storage. */
isMounted()224     public boolean isMounted() {
225         return mVolumeInfo == null ? false : (mVolumeInfo.getState() == VolumeInfo.STATE_MOUNTED
226                 || mVolumeInfo.getState() == VolumeInfo.STATE_MOUNTED_READ_ONLY);
227     }
228 
229     /** If it's an unmounted storage. */
isUnmounted()230     public boolean isUnmounted() {
231         return mVolumeInfo == null ? false : (mVolumeInfo.getState() == VolumeInfo.STATE_UNMOUNTED);
232     }
233 
234     /** If it's an unmountable storage. */
isUnmountable()235     public boolean isUnmountable() {
236         return mVolumeInfo == null ? false : mVolumeInfo.getState() == VolumeInfo.STATE_UNMOUNTABLE;
237     }
238 
239     /** If it's a private storage. */
isPrivate()240     public boolean isPrivate() {
241         return mVolumeInfo == null ? false : mVolumeInfo.getType() == VolumeInfo.TYPE_PRIVATE;
242     }
243 
244     /** If it's a public storage. */
isPublic()245     public boolean isPublic() {
246         return mVolumeInfo == null ? false : mVolumeInfo.getType() == VolumeInfo.TYPE_PUBLIC;
247     }
248 
249     /** Returns description. */
getDescription()250     public String getDescription() {
251         if (isVolumeInfo()) {
252             return mVolumeInfoDescription;
253         }
254         if (isDiskInfoUnsupported()) {
255             return mUnsupportedDiskInfo.getDescription();
256         }
257         return mMissingVolumeRecord.getNickname();
258     }
259 
260     /** Returns ID. */
getId()261     public String getId() {
262         if (isVolumeInfo()) {
263             return mVolumeInfo.getId();
264         }
265         if (isDiskInfoUnsupported()) {
266             return mUnsupportedDiskInfo.getId();
267         }
268         return mMissingVolumeRecord.getFsUuid();
269     }
270 
271     /** Returns disk ID. */
getDiskId()272     public String getDiskId() {
273         if (isVolumeInfo()) {
274             return mVolumeInfo.getDiskId();
275         }
276         if (isDiskInfoUnsupported()) {
277             return mUnsupportedDiskInfo.getId();
278         }
279         return null;
280     }
281 
282     /** Returns fsUuid. */
getFsUuid()283     public String getFsUuid() {
284         if (isVolumeInfo()) {
285             return mVolumeInfo.getFsUuid();
286         }
287         if (isDiskInfoUnsupported()) {
288             return null;
289         }
290         return mMissingVolumeRecord.getFsUuid();
291     }
292 
293     /** Returns root file if it's a VolumeInfo. */
getPath()294     public File getPath() {
295         return mVolumeInfo == null ? null : mVolumeInfo.getPath();
296     }
297 
298     /** Returns VolumeInfo of the StorageEntry. */
getVolumeInfo()299     public VolumeInfo getVolumeInfo() {
300         return mVolumeInfo;
301     }
302 }
303 
304