1 /* 2 * Copyright (C) 2014 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.voice; 18 19 import android.Manifest; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.app.AppGlobals; 23 import android.content.ComponentName; 24 import android.content.pm.PackageManager; 25 import android.content.pm.ServiceInfo; 26 import android.content.res.Resources; 27 import android.content.res.TypedArray; 28 import android.content.res.XmlResourceParser; 29 import android.os.RemoteException; 30 import android.util.AttributeSet; 31 import android.util.Log; 32 import android.util.Xml; 33 34 import org.xmlpull.v1.XmlPullParser; 35 import org.xmlpull.v1.XmlPullParserException; 36 37 import java.io.IOException; 38 39 /** @hide */ 40 public class VoiceInteractionServiceInfo { 41 static final String TAG = "VoiceInteractionServiceInfo"; 42 43 private String mParseError; 44 45 private ServiceInfo mServiceInfo; 46 private String mSessionService; 47 private String mRecognitionService; 48 private String mHotwordDetectionService; 49 private String mVisualQueryDetectionService; 50 private String mSettingsActivity; 51 private boolean mSupportsAssist; 52 private boolean mSupportsLaunchFromKeyguard; 53 private boolean mSupportsLocalInteraction; 54 55 /** 56 * Loads the service metadata published by the component. Success is indicated by 57 * {@link #getParseError()}. 58 * 59 * @param pm A PackageManager from which the XML can be loaded. 60 * @param comp The {@link VoiceInteractionService} component. 61 */ VoiceInteractionServiceInfo( @onNull PackageManager pm, @NonNull ComponentName comp, int userHandle)62 public VoiceInteractionServiceInfo( 63 @NonNull PackageManager pm, @NonNull ComponentName comp, int userHandle) 64 throws PackageManager.NameNotFoundException { 65 this(pm, getServiceInfoOrThrow(comp, userHandle)); 66 } 67 68 @NonNull getServiceInfoOrThrow(@onNull ComponentName comp, int userHandle)69 private static ServiceInfo getServiceInfoOrThrow(@NonNull ComponentName comp, int userHandle) 70 throws PackageManager.NameNotFoundException { 71 try { 72 ServiceInfo si = AppGlobals.getPackageManager().getServiceInfo(comp, 73 PackageManager.GET_META_DATA 74 | PackageManager.MATCH_DIRECT_BOOT_AWARE 75 | PackageManager.MATCH_DIRECT_BOOT_UNAWARE, 76 userHandle); 77 if (si != null) { 78 return si; 79 } 80 } catch (RemoteException e) { 81 } 82 throw new PackageManager.NameNotFoundException(comp.toString()); 83 } 84 85 /** 86 * Loads the service metadata published by the component. Success is indicated by 87 * {@link #getParseError()}. 88 * 89 * @param pm A PackageManager from which the XML can be loaded; usually the PackageManager 90 * from which {@code si} was originally retrieved. 91 * @param si The {@link VoiceInteractionService} info. 92 */ VoiceInteractionServiceInfo(@onNull PackageManager pm, @NonNull ServiceInfo si)93 public VoiceInteractionServiceInfo(@NonNull PackageManager pm, @NonNull ServiceInfo si) { 94 if (!Manifest.permission.BIND_VOICE_INTERACTION.equals(si.permission)) { 95 mParseError = "Service does not require permission " 96 + Manifest.permission.BIND_VOICE_INTERACTION; 97 return; 98 } 99 100 try (XmlResourceParser parser = si.loadXmlMetaData(pm, 101 VoiceInteractionService.SERVICE_META_DATA)) { 102 if (parser == null) { 103 mParseError = "No " + VoiceInteractionService.SERVICE_META_DATA 104 + " meta-data for " + si.packageName; 105 return; 106 } 107 108 Resources res = pm.getResourcesForApplication(si.applicationInfo); 109 110 AttributeSet attrs = Xml.asAttributeSet(parser); 111 112 int type; 113 while ((type=parser.next()) != XmlPullParser.END_DOCUMENT 114 && type != XmlPullParser.START_TAG) { 115 } 116 117 String nodeName = parser.getName(); 118 if (!"voice-interaction-service".equals(nodeName)) { 119 mParseError = "Meta-data does not start with voice-interaction-service tag"; 120 return; 121 } 122 123 TypedArray array = res.obtainAttributes(attrs, 124 com.android.internal.R.styleable.VoiceInteractionService); 125 mSessionService = array.getString( 126 com.android.internal.R.styleable.VoiceInteractionService_sessionService); 127 mRecognitionService = array.getString( 128 com.android.internal.R.styleable.VoiceInteractionService_recognitionService); 129 mSettingsActivity = array.getString( 130 com.android.internal.R.styleable.VoiceInteractionService_settingsActivity); 131 mSupportsAssist = array.getBoolean( 132 com.android.internal.R.styleable.VoiceInteractionService_supportsAssist, 133 false); 134 mSupportsLaunchFromKeyguard = array.getBoolean(com.android.internal. 135 R.styleable.VoiceInteractionService_supportsLaunchVoiceAssistFromKeyguard, 136 false); 137 mSupportsLocalInteraction = array.getBoolean(com.android.internal. 138 R.styleable.VoiceInteractionService_supportsLocalInteraction, false); 139 mHotwordDetectionService = array.getString(com.android.internal.R.styleable 140 .VoiceInteractionService_hotwordDetectionService); 141 mVisualQueryDetectionService = array.getString(com.android.internal.R.styleable 142 .VoiceInteractionService_visualQueryDetectionService); 143 array.recycle(); 144 if (mSessionService == null) { 145 mParseError = "No sessionService specified"; 146 return; 147 } 148 if (mRecognitionService == null) { 149 mParseError = "No recognitionService specified"; 150 return; 151 } 152 } catch (XmlPullParserException | IOException | PackageManager.NameNotFoundException e) { 153 mParseError = "Error parsing voice interation service meta-data: " + e; 154 Log.w(TAG, "error parsing voice interaction service meta-data", e); 155 return; 156 } 157 mServiceInfo = si; 158 } 159 getParseError()160 public String getParseError() { 161 return mParseError; 162 } 163 getServiceInfo()164 public ServiceInfo getServiceInfo() { 165 return mServiceInfo; 166 } 167 getSessionService()168 public String getSessionService() { 169 return mSessionService; 170 } 171 getRecognitionService()172 public String getRecognitionService() { 173 return mRecognitionService; 174 } 175 getSettingsActivity()176 public String getSettingsActivity() { 177 return mSettingsActivity; 178 } 179 getSupportsAssist()180 public boolean getSupportsAssist() { 181 return mSupportsAssist; 182 } 183 getSupportsLaunchFromKeyguard()184 public boolean getSupportsLaunchFromKeyguard() { 185 return mSupportsLaunchFromKeyguard; 186 } 187 getSupportsLocalInteraction()188 public boolean getSupportsLocalInteraction() { 189 return mSupportsLocalInteraction; 190 } 191 192 @Nullable getHotwordDetectionService()193 public String getHotwordDetectionService() { 194 return mHotwordDetectionService; 195 } 196 197 @Nullable getVisualQueryDetectionService()198 public String getVisualQueryDetectionService() { 199 return mVisualQueryDetectionService; 200 } 201 } 202