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