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.hardware.soundtrigger;
18 
19 import android.compat.annotation.UnsupportedAppUsage;
20 import android.os.Handler;
21 import android.os.Looper;
22 import android.os.Message;
23 
24 import java.lang.ref.WeakReference;
25 
26 /**
27  * The SoundTriggerModule provides APIs to control sound models and sound detection
28  * on a given sound trigger hardware module.
29  *
30  * @hide
31  */
32 public class SoundTriggerModule {
33     @UnsupportedAppUsage
34     private long mNativeContext;
35 
36     @UnsupportedAppUsage
37     private int mId;
38     private NativeEventHandlerDelegate mEventHandlerDelegate;
39 
40     // to be kept in sync with core/jni/android_hardware_SoundTrigger.cpp
41     private static final int EVENT_RECOGNITION = 1;
42     private static final int EVENT_SERVICE_DIED = 2;
43     private static final int EVENT_SOUNDMODEL = 3;
44     private static final int EVENT_SERVICE_STATE_CHANGE = 4;
45 
SoundTriggerModule(int moduleId, SoundTrigger.StatusListener listener, Handler handler)46     SoundTriggerModule(int moduleId, SoundTrigger.StatusListener listener, Handler handler) {
47         mId = moduleId;
48         mEventHandlerDelegate = new NativeEventHandlerDelegate(listener, handler);
49         native_setup(SoundTrigger.getCurrentOpPackageName(),
50                 new WeakReference<SoundTriggerModule>(this));
51     }
native_setup(String opPackageName, Object moduleThis)52     private native void native_setup(String opPackageName, Object moduleThis);
53 
54     @Override
finalize()55     protected void finalize() {
56         native_finalize();
57     }
native_finalize()58     private native void native_finalize();
59 
60     /**
61      * Detach from this module. The {@link SoundTrigger.StatusListener} callback will not be called
62      * anymore and associated resources will be released.
63      * */
64     @UnsupportedAppUsage
detach()65     public native void detach();
66 
67     /**
68      * Load a {@link SoundTrigger.SoundModel} to the hardware. A sound model must be loaded in
69      * order to start listening to a key phrase in this model.
70      * @param model The sound model to load.
71      * @param soundModelHandle an array of int where the sound model handle will be returned.
72      * @return - {@link SoundTrigger#STATUS_OK} in case of success
73      *         - {@link SoundTrigger#STATUS_ERROR} in case of unspecified error
74      *         - {@link SoundTrigger#STATUS_PERMISSION_DENIED} if the caller does not have
75      *         system permission
76      *         - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
77      *         - {@link SoundTrigger#STATUS_BAD_VALUE} if parameters are invalid
78      *         - {@link SoundTrigger#STATUS_DEAD_OBJECT} if the binder transaction to the native
79      *         service fails
80      *         - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence
81      */
82     @UnsupportedAppUsage
loadSoundModel(SoundTrigger.SoundModel model, int[] soundModelHandle)83     public native int loadSoundModel(SoundTrigger.SoundModel model, int[] soundModelHandle);
84 
85     /**
86      * Unload a {@link SoundTrigger.SoundModel} and abort any pendiong recognition
87      * @param soundModelHandle The sound model handle
88      * @return - {@link SoundTrigger#STATUS_OK} in case of success
89      *         - {@link SoundTrigger#STATUS_ERROR} in case of unspecified error
90      *         - {@link SoundTrigger#STATUS_PERMISSION_DENIED} if the caller does not have
91      *         system permission
92      *         - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
93      *         - {@link SoundTrigger#STATUS_BAD_VALUE} if the sound model handle is invalid
94      *         - {@link SoundTrigger#STATUS_DEAD_OBJECT} if the binder transaction to the native
95      *         service fails
96      */
97     @UnsupportedAppUsage
unloadSoundModel(int soundModelHandle)98     public native int unloadSoundModel(int soundModelHandle);
99 
100     /**
101      * Start listening to all key phrases in a {@link SoundTrigger.SoundModel}.
102      * Recognition must be restarted after each callback (success or failure) received on
103      * the {@link SoundTrigger.StatusListener}.
104      * @param soundModelHandle The sound model handle to start listening to
105      * @param config contains configuration information for this recognition request:
106      *  recognition mode, keyphrases, users, minimum confidence levels...
107      * @return - {@link SoundTrigger#STATUS_OK} in case of success
108      *         - {@link SoundTrigger#STATUS_ERROR} in case of unspecified error
109      *         - {@link SoundTrigger#STATUS_PERMISSION_DENIED} if the caller does not have
110      *         system permission
111      *         - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
112      *         - {@link SoundTrigger#STATUS_BAD_VALUE} if the sound model handle is invalid
113      *         - {@link SoundTrigger#STATUS_DEAD_OBJECT} if the binder transaction to the native
114      *         service fails
115      *         - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence
116      */
117     @UnsupportedAppUsage
startRecognition(int soundModelHandle, SoundTrigger.RecognitionConfig config)118     public native int startRecognition(int soundModelHandle, SoundTrigger.RecognitionConfig config);
119 
120     /**
121      * Stop listening to all key phrases in a {@link SoundTrigger.SoundModel}
122      * @param soundModelHandle The sound model handle to stop listening to
123      * @return - {@link SoundTrigger#STATUS_OK} in case of success
124      *         - {@link SoundTrigger#STATUS_ERROR} in case of unspecified error
125      *         - {@link SoundTrigger#STATUS_PERMISSION_DENIED} if the caller does not have
126      *         system permission
127      *         - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
128      *         - {@link SoundTrigger#STATUS_BAD_VALUE} if the sound model handle is invalid
129      *         - {@link SoundTrigger#STATUS_DEAD_OBJECT} if the binder transaction to the native
130      *         service fails
131      *         - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence
132      */
133     @UnsupportedAppUsage
stopRecognition(int soundModelHandle)134     public native int stopRecognition(int soundModelHandle);
135 
136     /**
137      * Get the current state of a {@link SoundTrigger.SoundModel}.
138      * The state will be returned asynchronously as a {@link SoundTrigger#RecognitionEvent}
139      * in the callback registered in the {@link SoundTrigger.startRecognition} method.
140      * @param soundModelHandle The sound model handle indicating which model's state to return
141      * @return - {@link SoundTrigger#STATUS_OK} in case of success
142      *         - {@link SoundTrigger#STATUS_ERROR} in case of unspecified error
143      *         - {@link SoundTrigger#STATUS_PERMISSION_DENIED} if the caller does not have
144      *         system permission
145      *         - {@link SoundTrigger#STATUS_NO_INIT} if the native service cannot be reached
146      *         - {@link SoundTrigger#STATUS_BAD_VALUE} if the sound model handle is invalid
147      *         - {@link SoundTrigger#STATUS_DEAD_OBJECT} if the binder transaction to the native
148      *         service fails
149      *         - {@link SoundTrigger#STATUS_INVALID_OPERATION} if the call is out of sequence
150      */
getModelState(int soundModelHandle)151     public native int getModelState(int soundModelHandle);
152 
153     private class NativeEventHandlerDelegate {
154         private final Handler mHandler;
155 
NativeEventHandlerDelegate(final SoundTrigger.StatusListener listener, Handler handler)156         NativeEventHandlerDelegate(final SoundTrigger.StatusListener listener,
157                                    Handler handler) {
158             // find the looper for our new event handler
159             Looper looper;
160             if (handler != null) {
161                 looper = handler.getLooper();
162             } else {
163                 looper = Looper.getMainLooper();
164             }
165 
166             // construct the event handler with this looper
167             if (looper != null) {
168                 // implement the event handler delegate
169                 mHandler = new Handler(looper) {
170                     @Override
171                     public void handleMessage(Message msg) {
172                         switch(msg.what) {
173                         case EVENT_RECOGNITION:
174                             if (listener != null) {
175                                 listener.onRecognition(
176                                         (SoundTrigger.RecognitionEvent)msg.obj);
177                             }
178                             break;
179                         case EVENT_SOUNDMODEL:
180                             if (listener != null) {
181                                 listener.onSoundModelUpdate(
182                                         (SoundTrigger.SoundModelEvent)msg.obj);
183                             }
184                             break;
185                         case EVENT_SERVICE_STATE_CHANGE:
186                             if (listener != null) {
187                                 listener.onServiceStateChange(msg.arg1);
188                             }
189                             break;
190                         case EVENT_SERVICE_DIED:
191                             if (listener != null) {
192                                 listener.onServiceDied();
193                             }
194                             break;
195                         default:
196                             break;
197                         }
198                     }
199                 };
200             } else {
201                 mHandler = null;
202             }
203         }
204 
handler()205         Handler handler() {
206             return mHandler;
207         }
208     }
209 
210     @SuppressWarnings("unused")
211     @UnsupportedAppUsage
postEventFromNative(Object module_ref, int what, int arg1, int arg2, Object obj)212     private static void postEventFromNative(Object module_ref,
213                                             int what, int arg1, int arg2, Object obj) {
214         SoundTriggerModule module = (SoundTriggerModule)((WeakReference)module_ref).get();
215         if (module == null) {
216             return;
217         }
218 
219         NativeEventHandlerDelegate delegate = module.mEventHandlerDelegate;
220         if (delegate != null) {
221             Handler handler = delegate.handler();
222             if (handler != null) {
223                 Message m = handler.obtainMessage(what, arg1, arg2, obj);
224                 handler.sendMessage(m);
225             }
226         }
227     }
228 }
229