• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.providers.media;
18 
19 import android.annotation.SuppressLint;
20 import android.os.Parcel;
21 import android.os.Parcelable;
22 import android.os.UserHandle;
23 import android.os.storage.StorageVolume;
24 import android.provider.MediaStore;
25 import android.util.Log;
26 
27 import androidx.annotation.NonNull;
28 import androidx.annotation.Nullable;
29 
30 import com.android.modules.utils.build.SdkLevel;
31 
32 import java.io.File;
33 import java.util.Objects;
34 
35 /**
36  * MediaVolume is a MediaProvider-internal representation of a storage volume.
37  *
38  * Before MediaVolume, volumes inside MediaProvider were represented by their name;
39  * but now that MediaProvider handles volumes on behalf on multiple users, the name of a volume
40  * might no longer be unique. So MediaVolume holds both a name and a user. The user may be
41  * null on volumes without an owner (eg public volumes).
42  *
43  * In addition to that, we keep the path and ID of the volume cached in here as well
44  * for easy access.
45  */
46 public final class MediaVolume implements Parcelable {
47     /**
48      * Name of the volume.
49      */
50     private final @NonNull String mName;
51 
52     /**
53      * User to which the volume belongs to; might be null in case of public volumes.
54      */
55     private final @Nullable UserHandle mUser;
56 
57     /**
58      * Path on which the volume is mounted.
59      */
60     private final @Nullable File mPath;
61 
62     /**
63      * Unique ID of the volume; eg "external;0"
64      */
65     private final @Nullable String mId;
66 
67     /**
68      * Whether the volume is managed from outside Android.
69      */
70     private final boolean mExternallyManaged;
71 
72     /**
73      * Whether the volume is public.
74      */
75     private final boolean mPublicVolume;
76 
getName()77     public @NonNull String getName() {
78         return mName;
79     }
80 
getUser()81     public @Nullable UserHandle getUser() {
82         return mUser;
83     }
84 
getPath()85     public @Nullable File getPath() {
86         return mPath;
87     }
88 
getId()89     public @Nullable String getId() {
90         return mId;
91     }
92 
isExternallyManaged()93     public boolean isExternallyManaged() {
94         return mExternallyManaged;
95     }
96 
isPublicVolume()97     public boolean isPublicVolume() {
98         return mPublicVolume;
99     }
100 
MediaVolume(@onNull String name, UserHandle user, File path, String id, boolean externallyManaged, boolean mPublicVolume)101     private MediaVolume (@NonNull String name, UserHandle user, File path, String id,
102                          boolean externallyManaged, boolean mPublicVolume) {
103         this.mName = name;
104         this.mUser = user;
105         this.mPath = path;
106         this.mId = id;
107         this.mExternallyManaged = externallyManaged;
108         this.mPublicVolume = mPublicVolume;
109     }
110 
MediaVolume(Parcel in)111     private MediaVolume (Parcel in) {
112         this.mName = in.readString();
113         this.mUser = in.readParcelable(null);
114         this.mPath  = new File(in.readString());
115         this.mId = in.readString();
116         this.mExternallyManaged = in.readInt() != 0;
117         this.mPublicVolume = in.readInt() != 0;
118     }
119 
120     @Override
equals(Object obj)121     public boolean equals(Object obj) {
122         if (this == obj) return true;
123         if (obj == null || getClass() != obj.getClass()) return false;
124         MediaVolume that = (MediaVolume) obj;
125         // We consciously don't compare the path, because:
126         // 1. On unmount events, the returned path for StorageVolumes is
127         // 'null', and different from a mounted volume.
128         // 2. A volume with a certain ID should never be mounted in two different paths, anyway
129         return Objects.equals(mName, that.mName) &&
130                 Objects.equals(mUser, that.mUser) &&
131                 Objects.equals(mId, that.mId) &&
132                 (mExternallyManaged == that.mExternallyManaged);
133     }
134 
135     @Override
hashCode()136     public int hashCode() {
137         return Objects.hash(mName, mUser, mId, mExternallyManaged);
138     }
139 
isVisibleToUser(UserHandle user)140     public boolean isVisibleToUser(UserHandle user) {
141         return mUser == null || user.equals(mUser);
142     }
143 
144     /**
145      * Adding NewApi Suppress Lint to fix some build errors after making
146      * {@link StorageVolume#getOwner()} a public Api
147      */
148     // TODO(b/213658045) : Remove this once the related changes are submitted.
149     @SuppressLint("NewApi")
150     @NonNull
fromStorageVolume(StorageVolume storageVolume)151     public static MediaVolume fromStorageVolume(StorageVolume storageVolume) {
152         String name = storageVolume.getMediaStoreVolumeName();
153         UserHandle user = storageVolume.getOwner();
154         File path = storageVolume.getDirectory();
155         String id = storageVolume.getId();
156         boolean externallyManaged =
157                 SdkLevel.isAtLeastT() ? storageVolume.isExternallyManaged() : false;
158         boolean publicVolume = !externallyManaged && !storageVolume.isPrimary();
159         return new MediaVolume(name, user, path, id, externallyManaged, publicVolume);
160     }
161 
fromInternal()162     public static MediaVolume fromInternal() {
163         String name = MediaStore.VOLUME_INTERNAL;
164         return new MediaVolume(name, null, null, null, false, false);
165     }
166 
167     @Override
describeContents()168     public int describeContents() {
169         return 0;
170     }
171 
172     @Override
writeToParcel(Parcel dest, int flags)173     public void writeToParcel(Parcel dest, int flags) {
174         dest.writeString(mName);
175         dest.writeParcelable(mUser, flags);
176         dest.writeString(mPath.toString());
177         dest.writeString(mId);
178         dest.writeInt(mExternallyManaged ? 1 : 0);
179         dest.writeInt(mPublicVolume ? 1 : 0);
180     }
181 
182     @Override
toString()183     public String toString() {
184         return "MediaVolume name: [" + mName + "] id: [" + mId + "] user: [" + mUser + "] path: ["
185                 + mPath + "] externallyManaged: [" + mExternallyManaged + "] mPublicVolume: ["
186                 + mPublicVolume + "]";
187     }
188 
189     public static final @android.annotation.NonNull Creator<MediaVolume> CREATOR
190             = new Creator<MediaVolume>() {
191         @Override
192         public MediaVolume createFromParcel(Parcel in) {
193             return new MediaVolume(in);
194         }
195 
196         @Override
197         public MediaVolume[] newArray(int size) {
198             return new MediaVolume[size];
199         }
200     };
201 }
202