1 /*
2  * Copyright (C) 2018 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.tv.settings.autofill;
18 
19 import android.Manifest;
20 import android.content.ComponentName;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.content.pm.PackageManager;
24 import android.content.pm.ResolveInfo;
25 import android.content.pm.ServiceInfo;
26 import android.provider.Settings;
27 import android.service.autofill.AutofillService;
28 import android.service.autofill.AutofillServiceInfo;
29 import android.text.TextUtils;
30 import android.util.Log;
31 
32 import androidx.annotation.NonNull;
33 import androidx.annotation.Nullable;
34 
35 import com.android.settingslib.applications.DefaultAppInfo;
36 
37 import java.util.ArrayList;
38 import java.util.List;
39 
40 /**
41  * Helper class for autofill related settings.
42  */
43 public class AutofillHelper {
44 
45     private static final String TAG = "AutofillHelper";
46     static final Intent AUTOFILL_PROBE = new Intent(AutofillService.SERVICE_INTERFACE);
47 
48     /**
49      * Get a list of autofill services.
50      */
51     @NonNull
getAutofillCandidates(@onNull Context context, @NonNull PackageManager pm, int myUserId)52     public static List<DefaultAppInfo> getAutofillCandidates(@NonNull Context context,
53             @NonNull PackageManager pm, int myUserId) {
54         final List<DefaultAppInfo> candidates = new ArrayList<>();
55         final List<ResolveInfo> resolveInfos = pm.queryIntentServices(
56                 AUTOFILL_PROBE, PackageManager.GET_META_DATA);
57         for (ResolveInfo info : resolveInfos) {
58             final String permission = info.serviceInfo.permission;
59             if (Manifest.permission.BIND_AUTOFILL_SERVICE.equals(permission)
60                     || Manifest.permission.BIND_AUTOFILL.equals(permission)) {
61                 candidates.add(new DefaultAppInfo(context, pm, myUserId, new ComponentName(
62                         info.serviceInfo.packageName, info.serviceInfo.name)));
63             }
64         }
65         return candidates;
66     }
67 
68     /**
69      * Get flattened ComponentName of current autofill service
70      */
71     @Nullable
getCurrentAutofill(@onNull Context context)72     public static String getCurrentAutofill(@NonNull Context context) {
73         return Settings.Secure.getString(context.getContentResolver(),
74                 Settings.Secure.AUTOFILL_SERVICE);
75     }
76 
77     /**
78      * Get flattened ComponentName of current autofill service
79      */
80     @Nullable
getCurrentAutofillAsComponentName(@onNull Context context)81     public static ComponentName getCurrentAutofillAsComponentName(@NonNull Context context) {
82         String flattenedName = getCurrentAutofill(context);
83         return TextUtils.isEmpty(flattenedName)
84                 ? null : ComponentName.unflattenFromString(flattenedName);
85     }
86 
87     /**
88      * Find the current autofill service from the list.
89      */
90     @Nullable
getCurrentAutofill(@onNull Context context, @NonNull List<DefaultAppInfo> candidates)91     public static DefaultAppInfo getCurrentAutofill(@NonNull Context context,
92             @NonNull List<DefaultAppInfo> candidates) {
93         final ComponentName name = getCurrentAutofillAsComponentName(context);
94         for (int i = 0; i < candidates.size(); i++) {
95             DefaultAppInfo appInfo = candidates.get(i);
96             if ((name == null && appInfo.componentName == null)
97                     || (name != null && name.equals(appInfo.componentName))) {
98                 return appInfo;
99             }
100         }
101         return null;
102     }
103 
104     /**
105      * Get the Intent for settings activity of autofill service. Returns null if does not exist.
106      */
107     @Nullable
getAutofillSettingsIntent(@onNull Context context, @NonNull PackageManager pm, @Nullable DefaultAppInfo appInfo)108     public static Intent getAutofillSettingsIntent(@NonNull Context context,
109             @NonNull PackageManager pm, @Nullable DefaultAppInfo appInfo) {
110         if (appInfo == null || appInfo.componentName == null) {
111             return null;
112         }
113         String plattenString = appInfo.componentName.flattenToString();
114         final List<ResolveInfo> resolveInfos = pm.queryIntentServices(
115                 AUTOFILL_PROBE, PackageManager.GET_META_DATA);
116 
117         for (ResolveInfo resolveInfo : resolveInfos) {
118             final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
119             final String flattenKey = new ComponentName(
120                     serviceInfo.packageName, serviceInfo.name).flattenToString();
121             if (TextUtils.equals(plattenString, flattenKey)) {
122                 final String settingsActivity;
123                 try {
124                     settingsActivity = new AutofillServiceInfo(context, serviceInfo)
125                             .getSettingsActivity();
126                 } catch (SecurityException e) {
127                     // Service does not declare the proper permission, ignore it.
128                     Log.w(TAG, "Error getting info for " + serviceInfo + ": " + e);
129                     return null;
130                 }
131                 if (TextUtils.isEmpty(settingsActivity)) {
132                     return null;
133                 }
134                 return new Intent(Intent.ACTION_MAIN).setComponent(
135                         new ComponentName(serviceInfo.packageName, settingsActivity));
136             }
137         }
138 
139         return null;
140     }
141 
142     /**
143      * Set autofill service and write to settings.
144      */
setCurrentAutofill(@onNull Context context, @Nullable String id)145     public static void setCurrentAutofill(@NonNull Context context, @Nullable String id) {
146         if (id == null) {
147             throw new IllegalArgumentException("Null ID");
148         }
149         Settings.Secure.putString(context.getContentResolver(), Settings.Secure.AUTOFILL_SERVICE,
150                 id);
151     }
152 
153 }
154