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