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