1 /* 2 * Copyright (C) 2017 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.defaultapps; 18 19 import android.Manifest; 20 import android.app.Activity; 21 import android.app.Dialog; 22 import android.content.ComponentName; 23 import android.content.ContentResolver; 24 import android.content.Context; 25 import android.content.DialogInterface; 26 import android.content.Intent; 27 import android.content.pm.PackageManager; 28 import android.content.pm.ResolveInfo; 29 import android.content.pm.ServiceInfo; 30 import android.net.Uri; 31 import android.os.Bundle; 32 import android.os.Handler; 33 import android.os.Looper; 34 import android.provider.Settings; 35 import android.service.autofill.AutofillService; 36 import android.service.autofill.AutofillServiceInfo; 37 import android.support.v7.preference.Preference; 38 import android.text.Html; 39 import android.text.TextUtils; 40 import android.util.Log; 41 42 import com.android.internal.content.PackageMonitor; 43 import com.android.internal.logging.nano.MetricsProto; 44 import com.android.settings.R; 45 46 import java.util.ArrayList; 47 import java.util.List; 48 49 public class DefaultAutofillPicker extends DefaultAppPickerFragment { 50 51 private static final String TAG = "DefaultAutofillPicker"; 52 53 static final String SETTING = Settings.Secure.AUTOFILL_SERVICE; 54 static final Intent AUTOFILL_PROBE = new Intent(AutofillService.SERVICE_INTERFACE); 55 56 /** 57 * Extra set when the fragment is implementing ACTION_REQUEST_SET_AUTOFILL_SERVICE. 58 */ 59 public static final String EXTRA_PACKAGE_NAME = "package_name"; 60 61 /** 62 * Set when the fragment is implementing ACTION_REQUEST_SET_AUTOFILL_SERVICE. 63 */ 64 private DialogInterface.OnClickListener mCancelListener; 65 private final Handler mHandler = new Handler(); 66 67 @Override 68 public void onCreate(Bundle savedInstanceState) { 69 super.onCreate(savedInstanceState); 70 71 final Activity activity = getActivity(); 72 if (activity != null && activity.getIntent().getStringExtra(EXTRA_PACKAGE_NAME) != null) { 73 mCancelListener = (d, w) -> { 74 activity.setResult(Activity.RESULT_CANCELED); 75 activity.finish(); 76 }; 77 } 78 79 mSettingsPackageMonitor.register(activity, activity.getMainLooper(), false); 80 update(); 81 } 82 83 @Override 84 protected ConfirmationDialogFragment newConfirmationDialogFragment(String selectedKey, 85 CharSequence confirmationMessage) { 86 final AutofillPickerConfirmationDialogFragment fragment = 87 new AutofillPickerConfirmationDialogFragment(); 88 fragment.init(this, selectedKey, confirmationMessage); 89 return fragment; 90 } 91 92 /** 93 * Custom dialog fragment that has a cancel listener used to propagate the result back to 94 * caller (for the cases where the picker is launched by 95 * {@code android.settings.REQUEST_SET_AUTOFILL_SERVICE}. 96 */ 97 public static class AutofillPickerConfirmationDialogFragment 98 extends ConfirmationDialogFragment { 99 100 @Override 101 public void onCreate(Bundle savedInstanceState) { 102 final DefaultAutofillPicker target = (DefaultAutofillPicker) getTargetFragment(); 103 setCancelListener(target.mCancelListener); 104 super.onCreate(savedInstanceState); 105 } 106 } 107 108 @Override 109 public int getMetricsCategory() { 110 return MetricsProto.MetricsEvent.DEFAULT_AUTOFILL_PICKER; 111 } 112 113 @Override 114 protected boolean shouldShowItemNone() { 115 return true; 116 } 117 118 /** 119 * Monitor coming and going auto fill services and calls {@link #update()} when necessary 120 */ 121 private final PackageMonitor mSettingsPackageMonitor = new PackageMonitor() { 122 @Override 123 public void onPackageAdded(String packageName, int uid) { 124 mHandler.post(() -> update()); 125 } 126 127 @Override 128 public void onPackageModified(String packageName) { 129 mHandler.post(() -> update()); 130 } 131 132 @Override 133 public void onPackageRemoved(String packageName, int uid) { 134 mHandler.post(() -> update()); 135 } 136 }; 137 138 /** 139 * Update the data in this UI. 140 */ 141 private void update() { 142 updateCandidates(); 143 addAddServicePreference(); 144 } 145 146 @Override 147 public void onDestroy() { 148 mSettingsPackageMonitor.unregister(); 149 super.onDestroy(); 150 } 151 152 /** 153 * Gets the preference that allows to add a new autofill service. 154 * 155 * @return The preference or {@code null} if no service can be added 156 */ 157 private Preference newAddServicePreferenceOrNull() { 158 final String searchUri = Settings.Secure.getString(getActivity().getContentResolver(), 159 Settings.Secure.AUTOFILL_SERVICE_SEARCH_URI); 160 if (TextUtils.isEmpty(searchUri)) { 161 return null; 162 } 163 164 final Intent addNewServiceIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(searchUri)); 165 Preference preference = new Preference(getPrefContext()); 166 preference.setTitle(R.string.print_menu_item_add_service); 167 preference.setIcon(R.drawable.ic_menu_add); 168 preference.setOrder(Integer.MAX_VALUE -1); 169 preference.setIntent(addNewServiceIntent); 170 preference.setPersistent(false); 171 return preference; 172 } 173 174 /** 175 * Add a preference that allows the user to add a service if the market link for that is 176 * configured. 177 */ 178 private void addAddServicePreference() { 179 final Preference addNewServicePreference = newAddServicePreferenceOrNull(); 180 if (addNewServicePreference != null) { 181 getPreferenceScreen().addPreference(addNewServicePreference); 182 } 183 } 184 185 @Override 186 protected List<DefaultAppInfo> getCandidates() { 187 final List<DefaultAppInfo> candidates = new ArrayList<>(); 188 final List<ResolveInfo> resolveInfos = mPm.getPackageManager() 189 .queryIntentServices(AUTOFILL_PROBE, PackageManager.GET_META_DATA); 190 for (ResolveInfo info : resolveInfos) { 191 final String permission = info.serviceInfo.permission; 192 // TODO(b/37563972): remove BIND_AUTOFILL once clients use BIND_AUTOFILL_SERVICE 193 if (Manifest.permission.BIND_AUTOFILL_SERVICE.equals(permission) 194 || Manifest.permission.BIND_AUTOFILL.equals(permission)) { 195 candidates.add(new DefaultAppInfo(mPm, mUserId, new ComponentName( 196 info.serviceInfo.packageName, info.serviceInfo.name))); 197 } 198 } 199 return candidates; 200 } 201 202 public static String getDefaultKey(Context context) { 203 String setting = Settings.Secure.getString(context.getContentResolver(), SETTING); 204 if (setting != null) { 205 ComponentName componentName = ComponentName.unflattenFromString(setting); 206 if (componentName != null) { 207 return componentName.flattenToString(); 208 } 209 } 210 return null; 211 } 212 213 @Override 214 protected String getDefaultKey() { 215 return getDefaultKey(getContext()); 216 } 217 218 @Override 219 protected CharSequence getConfirmationMessage(CandidateInfo appInfo) { 220 if (appInfo == null) { 221 return null; 222 } 223 final CharSequence appName = appInfo.loadLabel(); 224 final String message = getContext().getString( 225 R.string.autofill_confirmation_message, appName); 226 return Html.fromHtml(message); 227 } 228 229 @Override 230 protected boolean setDefaultKey(String key) { 231 Settings.Secure.putString(getContext().getContentResolver(), SETTING, key); 232 233 // Check if activity was launched from Settings.ACTION_REQUEST_SET_AUTOFILL_SERVICE 234 // intent, and set proper result if so... 235 final Activity activity = getActivity(); 236 if (activity != null) { 237 final String packageName = activity.getIntent().getStringExtra(EXTRA_PACKAGE_NAME); 238 if (packageName != null) { 239 final int result = key != null && key.startsWith(packageName) ? Activity.RESULT_OK 240 : Activity.RESULT_CANCELED; 241 activity.setResult(result); 242 activity.finish(); 243 } 244 } 245 return true; 246 } 247 248 /** 249 * Provides Intent to setting activity for the specified autofill service. 250 */ 251 static final class AutofillSettingIntentProvider implements SettingIntentProvider { 252 253 private final String mSelectedKey; 254 private final PackageManager mPackageManager; 255 256 public AutofillSettingIntentProvider(PackageManager packageManager, String key) { 257 mSelectedKey = key; 258 mPackageManager = packageManager; 259 } 260 261 @Override 262 public Intent getIntent() { 263 final List<ResolveInfo> resolveInfos = mPackageManager.queryIntentServices( 264 AUTOFILL_PROBE, PackageManager.GET_META_DATA); 265 266 for (ResolveInfo resolveInfo : resolveInfos) { 267 final ServiceInfo serviceInfo = resolveInfo.serviceInfo; 268 final String flattenKey = new ComponentName( 269 serviceInfo.packageName, serviceInfo.name).flattenToString(); 270 if (TextUtils.equals(mSelectedKey, flattenKey)) { 271 final String settingsActivity; 272 try { 273 settingsActivity = new AutofillServiceInfo(mPackageManager, serviceInfo) 274 .getSettingsActivity(); 275 } catch (SecurityException e) { 276 // Service does not declare the proper permission, ignore it. 277 Log.w(TAG, "Error getting info for " + serviceInfo + ": " + e); 278 return null; 279 } 280 if (TextUtils.isEmpty(settingsActivity)) { 281 return null; 282 } 283 return new Intent(Intent.ACTION_MAIN).setComponent( 284 new ComponentName(serviceInfo.packageName, settingsActivity)); 285 } 286 } 287 288 return null; 289 } 290 } 291 } 292