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