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