1 /*
2  * Copyright (C) 2023 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 android.credentials;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.annotation.TestApi;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.pm.ServiceInfo;
25 import android.graphics.drawable.Drawable;
26 import android.os.Parcel;
27 import android.os.Parcelable;
28 import android.text.TextUtils;
29 
30 import java.util.ArrayList;
31 import java.util.Collections;
32 import java.util.HashSet;
33 import java.util.List;
34 import java.util.Set;
35 
36 /**
37  * {@link ServiceInfo} and meta-data about a credential provider.
38  *
39  * @hide
40  */
41 @TestApi
42 public final class CredentialProviderInfo implements Parcelable {
43     @NonNull private final ServiceInfo mServiceInfo;
44     @NonNull private final Set<String> mCapabilities = new HashSet<>();
45     @Nullable private final CharSequence mOverrideLabel;
46     @Nullable private CharSequence mSettingsSubtitle = null;
47     private final boolean mIsSystemProvider;
48     private final boolean mIsEnabled;
49     private final boolean mIsPrimary;
50 
51     /**
52      * Constructs an information instance of the credential provider.
53      *
54      * @param builder the builder object.
55      */
CredentialProviderInfo(@onNull Builder builder)56     private CredentialProviderInfo(@NonNull Builder builder) {
57         mServiceInfo = builder.mServiceInfo;
58         mCapabilities.addAll(builder.mCapabilities);
59         mIsSystemProvider = builder.mIsSystemProvider;
60         mSettingsSubtitle = builder.mSettingsSubtitle;
61         mIsEnabled = builder.mIsEnabled;
62         mIsPrimary = builder.mIsPrimary;
63         mOverrideLabel = builder.mOverrideLabel;
64     }
65 
66     /** Returns true if the service supports the given {@code credentialType}, false otherwise. */
67     @NonNull
hasCapability(@onNull String credentialType)68     public boolean hasCapability(@NonNull String credentialType) {
69         return mCapabilities.contains(credentialType);
70     }
71 
72     /** Returns the service info. */
73     @NonNull
getServiceInfo()74     public ServiceInfo getServiceInfo() {
75         return mServiceInfo;
76     }
77 
78     /** Returns whether it is a system provider. */
isSystemProvider()79     public boolean isSystemProvider() {
80         return mIsSystemProvider;
81     }
82 
83     /** Returns the service icon. */
84     @Nullable
getServiceIcon(@onNull Context context)85     public Drawable getServiceIcon(@NonNull Context context) {
86         return mServiceInfo.loadIcon(context.getPackageManager());
87     }
88 
89     /** Returns the service label. */
90     @Nullable
getLabel(@onNull Context context)91     public CharSequence getLabel(@NonNull Context context) {
92         if (mOverrideLabel != null) {
93             return mOverrideLabel;
94         }
95         return mServiceInfo.loadSafeLabel(context.getPackageManager());
96     }
97 
98     /** Returns a list of capabilities this provider service can support. */
99     @NonNull
getCapabilities()100     public List<String> getCapabilities() {
101         List<String> capabilities = new ArrayList<>();
102         for (String capability : mCapabilities) {
103             capabilities.add(capability);
104         }
105         return Collections.unmodifiableList(capabilities);
106     }
107 
108     /** Returns whether the provider is enabled by the user. */
isEnabled()109     public boolean isEnabled() {
110         return mIsEnabled;
111     }
112 
113     /**
114      * Returns whether the provider is set as primary by the user.
115      *
116      */
isPrimary()117     public boolean isPrimary() {
118         return mIsPrimary;
119     }
120 
121     /** Returns the settings subtitle. */
122     @Nullable
getSettingsSubtitle()123     public CharSequence getSettingsSubtitle() {
124         return mSettingsSubtitle;
125     }
126 
127     /** Returns the component name for the service. */
128     @NonNull
getComponentName()129     public ComponentName getComponentName() {
130         return mServiceInfo.getComponentName();
131     }
132 
133     @Override
writeToParcel(@onNull Parcel dest, int flags)134     public void writeToParcel(@NonNull Parcel dest, int flags) {
135         dest.writeTypedObject(mServiceInfo, flags);
136         dest.writeBoolean(mIsSystemProvider);
137         dest.writeBoolean(mIsEnabled);
138         dest.writeBoolean(mIsPrimary);
139         TextUtils.writeToParcel(mOverrideLabel, dest, flags);
140         TextUtils.writeToParcel(mSettingsSubtitle, dest, flags);
141 
142         List<String> capabilities = getCapabilities();
143         dest.writeStringList(capabilities);
144     }
145 
146     @Override
describeContents()147     public int describeContents() {
148         return 0;
149     }
150 
151     @Override
toString()152     public String toString() {
153         return "CredentialProviderInfo {"
154                 + "serviceInfo="
155                 + mServiceInfo
156                 + ", "
157                 + "isSystemProvider="
158                 + mIsSystemProvider
159                 + ", "
160                 + "isEnabled="
161                 + mIsEnabled
162                 + ", "
163                 + "isPrimary="
164                 + mIsPrimary
165                 + ", "
166                 + "overrideLabel="
167                 + mOverrideLabel
168                 + ", "
169                 + "settingsSubtitle="
170                 + mSettingsSubtitle
171                 + ", "
172                 + "capabilities="
173                 + String.join(",", mCapabilities)
174                 + "}";
175     }
176 
CredentialProviderInfo(@onNull Parcel in)177     private CredentialProviderInfo(@NonNull Parcel in) {
178         mServiceInfo = in.readTypedObject(ServiceInfo.CREATOR);
179         mIsSystemProvider = in.readBoolean();
180         mIsEnabled = in.readBoolean();
181         mIsPrimary = in.readBoolean();
182         mOverrideLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
183         mSettingsSubtitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
184 
185         List<String> capabilities = new ArrayList<>();
186         in.readStringList(capabilities);
187         mCapabilities.addAll(capabilities);
188     }
189 
190     public static final @NonNull Parcelable.Creator<CredentialProviderInfo> CREATOR =
191             new Parcelable.Creator<CredentialProviderInfo>() {
192                 @Override
193                 public CredentialProviderInfo[] newArray(int size) {
194                     return new CredentialProviderInfo[size];
195                 }
196 
197                 @Override
198                 public CredentialProviderInfo createFromParcel(@NonNull Parcel in) {
199                     return new CredentialProviderInfo(in);
200                 }
201             };
202 
203     /** A builder for {@link CredentialProviderInfo} objects. */
204     public static final class Builder {
205 
206         @NonNull private ServiceInfo mServiceInfo;
207         @NonNull private Set<String> mCapabilities = new HashSet<>();
208         private boolean mIsSystemProvider = false;
209         @Nullable private CharSequence mSettingsSubtitle = null;
210         private boolean mIsEnabled = false;
211         private boolean mIsPrimary = false;
212         @Nullable private CharSequence mOverrideLabel = null;
213 
214         /**
215          * Creates a new builder.
216          *
217          * @param serviceInfo the service info of the credential provider service.
218          */
Builder(@onNull ServiceInfo serviceInfo)219         public Builder(@NonNull ServiceInfo serviceInfo) {
220             mServiceInfo = serviceInfo;
221         }
222 
223         /** Sets whether it is a system provider. */
setSystemProvider(boolean isSystemProvider)224         public @NonNull Builder setSystemProvider(boolean isSystemProvider) {
225             mIsSystemProvider = isSystemProvider;
226             return this;
227         }
228 
229         /**
230          * Sets the label to be used instead of getting from the system (for unit tests).
231          *
232          * @hide
233          */
setOverrideLabel(@onNull CharSequence overrideLabel)234         public @NonNull Builder setOverrideLabel(@NonNull CharSequence overrideLabel) {
235             mOverrideLabel = overrideLabel;
236             return this;
237         }
238 
239         /** Sets the settings subtitle. */
setSettingsSubtitle(@ullable CharSequence settingsSubtitle)240         public @NonNull Builder setSettingsSubtitle(@Nullable CharSequence settingsSubtitle) {
241             mSettingsSubtitle = settingsSubtitle;
242             return this;
243         }
244 
245         /** Sets a list of capabilities this provider service can support. */
addCapabilities(@onNull List<String> capabilities)246         public @NonNull Builder addCapabilities(@NonNull List<String> capabilities) {
247             mCapabilities.addAll(capabilities);
248             return this;
249         }
250 
251         /**
252          * Sets a list of capabilities this provider service can support.
253          *
254          * @hide
255          */
addCapabilities(@onNull Set<String> capabilities)256         public @NonNull Builder addCapabilities(@NonNull Set<String> capabilities) {
257             mCapabilities.addAll(capabilities);
258             return this;
259         }
260 
261         /** Sets whether it is enabled by the user. */
setEnabled(boolean isEnabled)262         public @NonNull Builder setEnabled(boolean isEnabled) {
263             mIsEnabled = isEnabled;
264             return this;
265         }
266 
267         /**
268          * Sets whether it is set as primary by the user.
269          *
270          * <p>Primary provider will be used for saving credentials by default. In most cases, there
271          * should only one primary provider exist. However, if there are multiple credential
272          * providers exist in the same package, all of them will be marked as primary.
273          *
274          * @hide
275          */
setPrimary(boolean isPrimary)276         public @NonNull Builder setPrimary(boolean isPrimary) {
277             mIsPrimary = isPrimary;
278             return this;
279         }
280 
281         /** Builds a new {@link CredentialProviderInfo} instance. */
build()282         public @NonNull CredentialProviderInfo build() {
283             return new CredentialProviderInfo(this);
284         }
285     }
286 }
287