AutofillServiceInfo.java revision a6ebff0f0ba66274b333a157c3f15d7c38527fe5
1/* 2 * Copyright (C) 2016 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 */ 16package android.service.autofill; 17 18import android.Manifest; 19import android.annotation.NonNull; 20import android.annotation.Nullable; 21import android.app.AppGlobals; 22import android.content.ComponentName; 23import android.content.Context; 24import android.content.pm.PackageManager; 25import android.content.pm.ServiceInfo; 26import android.content.res.Resources; 27import android.content.res.TypedArray; 28import android.content.res.XmlResourceParser; 29import android.metrics.LogMaker; 30import android.os.RemoteException; 31import android.text.TextUtils; 32import android.util.ArrayMap; 33import android.util.AttributeSet; 34import android.util.Log; 35import android.util.Pair; 36import android.util.Xml; 37 38import com.android.internal.R; 39import com.android.internal.logging.MetricsLogger; 40import com.android.internal.logging.nano.MetricsProto.MetricsEvent; 41import com.android.internal.util.XmlUtils; 42 43import org.xmlpull.v1.XmlPullParser; 44import org.xmlpull.v1.XmlPullParserException; 45 46import java.io.IOException; 47import java.io.PrintWriter; 48 49/** 50 * {@link ServiceInfo} and meta-data about an {@link AutofillService}. 51 * 52 * @hide 53 */ 54public final class AutofillServiceInfo { 55 private static final String TAG = "AutofillServiceInfo"; 56 57 private static final String TAG_AUTOFILL_SERVICE = "autofill-service"; 58 private static final String TAG_COMPATIBILITY_PACKAGE = "compatibility-package"; 59 60 private static ServiceInfo getServiceInfoOrThrow(ComponentName comp, int userHandle) 61 throws PackageManager.NameNotFoundException { 62 try { 63 ServiceInfo si = AppGlobals.getPackageManager().getServiceInfo( 64 comp, 65 PackageManager.GET_META_DATA, 66 userHandle); 67 if (si != null) { 68 return si; 69 } 70 } catch (RemoteException e) { 71 } 72 throw new PackageManager.NameNotFoundException(comp.toString()); 73 } 74 75 @NonNull 76 private final ServiceInfo mServiceInfo; 77 78 @Nullable 79 private final String mSettingsActivity; 80 81 @Nullable 82 private final ArrayMap<String, Pair<Long, String>> mCompatibilityPackages; 83 84 public AutofillServiceInfo(Context context, ComponentName comp, int userHandle) 85 throws PackageManager.NameNotFoundException { 86 this(context, getServiceInfoOrThrow(comp, userHandle)); 87 } 88 89 public AutofillServiceInfo(Context context, ServiceInfo si) { 90 // Check for permissions. 91 if (!Manifest.permission.BIND_AUTOFILL_SERVICE.equals(si.permission)) { 92 if (Manifest.permission.BIND_AUTOFILL.equals(si.permission)) { 93 // Let it go for now... 94 Log.w(TAG, "AutofillService from '" + si.packageName + "' uses unsupported " 95 + "permission " + Manifest.permission.BIND_AUTOFILL + ". It works for " 96 + "now, but might not be supported on future releases"); 97 new MetricsLogger().write(new LogMaker(MetricsEvent.AUTOFILL_INVALID_PERMISSION) 98 .setPackageName(si.packageName)); 99 } else { 100 Log.w(TAG, "AutofillService from '" + si.packageName 101 + "' does not require permission " 102 + Manifest.permission.BIND_AUTOFILL_SERVICE); 103 throw new SecurityException("Service does not require permission " 104 + Manifest.permission.BIND_AUTOFILL_SERVICE); 105 } 106 } 107 108 mServiceInfo = si; 109 110 // Get the AutoFill metadata, if declared. 111 final XmlResourceParser parser = si.loadXmlMetaData(context.getPackageManager(), 112 AutofillService.SERVICE_META_DATA); 113 if (parser == null) { 114 mSettingsActivity = null; 115 mCompatibilityPackages = null; 116 return; 117 } 118 119 String settingsActivity = null; 120 ArrayMap<String, Pair<Long, String>> compatibilityPackages = null; 121 122 try { 123 final Resources resources = context.getPackageManager().getResourcesForApplication( 124 si.applicationInfo); 125 126 int type = 0; 127 while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) { 128 type = parser.next(); 129 } 130 131 if (TAG_AUTOFILL_SERVICE.equals(parser.getName())) { 132 final AttributeSet allAttributes = Xml.asAttributeSet(parser); 133 TypedArray afsAttributes = null; 134 try { 135 afsAttributes = resources.obtainAttributes(allAttributes, 136 com.android.internal.R.styleable.AutofillService); 137 settingsActivity = afsAttributes.getString( 138 R.styleable.AutofillService_settingsActivity); 139 } finally { 140 if (afsAttributes != null) { 141 afsAttributes.recycle(); 142 } 143 } 144 compatibilityPackages = parseCompatibilityPackages(parser, resources); 145 } else { 146 Log.e(TAG, "Meta-data does not start with autofill-service tag"); 147 } 148 } catch (PackageManager.NameNotFoundException | IOException | XmlPullParserException e) { 149 Log.e(TAG, "Error parsing auto fill service meta-data", e); 150 } 151 152 mSettingsActivity = settingsActivity; 153 mCompatibilityPackages = compatibilityPackages; 154 } 155 156 private ArrayMap<String, Pair<Long, String>> parseCompatibilityPackages(XmlPullParser parser, 157 Resources resources) 158 throws IOException, XmlPullParserException { 159 ArrayMap<String, Pair<Long, String>> compatibilityPackages = null; 160 161 final int outerDepth = parser.getDepth(); 162 int type; 163 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT 164 && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { 165 if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) { 166 continue; 167 } 168 169 if (TAG_COMPATIBILITY_PACKAGE.equals(parser.getName())) { 170 TypedArray cpAttributes = null; 171 try { 172 final AttributeSet allAttributes = Xml.asAttributeSet(parser); 173 174 cpAttributes = resources.obtainAttributes(allAttributes, 175 R.styleable.AutofillService_CompatibilityPackage); 176 177 final String name = cpAttributes.getString( 178 R.styleable.AutofillService_CompatibilityPackage_name); 179 if (TextUtils.isEmpty(name)) { 180 Log.e(TAG, "Invalid compatibility package:" + name); 181 break; 182 } 183 184 final String maxVersionCodeStr = cpAttributes.getString( 185 R.styleable.AutofillService_CompatibilityPackage_maxLongVersionCode); 186 final Long maxVersionCode; 187 if (maxVersionCodeStr != null) { 188 try { 189 maxVersionCode = Long.parseLong(maxVersionCodeStr); 190 } catch (NumberFormatException e) { 191 Log.e(TAG, "Invalid compatibility max version code:" 192 + maxVersionCodeStr); 193 break; 194 } 195 if (maxVersionCode < 0) { 196 Log.e(TAG, "Invalid compatibility max version code:" 197 + maxVersionCode); 198 break; 199 } 200 } else { 201 maxVersionCode = Long.MAX_VALUE; 202 } 203 final String urlBarResourceId = cpAttributes.getString( 204 R.styleable.AutofillService_CompatibilityPackage_urlBarResourceId); 205 206 if (compatibilityPackages == null) { 207 compatibilityPackages = new ArrayMap<>(); 208 } 209 compatibilityPackages.put(name, new Pair<>(maxVersionCode, urlBarResourceId)); 210 } finally { 211 XmlUtils.skipCurrentTag(parser); 212 if (cpAttributes != null) { 213 cpAttributes.recycle(); 214 } 215 } 216 } 217 } 218 219 return compatibilityPackages; 220 } 221 222 public ServiceInfo getServiceInfo() { 223 return mServiceInfo; 224 } 225 226 @Nullable 227 public String getSettingsActivity() { 228 return mSettingsActivity; 229 } 230 231 public ArrayMap<String, Pair<Long, String>> getCompatibilityPackages() { 232 return mCompatibilityPackages; 233 } 234 235 /** 236 * Gets the resource id of the URL bar for a package. Used in compat mode 237 */ 238 // TODO: return a list of strings instead 239 @Nullable 240 public String getUrlBarResourceId(String packageName) { 241 if (mCompatibilityPackages == null) { 242 return null; 243 } 244 final Pair<Long, String> pair = mCompatibilityPackages.get(packageName); 245 return pair == null ? null : pair.second; 246 } 247 248 @Override 249 public String toString() { 250 final StringBuilder builder = new StringBuilder(); 251 builder.append(getClass().getSimpleName()); 252 builder.append("[").append(mServiceInfo); 253 builder.append(", settings:").append(mSettingsActivity); 254 builder.append(", hasCompatPckgs:").append(mCompatibilityPackages != null 255 && !mCompatibilityPackages.isEmpty()).append("]"); 256 return builder.toString(); 257 } 258 259 /** 260 * Dumps it! 261 */ 262 public void dump(String prefix, PrintWriter pw) { 263 pw.print(prefix); pw.print("Component: "); pw.println(getServiceInfo().getComponentName()); 264 pw.print(prefix); pw.print("Settings: "); pw.println(mSettingsActivity); 265 pw.print(prefix); pw.print("Compat packages: "); pw.println(mCompatibilityPackages); 266 } 267} 268