1 /*
2  * Copyright (C) 2019 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 android.service.contentcapture;
18 
19 import android.Manifest;
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.UserIdInt;
23 import android.app.AppGlobals;
24 import android.content.ComponentName;
25 import android.content.Context;
26 import android.content.pm.PackageManager;
27 import android.content.pm.PackageManager.NameNotFoundException;
28 import android.content.pm.ServiceInfo;
29 import android.content.res.Resources;
30 import android.content.res.TypedArray;
31 import android.content.res.XmlResourceParser;
32 import android.os.RemoteException;
33 import android.util.AttributeSet;
34 import android.util.Log;
35 import android.util.Slog;
36 import android.util.Xml;
37 
38 import com.android.internal.R;
39 
40 import org.xmlpull.v1.XmlPullParser;
41 import org.xmlpull.v1.XmlPullParserException;
42 
43 import java.io.IOException;
44 import java.io.PrintWriter;
45 
46 /**
47  * {@link ServiceInfo} and meta-data about an {@link ContentCaptureService}.
48  *
49  * @hide
50  */
51 public class ContentCaptureServiceInfo {
52 
53     private static final String TAG = ContentCaptureServiceInfo.class.getSimpleName();
54     private static final String XML_TAG_SERVICE = "content-capture-service";
55 
getServiceInfoOrThrow(ComponentName comp, boolean isTemp, @UserIdInt int userId)56     private static ServiceInfo getServiceInfoOrThrow(ComponentName comp, boolean isTemp,
57             @UserIdInt int userId) throws PackageManager.NameNotFoundException {
58         int flags = PackageManager.GET_META_DATA;
59         if (!isTemp) {
60             flags |= PackageManager.MATCH_SYSTEM_ONLY;
61         }
62 
63         ServiceInfo si = null;
64         try {
65             si = AppGlobals.getPackageManager().getServiceInfo(comp, flags, userId);
66         } catch (RemoteException e) {
67         }
68         if (si == null) {
69             throw new NameNotFoundException("Could not get serviceInfo for "
70                     + (isTemp ? " (temp)" : "(default system)")
71                     + " " + comp.flattenToShortString());
72         }
73         return si;
74     }
75 
76     @NonNull
77     private final ServiceInfo mServiceInfo;
78 
79     @Nullable
80     private final String mSettingsActivity;
81 
ContentCaptureServiceInfo(@onNull Context context, @NonNull ComponentName comp, boolean isTemporaryService, @UserIdInt int userId)82     public ContentCaptureServiceInfo(@NonNull Context context, @NonNull ComponentName comp,
83             boolean isTemporaryService, @UserIdInt int userId)
84             throws PackageManager.NameNotFoundException {
85         this(context, getServiceInfoOrThrow(comp, isTemporaryService, userId));
86     }
87 
ContentCaptureServiceInfo(@onNull Context context, @NonNull ServiceInfo si)88     private ContentCaptureServiceInfo(@NonNull Context context, @NonNull ServiceInfo si) {
89         // Check for permissions.
90         if (!Manifest.permission.BIND_CONTENT_CAPTURE_SERVICE.equals(si.permission)) {
91             Slog.w(TAG, "ContentCaptureService from '" + si.packageName
92                     + "' does not require permission "
93                     + Manifest.permission.BIND_CONTENT_CAPTURE_SERVICE);
94             throw new SecurityException("Service does not require permission "
95                     + Manifest.permission.BIND_CONTENT_CAPTURE_SERVICE);
96         }
97 
98         mServiceInfo = si;
99 
100         // Get the metadata, if declared.
101         final XmlResourceParser parser = si.loadXmlMetaData(context.getPackageManager(),
102                 ContentCaptureService.SERVICE_META_DATA);
103         if (parser == null) {
104             mSettingsActivity = null;
105             return;
106         }
107 
108         String settingsActivity = null;
109 
110         try {
111             final Resources resources = context.getPackageManager().getResourcesForApplication(
112                     si.applicationInfo);
113 
114             int type = 0;
115             while (type != XmlPullParser.END_DOCUMENT && type != XmlPullParser.START_TAG) {
116                 type = parser.next();
117             }
118 
119             if (XML_TAG_SERVICE.equals(parser.getName())) {
120                 final AttributeSet allAttributes = Xml.asAttributeSet(parser);
121                 TypedArray afsAttributes = null;
122                 try {
123                     afsAttributes = resources.obtainAttributes(allAttributes,
124                             com.android.internal.R.styleable.ContentCaptureService);
125                     settingsActivity = afsAttributes.getString(
126                             R.styleable.ContentCaptureService_settingsActivity);
127                 } finally {
128                     if (afsAttributes != null) {
129                         afsAttributes.recycle();
130                     }
131                 }
132             } else {
133                 Log.e(TAG, "Meta-data does not start with content-capture-service tag");
134             }
135         } catch (PackageManager.NameNotFoundException | IOException | XmlPullParserException e) {
136             Log.e(TAG, "Error parsing auto fill service meta-data", e);
137         }
138 
139         mSettingsActivity = settingsActivity;
140     }
141 
142     @NonNull
getServiceInfo()143     public ServiceInfo getServiceInfo() {
144         return mServiceInfo;
145     }
146 
147     @Nullable
getSettingsActivity()148     public String getSettingsActivity() {
149         return mSettingsActivity;
150     }
151 
152     @Override
toString()153     public String toString() {
154         final StringBuilder builder = new StringBuilder();
155         builder.append(getClass().getSimpleName());
156         builder.append("[").append(mServiceInfo);
157         builder.append(", settings:").append(mSettingsActivity);
158         return builder.toString();
159     }
160 
161     /**
162      * Dumps it!
163      */
dump(@onNull String prefix, @NonNull PrintWriter pw)164     public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
165         pw.print(prefix);
166         pw.print("Component: ");
167         pw.println(getServiceInfo().getComponentName());
168         pw.print(prefix);
169         pw.print("Settings: ");
170         pw.println(mSettingsActivity);
171     }
172 }
173