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