• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 com.android.settings.applications.credentials;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.pm.ApplicationInfo;
24 import android.content.pm.ServiceInfo;
25 import android.credentials.CredentialProviderInfo;
26 import android.graphics.drawable.Drawable;
27 import android.service.autofill.AutofillServiceInfo;
28 import android.text.TextUtils;
29 import android.util.IconDrawableFactory;
30 
31 import java.util.ArrayList;
32 import java.util.Collections;
33 import java.util.HashMap;
34 import java.util.HashSet;
35 import java.util.List;
36 import java.util.Map;
37 import java.util.Set;
38 
39 /**
40  * Holds combined autofill and credential manager data grouped by package name. Contains backing
41  * logic for each row in settings.
42  */
43 public final class CombinedProviderInfo {
44     private final List<CredentialProviderInfo> mCredentialProviderInfos;
45     private final @Nullable AutofillServiceInfo mAutofillServiceInfo;
46     private final boolean mIsDefaultAutofillProvider;
47     private final boolean mIsPrimaryCredmanProvider;
48 
49     /** Constructs an information instance from both autofill and credential provider. */
CombinedProviderInfo( @ullable List<CredentialProviderInfo> cpis, @Nullable AutofillServiceInfo asi, boolean isDefaultAutofillProvider, boolean isPrimaryCredmanProvider)50     public CombinedProviderInfo(
51             @Nullable List<CredentialProviderInfo> cpis,
52             @Nullable AutofillServiceInfo asi,
53             boolean isDefaultAutofillProvider,
54             boolean isPrimaryCredmanProvider) {
55         if (cpis == null) {
56             mCredentialProviderInfos = new ArrayList<>();
57         } else {
58             mCredentialProviderInfos = new ArrayList<>(cpis);
59         }
60         mAutofillServiceInfo = asi;
61         mIsDefaultAutofillProvider = isDefaultAutofillProvider;
62         mIsPrimaryCredmanProvider = isPrimaryCredmanProvider;
63     }
64 
65     /** Returns the credential provider info. */
66     @NonNull
getCredentialProviderInfos()67     public List<CredentialProviderInfo> getCredentialProviderInfos() {
68         return mCredentialProviderInfos;
69     }
70 
71     /** Returns the autofill provider info. */
72     @Nullable
getAutofillServiceInfo()73     public AutofillServiceInfo getAutofillServiceInfo() {
74         return mAutofillServiceInfo;
75     }
76 
77     /** Returns the application info. */
getApplicationInfo()78     public @Nullable ApplicationInfo getApplicationInfo() {
79         if (!mCredentialProviderInfos.isEmpty()) {
80             return mCredentialProviderInfos.get(0).getServiceInfo().applicationInfo;
81         }
82         return mAutofillServiceInfo.getServiceInfo().applicationInfo;
83     }
84 
85     /** Returns the app icon. */
86     @Nullable
getAppIcon(@onNull Context context, int userId)87     public Drawable getAppIcon(@NonNull Context context, int userId) {
88         final IconDrawableFactory factory = IconDrawableFactory.newInstance(context);
89         final ServiceInfo brandingService = getBrandingService();
90         final ApplicationInfo appInfo = getApplicationInfo();
91 
92         Drawable icon = null;
93         if (brandingService != null && appInfo != null) {
94             icon = factory.getBadgedIcon(brandingService, appInfo, userId);
95         }
96 
97         // If the branding service gave us a icon then use that.
98         if (icon != null) {
99             return icon;
100         }
101 
102         // Otherwise fallback to the app icon and then the package name.
103         if (appInfo != null) {
104             return factory.getBadgedIcon(appInfo, userId);
105         }
106         return null;
107     }
108 
109     /** Returns the app name. */
110     @Nullable
getAppName(@onNull Context context)111     public CharSequence getAppName(@NonNull Context context) {
112         CharSequence name = "";
113         ServiceInfo brandingService = getBrandingService();
114         if (brandingService != null) {
115             name = brandingService.loadLabel(context.getPackageManager());
116         }
117 
118         // If the branding service gave us a name then use that.
119         if (!TextUtils.isEmpty(name)) {
120             return name;
121         }
122 
123         // Otherwise fallback to the app label and then the package name.
124         final ApplicationInfo appInfo = getApplicationInfo();
125         if (appInfo != null) {
126             name = appInfo.loadLabel(context.getPackageManager());
127             if (TextUtils.isEmpty(name)) {
128                 return appInfo.packageName;
129             }
130         }
131         return "";
132     }
133 
134     /** Gets the service to use for branding (name, icons). */
getBrandingService()135     public @Nullable ServiceInfo getBrandingService() {
136         // If the app has an autofill service then use that.
137         if (mAutofillServiceInfo != null) {
138             return mAutofillServiceInfo.getServiceInfo();
139         }
140 
141         // If there are no credman providers then stop here.
142         if (mCredentialProviderInfos.isEmpty()) {
143             return null;
144         }
145 
146         // Build a list of credential providers and sort them by component names
147         // alphabetically to ensure we are deterministic when picking the provider.
148         Map<String, ServiceInfo> flattenedNamesToServices = new HashMap<>();
149         List<String> flattenedNames = new ArrayList<>();
150         for (CredentialProviderInfo cpi : mCredentialProviderInfos) {
151             final String flattenedName = cpi.getComponentName().flattenToString();
152             flattenedNamesToServices.put(flattenedName, cpi.getServiceInfo());
153             flattenedNames.add(flattenedName);
154         }
155 
156         Collections.sort(flattenedNames);
157         return flattenedNamesToServices.get(flattenedNames.get(0));
158     }
159 
160     /** Returns whether the provider is the default autofill provider. */
isDefaultAutofillProvider()161     public boolean isDefaultAutofillProvider() {
162         return mIsDefaultAutofillProvider;
163     }
164 
165     /** Returns whether the provider is the default credman provider. */
isPrimaryCredmanProvider()166     public boolean isPrimaryCredmanProvider() {
167         return mIsPrimaryCredmanProvider;
168     }
169 
170     /** Returns the settings subtitle. */
171     @Nullable
getSettingsSubtitle()172     public String getSettingsSubtitle() {
173         List<String> subtitles = new ArrayList<>();
174         for (CredentialProviderInfo cpi : mCredentialProviderInfos) {
175             // Convert from a CharSequence.
176             String subtitle = String.valueOf(cpi.getSettingsSubtitle());
177             if (subtitle != null && !TextUtils.isEmpty(subtitle) && !subtitle.equals("null")) {
178                 subtitles.add(subtitle);
179             }
180         }
181 
182         if (subtitles.size() == 0) {
183             return "";
184         }
185 
186         return String.join(", ", subtitles);
187     }
188 
189     /** Returns the autofill component name string. */
190     @Nullable
getAutofillServiceString()191     public String getAutofillServiceString() {
192         if (mAutofillServiceInfo != null) {
193             return mAutofillServiceInfo.getServiceInfo().getComponentName().flattenToString();
194         }
195         return null;
196     }
197 
198     /** Returns the provider that gets the top spot. */
getTopProvider( List<CombinedProviderInfo> providers)199     public static @Nullable CombinedProviderInfo getTopProvider(
200             List<CombinedProviderInfo> providers) {
201         // If there is an autofill provider then it should be the
202         // top app provider.
203         for (CombinedProviderInfo cpi : providers) {
204             if (cpi.isDefaultAutofillProvider()) {
205                 return cpi;
206             }
207         }
208 
209         // If there is a primary cred man provider then return that.
210         for (CombinedProviderInfo cpi : providers) {
211             if (cpi.isPrimaryCredmanProvider()) {
212                 return cpi;
213             }
214         }
215 
216         return null;
217     }
218 
buildMergedList( List<AutofillServiceInfo> asiList, List<CredentialProviderInfo> cpiList, @Nullable String defaultAutofillProvider)219     public static List<CombinedProviderInfo> buildMergedList(
220             List<AutofillServiceInfo> asiList,
221             List<CredentialProviderInfo> cpiList,
222             @Nullable String defaultAutofillProvider) {
223         ComponentName defaultAutofillProviderComponent =
224                 (defaultAutofillProvider == null)
225                         ? null
226                         : ComponentName.unflattenFromString(defaultAutofillProvider);
227 
228         // Index the autofill providers by package name.
229         Set<String> packageNames = new HashSet<>();
230         Map<String, List<AutofillServiceInfo>> autofillServices = new HashMap<>();
231         for (AutofillServiceInfo asi : asiList) {
232             final String packageName = asi.getServiceInfo().packageName;
233             if (!autofillServices.containsKey(packageName)) {
234                 autofillServices.put(packageName, new ArrayList<>());
235             }
236 
237             autofillServices.get(packageName).add(asi);
238             packageNames.add(packageName);
239         }
240 
241         // Index the credman providers by package name.
242         Map<String, List<CredentialProviderInfo>> credmanServices = new HashMap<>();
243         for (CredentialProviderInfo cpi : cpiList) {
244             String packageName = cpi.getServiceInfo().packageName;
245             if (!credmanServices.containsKey(packageName)) {
246                 credmanServices.put(packageName, new ArrayList<>());
247             }
248 
249             credmanServices.get(packageName).add(cpi);
250             packageNames.add(packageName);
251         }
252 
253         // Now go through and build the joint datasets.
254         List<CombinedProviderInfo> cmpi = new ArrayList<>();
255         for (String packageName : packageNames) {
256             List<AutofillServiceInfo> asi =
257                     autofillServices.getOrDefault(packageName, new ArrayList<>());
258             List<CredentialProviderInfo> cpi =
259                     credmanServices.getOrDefault(packageName, new ArrayList<>());
260 
261             // If there are multiple autofill services then pick the first one.
262             AutofillServiceInfo selectedAsi = null;
263             if (asi != null && !asi.isEmpty()) {
264                 selectedAsi = asi.get(0);
265             }
266 
267             // Check if we are the default autofill provider.
268             boolean isDefaultAutofillProvider = false;
269             if (defaultAutofillProviderComponent != null
270                     && defaultAutofillProviderComponent.getPackageName().equals(packageName)) {
271                 isDefaultAutofillProvider = true;
272             }
273 
274             // Check if we have any enabled cred man services.
275             boolean isPrimaryCredmanProvider = false;
276             if (cpi != null && !cpi.isEmpty()) {
277                 isPrimaryCredmanProvider = cpi.get(0).isPrimary();
278             }
279 
280             cmpi.add(
281                     new CombinedProviderInfo(
282                             cpi, selectedAsi, isDefaultAutofillProvider, isPrimaryCredmanProvider));
283         }
284 
285         return cmpi;
286     }
287 }
288