AutofillServiceInfo.java revision 104b932b7b84e4d4dda51b187615d79e2559ebca
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.pm.PackageManager;
24import android.content.pm.ServiceInfo;
25import android.content.res.Resources;
26import android.content.res.TypedArray;
27import android.content.res.XmlResourceParser;
28import android.os.RemoteException;
29import android.util.AttributeSet;
30import android.util.Log;
31import android.util.Xml;
32import org.xmlpull.v1.XmlPullParser;
33import org.xmlpull.v1.XmlPullParserException;
34
35import com.android.internal.R;
36
37import java.io.IOException;
38
39// TODO(b/33197203 , b/33802548): add CTS tests
40/**
41 * {@link ServiceInfo} and meta-data about an {@link AutofillService}.
42 *
43 * @hide
44 */
45public final class AutofillServiceInfo {
46    private static final String TAG = "AutofillServiceInfo";
47
48    private static ServiceInfo getServiceInfoOrThrow(ComponentName comp, int userHandle)
49            throws PackageManager.NameNotFoundException {
50        try {
51            ServiceInfo si = AppGlobals.getPackageManager().getServiceInfo(
52                    comp,
53                    PackageManager.GET_META_DATA,
54                    userHandle);
55            if (si != null) {
56                return si;
57            }
58        } catch (RemoteException e) {
59        }
60        throw new PackageManager.NameNotFoundException(comp.toString());
61    }
62
63    @NonNull
64    private final ServiceInfo mServiceInfo;
65
66    @Nullable
67    private final String mSettingsActivity;
68
69    public AutofillServiceInfo(PackageManager pm, ComponentName comp, int userHandle)
70            throws PackageManager.NameNotFoundException {
71        this(pm, getServiceInfoOrThrow(comp, userHandle));
72    }
73
74    public AutofillServiceInfo(PackageManager pm, ServiceInfo si) {
75        mServiceInfo = si;
76        final TypedArray metaDataArray = getMetaDataArray(pm, si);
77        if (metaDataArray != null) {
78            // TODO(b/35956626): inline newSettingsActivity once clients migrate
79            final String newSettingsActivity =
80                    metaDataArray.getString(R.styleable.AutofillService_settingsActivity);
81            if (newSettingsActivity != null) {
82                mSettingsActivity = newSettingsActivity;
83            } else {
84                mSettingsActivity =
85                        metaDataArray.getString(R.styleable.AutoFillService_settingsActivity);
86            }
87            metaDataArray.recycle();
88        } else {
89            mSettingsActivity = null;
90        }
91    }
92
93    /**
94     * Gets the meta-data as a TypedArray, or null if not provided, or throws if invalid.
95     */
96    @Nullable
97    private static TypedArray getMetaDataArray(PackageManager pm, ServiceInfo si) {
98        // Check for permissions.
99        // TODO(b/35956626): remove check for BIND_AUTO_FILL once clients migrate
100        if (!Manifest.permission.BIND_AUTOFILL.equals(si.permission)
101                && !Manifest.permission.BIND_AUTO_FILL.equals(si.permission)) {
102            Log.e(TAG, "Service does not require permission " + Manifest.permission.BIND_AUTOFILL);
103            return null;
104        }
105
106        // TODO(b/35956626): remove once clients migrate
107        final boolean oldStyle = !Manifest.permission.BIND_AUTOFILL.equals(si.permission);
108
109        // Get the AutoFill metadata, if declared.
110        XmlResourceParser parser = si.loadXmlMetaData(pm, AutofillService.SERVICE_META_DATA);
111        if (parser == null) {
112            return null;
113        }
114
115        // Parse service info and get the <autofill-service> tag as an AttributeSet.
116        AttributeSet attrs;
117        try {
118            // Move the XML parser to the first tag.
119            try {
120                int type;
121                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
122                        && type != XmlPullParser.START_TAG) {
123                }
124            } catch (XmlPullParserException | IOException e) {
125                Log.e(TAG, "Error parsing auto fill service meta-data", e);
126                return null;
127            }
128
129            if (!"autofill-service".equals(parser.getName())) {
130                Log.e(TAG, "Meta-data does not start with autofill-service tag");
131                return null;
132            }
133            attrs = Xml.asAttributeSet(parser);
134
135            // Get resources required to read the AttributeSet.
136            Resources res;
137            try {
138                res = pm.getResourcesForApplication(si.applicationInfo);
139            } catch (PackageManager.NameNotFoundException e) {
140                Log.e(TAG, "Error getting application resources", e);
141                return null;
142            }
143
144            return oldStyle ? res.obtainAttributes(attrs, R.styleable.AutoFillService)
145                    : res.obtainAttributes(attrs, R.styleable.AutofillService);
146        } finally {
147            parser.close();
148        }
149    }
150
151    public ServiceInfo getServiceInfo() {
152        return mServiceInfo;
153    }
154
155    @Nullable
156    public String getSettingsActivity() {
157        return mSettingsActivity;
158    }
159}
160