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