1 /*
2  * Copyright (C) 2020 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.translation;
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 TranslationService}.
48  *
49  * @hide
50  */
51 public final class TranslationServiceInfo {
52 
53     private static final String TAG = "TranslationServiceInfo";
54     private static final String XML_TAG_SERVICE = "translation-service";
55 
56     @NonNull
57     private final ServiceInfo mServiceInfo;
58 
59     @Nullable
60     private final String mSettingsActivity;
61 
getServiceInfoOrThrow(ComponentName comp, boolean isTemp, @UserIdInt int userId)62     private static ServiceInfo getServiceInfoOrThrow(ComponentName comp, boolean isTemp,
63             @UserIdInt int userId) throws PackageManager.NameNotFoundException {
64         int flags = PackageManager.GET_META_DATA;
65         if (!isTemp) {
66             flags |= PackageManager.MATCH_SYSTEM_ONLY;
67         }
68 
69         ServiceInfo si = null;
70         try {
71             si = AppGlobals.getPackageManager().getServiceInfo(comp, flags, userId);
72         } catch (RemoteException e) {
73         }
74         if (si == null) {
75             throw new NameNotFoundException("Could not get serviceInfo for "
76                     + (isTemp ? " (temp)" : "(default system)")
77                     + " " + comp.flattenToShortString());
78         }
79         return si;
80     }
81 
82     @NonNull
getServiceInfo()83     public ServiceInfo getServiceInfo() {
84         return mServiceInfo;
85     }
86 
87     @Nullable
getSettingsActivity()88     public String getSettingsActivity() {
89         return mSettingsActivity;
90     }
91 
TranslationServiceInfo(@onNull Context context, @NonNull ComponentName comp, boolean isTemporaryService, @UserIdInt int userId)92     public TranslationServiceInfo(@NonNull Context context, @NonNull ComponentName comp,
93             boolean isTemporaryService, @UserIdInt int userId)
94             throws PackageManager.NameNotFoundException {
95         this(context, getServiceInfoOrThrow(comp, isTemporaryService, userId));
96     }
97 
TranslationServiceInfo(@onNull Context context, @NonNull ServiceInfo si)98     private TranslationServiceInfo(@NonNull Context context, @NonNull ServiceInfo si) {
99         // Check for permission.
100         if (!Manifest.permission.BIND_TRANSLATION_SERVICE.equals(si.permission)) {
101             Slog.w(TAG, "TranslationServiceInfo from '" + si.packageName
102                     + "' does not require permission "
103                     + Manifest.permission.BIND_TRANSLATION_SERVICE);
104             throw new SecurityException("Service does not require permission "
105                     + Manifest.permission.BIND_TRANSLATION_SERVICE);
106         }
107 
108         mServiceInfo = si;
109 
110         // Get the metadata, if declared.
111         // TODO: Try to find more easier way to do this.
112         final XmlResourceParser parser = si.loadXmlMetaData(context.getPackageManager(),
113                 TranslationService.SERVICE_META_DATA);
114         if (parser == null) {
115             mSettingsActivity = null;
116             return;
117         }
118 
119         String settingsActivity = 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 (XML_TAG_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.TranslationService);
136                     settingsActivity = afsAttributes.getString(
137                             R.styleable.ContentCaptureService_settingsActivity);
138                 } finally {
139                     if (afsAttributes != null) {
140                         afsAttributes.recycle();
141                     }
142                 }
143             } else {
144                 Log.e(TAG, "Meta-data does not start with translation-service tag");
145             }
146         } catch (PackageManager.NameNotFoundException | IOException | XmlPullParserException e) {
147             Log.e(TAG, "Error parsing auto fill service meta-data", e);
148         }
149 
150         mSettingsActivity = settingsActivity;
151     }
152 
153     @Override
toString()154     public String toString() {
155         final StringBuilder builder = new StringBuilder();
156         builder.append(getClass().getSimpleName());
157         builder.append("[").append(mServiceInfo);
158         builder.append(", settings:").append(mSettingsActivity);
159         return builder.toString();
160     }
161 
162     /**
163      * Dumps the service information.
164      */
dump(@onNull String prefix, @NonNull PrintWriter pw)165     public void dump(@NonNull String prefix, @NonNull PrintWriter pw) {
166         pw.print(prefix);
167         pw.print("Component: ");
168         pw.println(getServiceInfo().getComponentName());
169         pw.print(prefix);
170         pw.print("Settings: ");
171         pw.println(mSettingsActivity);
172     }
173 }
174