Home | History | Annotate | Download | only in autofill
      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