VoiceInteractionService.java revision fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7
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 17package android.service.voice; 18 19import android.annotation.SdkConstant; 20import android.app.Service; 21import android.content.ComponentName; 22import android.content.Context; 23import android.content.Intent; 24import android.hardware.soundtrigger.KeyphraseEnrollmentInfo; 25import android.hardware.soundtrigger.SoundTriggerHelper; 26import android.os.Bundle; 27import android.os.Handler; 28import android.os.IBinder; 29import android.os.Message; 30import android.os.RemoteException; 31import android.os.ServiceManager; 32 33import android.provider.Settings; 34import com.android.internal.annotations.VisibleForTesting; 35import com.android.internal.app.IVoiceInteractionManagerService; 36 37 38/** 39 * Top-level service of the current global voice interactor, which is providing 40 * support for hotwording, the back-end of a {@link android.app.VoiceInteractor}, etc. 41 * The current VoiceInteractionService that has been selected by the user is kept 42 * always running by the system, to allow it to do things like listen for hotwords 43 * in the background to instigate voice interactions. 44 * 45 * <p>Because this service is always running, it should be kept as lightweight as 46 * possible. Heavy-weight operations (including showing UI) should be implemented 47 * in the associated {@link android.service.voice.VoiceInteractionSessionService} when 48 * an actual voice interaction is taking place, and that service should run in a 49 * separate process from this one. 50 */ 51public class VoiceInteractionService extends Service { 52 /** 53 * The {@link Intent} that must be declared as handled by the service. 54 * To be supported, the service must also require the 55 * {@link android.Manifest.permission#BIND_VOICE_INTERACTION} permission so 56 * that other applications can not abuse it. 57 */ 58 @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION) 59 public static final String SERVICE_INTERFACE = 60 "android.service.voice.VoiceInteractionService"; 61 62 /** 63 * Name under which a VoiceInteractionService component publishes information about itself. 64 * This meta-data should reference an XML resource containing a 65 * <code><{@link 66 * android.R.styleable#VoiceInteractionService voice-interaction-service}></code> tag. 67 */ 68 public static final String SERVICE_META_DATA = "android.voice_interaction"; 69 70 IVoiceInteractionService mInterface = new IVoiceInteractionService.Stub() { 71 @Override public void ready() { 72 mHandler.sendEmptyMessage(MSG_READY); 73 } 74 }; 75 76 MyHandler mHandler; 77 78 IVoiceInteractionManagerService mSystemService; 79 80 private KeyphraseEnrollmentInfo mKeyphraseEnrollmentInfo; 81 private SoundTriggerHelper mSoundTriggerHelper; 82 83 static final int MSG_READY = 1; 84 85 class MyHandler extends Handler { 86 @Override 87 public void handleMessage(Message msg) { 88 switch (msg.what) { 89 case MSG_READY: 90 onReady(); 91 break; 92 default: 93 super.handleMessage(msg); 94 } 95 } 96 } 97 98 /** 99 * Check whether the given service component is the currently active 100 * VoiceInteractionService. 101 */ 102 public static boolean isActiveService(Context context, ComponentName service) { 103 String cur = Settings.Secure.getString(context.getContentResolver(), 104 Settings.Secure.VOICE_INTERACTION_SERVICE); 105 if (cur == null || cur.isEmpty()) { 106 return false; 107 } 108 ComponentName curComp = ComponentName.unflattenFromString(cur); 109 if (curComp == null) { 110 return false; 111 } 112 return curComp.equals(cur); 113 } 114 115 /** 116 * Initiate the execution of a new {@link android.service.voice.VoiceInteractionSession}. 117 * @param args Arbitrary arguments that will be propagated to the session. 118 */ 119 public void startSession(Bundle args) { 120 if (mSystemService == null) { 121 throw new IllegalStateException("Not available until onReady() is called"); 122 } 123 try { 124 mSystemService.startSession(mInterface, args); 125 } catch (RemoteException e) { 126 } 127 } 128 129 @Override 130 public void onCreate() { 131 super.onCreate(); 132 mHandler = new MyHandler(); 133 } 134 135 @Override 136 public IBinder onBind(Intent intent) { 137 if (SERVICE_INTERFACE.equals(intent.getAction())) { 138 return mInterface.asBinder(); 139 } 140 return null; 141 } 142 143 /** 144 * Called during service initialization to tell you when the system is ready 145 * to receive interaction from it. You should generally do initialization here 146 * rather than in {@link #onCreate()}. Methods such as {@link #startSession} 147 * and {@link #getAlwaysOnHotwordDetector} will not be operational until this point. 148 */ 149 public void onReady() { 150 mSystemService = IVoiceInteractionManagerService.Stub.asInterface( 151 ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE)); 152 mKeyphraseEnrollmentInfo = new KeyphraseEnrollmentInfo(getPackageManager()); 153 mSoundTriggerHelper = new SoundTriggerHelper(); 154 } 155 156 /** 157 * @param keyphrase The keyphrase that's being used, for example "Hello Android". 158 * @param locale The locale for which the enrollment needs to be performed. 159 * This is a Java locale, for example "en_US". 160 * @param callback The callback to notify of detection events. 161 * @return An always-on hotword detector for the given keyphrase and locale. 162 */ 163 public final AlwaysOnHotwordDetector getAlwaysOnHotwordDetector( 164 String keyphrase, String locale, AlwaysOnHotwordDetector.Callback callback) { 165 if (mSystemService == null) { 166 throw new IllegalStateException("Not available until onReady() is called"); 167 } 168 // TODO: Cache instances and return the same one instead of creating a new interactor 169 // for the same keyphrase/locale combination. 170 return new AlwaysOnHotwordDetector(keyphrase, locale, callback, 171 mKeyphraseEnrollmentInfo, mSoundTriggerHelper, mInterface, mSystemService); 172 } 173 174 /** 175 * @return Details of keyphrases available for enrollment. 176 * @hide 177 */ 178 @VisibleForTesting 179 protected final KeyphraseEnrollmentInfo getKeyphraseEnrollmentInfo() { 180 return mKeyphraseEnrollmentInfo; 181 } 182} 183