blob: 982f43dcc16199ae57a71e3e5ce0da6f61172681 [file] [log] [blame]
Dianne Hackborn91097de2014-04-04 18:02:06 -07001/**
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
17package android.service.voice;
18
19import android.annotation.SdkConstant;
Dianne Hackborn91097de2014-04-04 18:02:06 -070020import android.app.Service;
Dianne Hackbornfee756f2014-07-16 17:31:10 -070021import android.content.ComponentName;
Dianne Hackborn91097de2014-04-04 18:02:06 -070022import android.content.Context;
23import android.content.Intent;
Sandeepd7018202014-07-10 15:15:39 -070024import android.hardware.soundtrigger.KeyphraseEnrollmentInfo;
Dianne Hackborn18f0d352014-04-25 17:06:18 -070025import android.os.Bundle;
Dianne Hackbornfee756f2014-07-16 17:31:10 -070026import android.os.Handler;
Dianne Hackborn91097de2014-04-04 18:02:06 -070027import android.os.IBinder;
Dianne Hackbornfee756f2014-07-16 17:31:10 -070028import android.os.Message;
Dianne Hackborn91097de2014-04-04 18:02:06 -070029import android.os.RemoteException;
30import android.os.ServiceManager;
Sandeep Siddharthae5706782014-05-29 17:54:06 -070031
Dianne Hackbornfee756f2014-07-16 17:31:10 -070032import android.provider.Settings;
Sandeep Siddhartha22968952014-06-10 12:32:53 -070033import com.android.internal.annotations.VisibleForTesting;
Dianne Hackborn91097de2014-04-04 18:02:06 -070034import com.android.internal.app.IVoiceInteractionManagerService;
35
Sandeep Siddharthaf7a13df2014-06-11 14:11:06 -070036
Dianne Hackbornc03c9162014-05-02 10:45:59 -070037/**
38 * Top-level service of the current global voice interactor, which is providing
39 * support for hotwording, the back-end of a {@link android.app.VoiceInteractor}, etc.
40 * The current VoiceInteractionService that has been selected by the user is kept
41 * always running by the system, to allow it to do things like listen for hotwords
42 * in the background to instigate voice interactions.
43 *
44 * <p>Because this service is always running, it should be kept as lightweight as
45 * possible. Heavy-weight operations (including showing UI) should be implemented
46 * in the associated {@link android.service.voice.VoiceInteractionSessionService} when
47 * an actual voice interaction is taking place, and that service should run in a
48 * separate process from this one.
49 */
Dianne Hackborn91097de2014-04-04 18:02:06 -070050public class VoiceInteractionService extends Service {
51 /**
52 * The {@link Intent} that must be declared as handled by the service.
53 * To be supported, the service must also require the
54 * {@link android.Manifest.permission#BIND_VOICE_INTERACTION} permission so
55 * that other applications can not abuse it.
56 */
57 @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
58 public static final String SERVICE_INTERFACE =
59 "android.service.voice.VoiceInteractionService";
60
61 /**
62 * Name under which a VoiceInteractionService component publishes information about itself.
63 * This meta-data should reference an XML resource containing a
64 * <code>&lt;{@link
65 * android.R.styleable#VoiceInteractionService voice-interaction-service}&gt;</code> tag.
66 */
67 public static final String SERVICE_META_DATA = "android.voice_interaction";
68
69 IVoiceInteractionService mInterface = new IVoiceInteractionService.Stub() {
Dianne Hackbornfee756f2014-07-16 17:31:10 -070070 @Override public void ready() {
71 mHandler.sendEmptyMessage(MSG_READY);
72 }
Dianne Hackborn91097de2014-04-04 18:02:06 -070073 };
74
Dianne Hackbornfee756f2014-07-16 17:31:10 -070075 MyHandler mHandler;
76
Dianne Hackborn91097de2014-04-04 18:02:06 -070077 IVoiceInteractionManagerService mSystemService;
78
Sandeep Siddharthae912ac02014-06-03 16:29:37 -070079 private KeyphraseEnrollmentInfo mKeyphraseEnrollmentInfo;
80
Dianne Hackbornfee756f2014-07-16 17:31:10 -070081 static final int MSG_READY = 1;
82
83 class MyHandler extends Handler {
84 @Override
85 public void handleMessage(Message msg) {
86 switch (msg.what) {
87 case MSG_READY:
88 onReady();
89 break;
90 default:
91 super.handleMessage(msg);
92 }
93 }
94 }
95
96 /**
97 * Check whether the given service component is the currently active
98 * VoiceInteractionService.
99 */
100 public static boolean isActiveService(Context context, ComponentName service) {
101 String cur = Settings.Secure.getString(context.getContentResolver(),
102 Settings.Secure.VOICE_INTERACTION_SERVICE);
103 if (cur == null || cur.isEmpty()) {
104 return false;
105 }
106 ComponentName curComp = ComponentName.unflattenFromString(cur);
107 if (curComp == null) {
108 return false;
109 }
110 return curComp.equals(cur);
111 }
112
113 /**
114 * Initiate the execution of a new {@link android.service.voice.VoiceInteractionSession}.
115 * @param args Arbitrary arguments that will be propagated to the session.
116 */
Dianne Hackbornc03c9162014-05-02 10:45:59 -0700117 public void startSession(Bundle args) {
Dianne Hackbornfee756f2014-07-16 17:31:10 -0700118 if (mSystemService == null) {
119 throw new IllegalStateException("Not available until onReady() is called");
120 }
Dianne Hackborn91097de2014-04-04 18:02:06 -0700121 try {
Dianne Hackbornc03c9162014-05-02 10:45:59 -0700122 mSystemService.startSession(mInterface, args);
Dianne Hackborn91097de2014-04-04 18:02:06 -0700123 } catch (RemoteException e) {
124 }
125 }
126
127 @Override
128 public void onCreate() {
129 super.onCreate();
Dianne Hackbornfee756f2014-07-16 17:31:10 -0700130 mHandler = new MyHandler();
Dianne Hackborn91097de2014-04-04 18:02:06 -0700131 }
132
133 @Override
134 public IBinder onBind(Intent intent) {
135 if (SERVICE_INTERFACE.equals(intent.getAction())) {
136 return mInterface.asBinder();
137 }
138 return null;
139 }
Sandeep Siddharthae912ac02014-06-03 16:29:37 -0700140
141 /**
Dianne Hackbornfee756f2014-07-16 17:31:10 -0700142 * Called during service initialization to tell you when the system is ready
143 * to receive interaction from it. You should generally do initialization here
144 * rather than in {@link #onCreate()}. Methods such as {@link #startSession}
145 * and {@link #getAlwaysOnHotwordDetector} will not be operational until this point.
146 */
147 public void onReady() {
148 mSystemService = IVoiceInteractionManagerService.Stub.asInterface(
149 ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
150 mKeyphraseEnrollmentInfo = new KeyphraseEnrollmentInfo(getPackageManager());
Dianne Hackbornfee756f2014-07-16 17:31:10 -0700151 }
152
153 /**
Sandeepd7018202014-07-10 15:15:39 -0700154 * @param keyphrase The keyphrase that's being used, for example "Hello Android".
155 * @param locale The locale for which the enrollment needs to be performed.
156 * This is a Java locale, for example "en_US".
157 * @param callback The callback to notify of detection events.
158 * @return An always-on hotword detector for the given keyphrase and locale.
Sandeep Siddharthae912ac02014-06-03 16:29:37 -0700159 */
Sandeepd7018202014-07-10 15:15:39 -0700160 public final AlwaysOnHotwordDetector getAlwaysOnHotwordDetector(
161 String keyphrase, String locale, AlwaysOnHotwordDetector.Callback callback) {
Dianne Hackbornfee756f2014-07-16 17:31:10 -0700162 if (mSystemService == null) {
163 throw new IllegalStateException("Not available until onReady() is called");
164 }
165 // TODO: Cache instances and return the same one instead of creating a new interactor
166 // for the same keyphrase/locale combination.
Sandeepd7018202014-07-10 15:15:39 -0700167 return new AlwaysOnHotwordDetector(keyphrase, locale, callback,
Sandeep Siddhartha05589722014-07-17 16:21:54 -0700168 mKeyphraseEnrollmentInfo, mInterface, mSystemService);
Sandeep Siddharthae912ac02014-06-03 16:29:37 -0700169 }
Sandeep Siddhartha22968952014-06-10 12:32:53 -0700170
171 /**
172 * @return Details of keyphrases available for enrollment.
173 * @hide
174 */
175 @VisibleForTesting
176 protected final KeyphraseEnrollmentInfo getKeyphraseEnrollmentInfo() {
177 return mKeyphraseEnrollmentInfo;
178 }
Dianne Hackborn91097de2014-04-04 18:02:06 -0700179}