1d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep/**
2d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep * Copyright (C) 2014 The Android Open Source Project
3d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep *
4d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep * Licensed under the Apache License, Version 2.0 (the "License");
5d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep * you may not use this file except in compliance with the License.
6d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep * You may obtain a copy of the License at
7d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep *
8d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep *      http://www.apache.org/licenses/LICENSE-2.0
9d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep *
10d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep * Unless required by applicable law or agreed to in writing, software
11d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep * distributed under the License is distributed on an "AS IS" BASIS,
12d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep * See the License for the specific language governing permissions and
14d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep * limitations under the License.
15d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep */
16d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep
17055897208d659e9734a82def88be4a806ff55448Sandeep Siddharthapackage com.android.server.voiceinteraction;
18d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep
19efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddharthaimport android.content.BroadcastReceiver;
20efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddharthaimport android.content.Context;
21efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddharthaimport android.content.Intent;
22efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddharthaimport android.content.IntentFilter;
23055897208d659e9734a82def88be4a806ff55448Sandeep Siddharthaimport android.hardware.soundtrigger.IRecognitionStatusCallback;
24055897208d659e9734a82def88be4a806ff55448Sandeep Siddharthaimport android.hardware.soundtrigger.SoundTrigger;
25055897208d659e9734a82def88be4a806ff55448Sandeep Siddharthaimport android.hardware.soundtrigger.SoundTrigger.Keyphrase;
266817337118655d5792e36e954b123e6daa4174a6Sandeep Siddharthaimport android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionEvent;
276817337118655d5792e36e954b123e6daa4174a6Sandeep Siddharthaimport android.hardware.soundtrigger.SoundTrigger.KeyphraseRecognitionExtra;
28055897208d659e9734a82def88be4a806ff55448Sandeep Siddharthaimport android.hardware.soundtrigger.SoundTrigger.KeyphraseSoundModel;
29d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeepimport android.hardware.soundtrigger.SoundTrigger.ModuleProperties;
308ecaf5f5cfd18e0436db1a27ccf46a063e9aacd7Sandeep Siddharthaimport android.hardware.soundtrigger.SoundTrigger.RecognitionConfig;
31d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeepimport android.hardware.soundtrigger.SoundTrigger.RecognitionEvent;
32d3b8223377b8046280e4c09e728edc600171f941Eric Laurentimport android.hardware.soundtrigger.SoundTrigger.SoundModelEvent;
33055897208d659e9734a82def88be4a806ff55448Sandeep Siddharthaimport android.hardware.soundtrigger.SoundTriggerModule;
34efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddharthaimport android.os.PowerManager;
35055897208d659e9734a82def88be4a806ff55448Sandeep Siddharthaimport android.os.RemoteException;
36cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddharthaimport android.telephony.PhoneStateListener;
37cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddharthaimport android.telephony.TelephonyManager;
38d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeepimport android.util.Slog;
39d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep
406b8556d2e320b631d3741bf064796efddb6e51dfSandeep Siddharthaimport java.io.FileDescriptor;
416b8556d2e320b631d3741bf064796efddb6e51dfSandeep Siddharthaimport java.io.PrintWriter;
42d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeepimport java.util.ArrayList;
43d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep
44d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep/**
45d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep * Helper for {@link SoundTrigger} APIs.
46d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep * Currently this just acts as an abstraction over all SoundTrigger API calls.
47d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep *
48d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep * @hide
49d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep */
50d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeeppublic class SoundTriggerHelper implements SoundTrigger.StatusListener {
51d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep    static final String TAG = "SoundTriggerHelper";
528cf8f71644643601fe8c3e9538fd00412b1ae8b1Sandeep Siddhartha    static final boolean DBG = false;
53d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep
54d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep    /**
55055897208d659e9734a82def88be4a806ff55448Sandeep Siddhartha     * Return codes for {@link #startRecognition(int, KeyphraseSoundModel,
56055897208d659e9734a82def88be4a806ff55448Sandeep Siddhartha     *      IRecognitionStatusCallback, RecognitionConfig)},
57055897208d659e9734a82def88be4a806ff55448Sandeep Siddhartha     * {@link #stopRecognition(int, IRecognitionStatusCallback)}
58d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep     */
59055897208d659e9734a82def88be4a806ff55448Sandeep Siddhartha    public static final int STATUS_ERROR = SoundTrigger.STATUS_ERROR;
60055897208d659e9734a82def88be4a806ff55448Sandeep Siddhartha    public static final int STATUS_OK = SoundTrigger.STATUS_OK;
61d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep
62cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    private static final int INVALID_VALUE = Integer.MIN_VALUE;
63d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep
64cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    /** The {@link ModuleProperties} for the system, or null if none exists. */
65055897208d659e9734a82def88be4a806ff55448Sandeep Siddhartha    final ModuleProperties moduleProperties;
66d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep
67d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep    /** The properties for the DSP module */
682475e38c10a02973665752e0b829153a5c493b28Eric Laurent    private SoundTriggerModule mModule;
69cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    private final Object mLock = new Object();
70efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha    private final Context mContext;
71cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    private final TelephonyManager mTelephonyManager;
72cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    private final PhoneStateListener mPhoneStateListener;
73efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha    private final PowerManager mPowerManager;
74cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha
75cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    // TODO: Since many layers currently only deal with one recognition
76cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    // we simplify things by assuming one listener here too.
77cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    private IRecognitionStatusCallback mActiveListener;
78cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    private int mKeyphraseId = INVALID_VALUE;
79cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    private int mCurrentSoundModelHandle = INVALID_VALUE;
8045c00b5877e908f44853783b42deb437cfd30d94Sandeep Siddhartha    private KeyphraseSoundModel mCurrentSoundModel = null;
812c0273e50a3162595e9a54030166f2369b039a5aSandeep Siddhartha    // FIXME: Ideally this should not be stored if allowMultipleTriggers happens at a lower layer.
822c0273e50a3162595e9a54030166f2369b039a5aSandeep Siddhartha    private RecognitionConfig mRecognitionConfig = null;
83cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    private boolean mRequested = false;
84cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    private boolean mCallActive = false;
85efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha    private boolean mIsPowerSaveMode = false;
86cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    // Indicates if the native sound trigger service is disabled or not.
87cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    // This is an indirect indication of the microphone being open in some other application.
88cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    private boolean mServiceDisabled = false;
89cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    private boolean mStarted = false;
90efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha    private PowerSaveModeListener mPowerSaveModeListener;
91cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha
92efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha    SoundTriggerHelper(Context context) {
93d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep        ArrayList <ModuleProperties> modules = new ArrayList<>();
94d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep        int status = SoundTrigger.listModules(modules);
95efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha        mContext = context;
96efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha        mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
97efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha        mPowerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
98cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        mPhoneStateListener = new MyCallStateListener();
99d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep        if (status != SoundTrigger.STATUS_OK || modules.size() == 0) {
100a433af6b739e6d1b57a06913401086cdd8eccffbSandeep Siddhartha            Slog.w(TAG, "listModules status=" + status + ", # of modules=" + modules.size());
101055897208d659e9734a82def88be4a806ff55448Sandeep Siddhartha            moduleProperties = null;
102d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep            mModule = null;
103d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep        } else {
104d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep            // TODO: Figure out how to determine which module corresponds to the DSP hardware.
105055897208d659e9734a82def88be4a806ff55448Sandeep Siddhartha            moduleProperties = modules.get(0);
106d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep        }
107d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep    }
108d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep
109d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep    /**
110055897208d659e9734a82def88be4a806ff55448Sandeep Siddhartha     * Starts recognition for the given keyphraseId.
111d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep     *
112d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep     * @param keyphraseId The identifier of the keyphrase for which
113d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep     *        the recognition is to be started.
1148ecaf5f5cfd18e0436db1a27ccf46a063e9aacd7Sandeep Siddhartha     * @param soundModel The sound model to use for recognition.
115d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep     * @param listener The listener for the recognition events related to the given keyphrase.
116d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep     * @return One of {@link #STATUS_ERROR} or {@link #STATUS_OK}.
117d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep     */
118cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    int startRecognition(int keyphraseId,
119055897208d659e9734a82def88be4a806ff55448Sandeep Siddhartha            KeyphraseSoundModel soundModel,
120055897208d659e9734a82def88be4a806ff55448Sandeep Siddhartha            IRecognitionStatusCallback listener,
121055897208d659e9734a82def88be4a806ff55448Sandeep Siddhartha            RecognitionConfig recognitionConfig) {
122cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        if (soundModel == null || listener == null || recognitionConfig == null) {
123d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep            return STATUS_ERROR;
124d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep        }
125d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep
126cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        synchronized (mLock) {
127cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            if (DBG) {
128cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                Slog.d(TAG, "startRecognition for keyphraseId=" + keyphraseId
129cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                        + " soundModel=" + soundModel + ", listener=" + listener.asBinder()
130cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                        + ", recognitionConfig=" + recognitionConfig);
131cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                Slog.d(TAG, "moduleProperties=" + moduleProperties);
132cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                Slog.d(TAG, "current listener="
133cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                        + (mActiveListener == null ? "null" : mActiveListener.asBinder()));
134cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                Slog.d(TAG, "current SoundModel handle=" + mCurrentSoundModelHandle);
135cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                Slog.d(TAG, "current SoundModel UUID="
13645c00b5877e908f44853783b42deb437cfd30d94Sandeep Siddhartha                        + (mCurrentSoundModel == null ? null : mCurrentSoundModel.uuid));
137299efbe1fbdca7bf2c852b67df3da361930f3ef2Sandeep Siddhartha            }
138110f569b47bc21fb38ec25b6110ee302ce137e06Sandeep Siddhartha
139cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            if (!mStarted) {
140cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                // Get the current call state synchronously for the first recognition.
141cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                mCallActive = mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE;
142cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                // Register for call state changes when the first call to start recognition occurs.
143cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
144efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha
145efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha                // Register for power saver mode changes when the first call to start recognition
146efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha                // occurs.
147efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha                if (mPowerSaveModeListener == null) {
148efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha                    mPowerSaveModeListener = new PowerSaveModeListener();
149efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha                    mContext.registerReceiver(mPowerSaveModeListener,
150efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha                            new IntentFilter(PowerManager.ACTION_POWER_SAVE_MODE_CHANGED));
151efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha                }
152efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha                mIsPowerSaveMode = mPowerManager.isPowerSaveMode();
153055897208d659e9734a82def88be4a806ff55448Sandeep Siddhartha            }
1548ecaf5f5cfd18e0436db1a27ccf46a063e9aacd7Sandeep Siddhartha
1552475e38c10a02973665752e0b829153a5c493b28Eric Laurent            if (moduleProperties == null) {
156cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                Slog.w(TAG, "Attempting startRecognition without the capability");
157299efbe1fbdca7bf2c852b67df3da361930f3ef2Sandeep Siddhartha                return STATUS_ERROR;
158299efbe1fbdca7bf2c852b67df3da361930f3ef2Sandeep Siddhartha            }
1592475e38c10a02973665752e0b829153a5c493b28Eric Laurent            if (mModule == null) {
1602475e38c10a02973665752e0b829153a5c493b28Eric Laurent                mModule = SoundTrigger.attachModule(moduleProperties.id, this, null);
1612475e38c10a02973665752e0b829153a5c493b28Eric Laurent                if (mModule == null) {
1622475e38c10a02973665752e0b829153a5c493b28Eric Laurent                    Slog.w(TAG, "startRecognition cannot attach to sound trigger module");
1632475e38c10a02973665752e0b829153a5c493b28Eric Laurent                    return STATUS_ERROR;
1642475e38c10a02973665752e0b829153a5c493b28Eric Laurent                }
1652475e38c10a02973665752e0b829153a5c493b28Eric Laurent            }
1668ecaf5f5cfd18e0436db1a27ccf46a063e9aacd7Sandeep Siddhartha
1678cf8f71644643601fe8c3e9538fd00412b1ae8b1Sandeep Siddhartha            // Unload the previous model if the current one isn't invalid
16845c00b5877e908f44853783b42deb437cfd30d94Sandeep Siddhartha            // and, it's not the same as the new one.
16945c00b5877e908f44853783b42deb437cfd30d94Sandeep Siddhartha            // This helps use cache and reuse the model and just start/stop it when necessary.
170cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            if (mCurrentSoundModelHandle != INVALID_VALUE
17145c00b5877e908f44853783b42deb437cfd30d94Sandeep Siddhartha                    && !soundModel.equals(mCurrentSoundModel)) {
172cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                Slog.w(TAG, "Unloading previous sound model");
173cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                int status = mModule.unloadSoundModel(mCurrentSoundModelHandle);
174cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                if (status != SoundTrigger.STATUS_OK) {
175cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                    Slog.w(TAG, "unloadSoundModel call failed with " + status);
176cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                }
17745c00b5877e908f44853783b42deb437cfd30d94Sandeep Siddhartha                internalClearSoundModelLocked();
178cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                mStarted = false;
179cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            }
180cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha
181cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            // If the previous recognition was by a different listener,
182cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            // Notify them that it was stopped.
183cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            if (mActiveListener != null && mActiveListener.asBinder() != listener.asBinder()) {
184cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                Slog.w(TAG, "Canceling previous recognition");
185cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                try {
186cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                    mActiveListener.onError(STATUS_ERROR);
187cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                } catch (RemoteException e) {
1886df952ec2208714d3206c54987eb388aee799be6Sandeep Siddhartha                    Slog.w(TAG, "RemoteException in onDetectionStopped", e);
189cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                }
190cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                mActiveListener = null;
191cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            }
192cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha
193cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            // Load the sound model if the current one is null.
194cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            int soundModelHandle = mCurrentSoundModelHandle;
195cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            if (mCurrentSoundModelHandle == INVALID_VALUE
19645c00b5877e908f44853783b42deb437cfd30d94Sandeep Siddhartha                    || mCurrentSoundModel == null) {
197cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                int[] handle = new int[] { INVALID_VALUE };
198cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                int status = mModule.loadSoundModel(soundModel, handle);
199cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                if (status != SoundTrigger.STATUS_OK) {
200cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                    Slog.w(TAG, "loadSoundModel call failed with " + status);
201cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                    return status;
202cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                }
203cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                if (handle[0] == INVALID_VALUE) {
204cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                    Slog.w(TAG, "loadSoundModel call returned invalid sound model handle");
205cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                    return STATUS_ERROR;
206cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                }
207cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                soundModelHandle = handle[0];
208cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            } else {
209cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                if (DBG) Slog.d(TAG, "Reusing previously loaded sound model");
210cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            }
211d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep
212cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            // Start the recognition.
213cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            mRequested = true;
214cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            mKeyphraseId = keyphraseId;
215cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            mCurrentSoundModelHandle = soundModelHandle;
21645c00b5877e908f44853783b42deb437cfd30d94Sandeep Siddhartha            mCurrentSoundModel = soundModel;
217cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            mRecognitionConfig = recognitionConfig;
218cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            // Register the new listener. This replaces the old one.
219cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            // There can only be a maximum of one active listener at any given time.
220cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            mActiveListener = listener;
221cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha
222cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            return updateRecognitionLocked(false /* don't notify for synchronous calls */);
223cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        }
224d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep    }
225d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep
226d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep    /**
227055897208d659e9734a82def88be4a806ff55448Sandeep Siddhartha     * Stops recognition for the given {@link Keyphrase} if a recognition is
228055897208d659e9734a82def88be4a806ff55448Sandeep Siddhartha     * currently active.
229d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep     *
2308ecaf5f5cfd18e0436db1a27ccf46a063e9aacd7Sandeep Siddhartha     * @param keyphraseId The identifier of the keyphrase for which
2318ecaf5f5cfd18e0436db1a27ccf46a063e9aacd7Sandeep Siddhartha     *        the recognition is to be stopped.
2328ecaf5f5cfd18e0436db1a27ccf46a063e9aacd7Sandeep Siddhartha     * @param listener The listener for the recognition events related to the given keyphrase.
2338ecaf5f5cfd18e0436db1a27ccf46a063e9aacd7Sandeep Siddhartha     *
234d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep     * @return One of {@link #STATUS_ERROR} or {@link #STATUS_OK}.
235d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep     */
236cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    int stopRecognition(int keyphraseId, IRecognitionStatusCallback listener) {
237cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        if (listener == null) {
238d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep            return STATUS_ERROR;
239d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep        }
240d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep
241cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        synchronized (mLock) {
242cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            if (DBG) {
243cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                Slog.d(TAG, "stopRecognition for keyphraseId=" + keyphraseId
244cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                        + ", listener=" + listener.asBinder());
245cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                Slog.d(TAG, "current listener="
246cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                        + (mActiveListener == null ? "null" : mActiveListener.asBinder()));
247cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            }
248cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha
249cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            if (moduleProperties == null || mModule == null) {
250cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                Slog.w(TAG, "Attempting stopRecognition without the capability");
251cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                return STATUS_ERROR;
252cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            }
253cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha
254cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            if (mActiveListener == null) {
255cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                // startRecognition hasn't been called or it failed.
256cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                Slog.w(TAG, "Attempting stopRecognition without a successful startRecognition");
257cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                return STATUS_ERROR;
258cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            }
259cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            if (mActiveListener.asBinder() != listener.asBinder()) {
260cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                // We don't allow a different listener to stop the recognition than the one
261cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                // that started it.
262cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                Slog.w(TAG, "Attempting stopRecognition for another recognition");
263cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                return STATUS_ERROR;
264cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            }
265cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha
266d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep            // Stop recognition if it's the current one, ignore otherwise.
267cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            mRequested = false;
268cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            int status = updateRecognitionLocked(false /* don't notify for synchronous calls */);
2698ecaf5f5cfd18e0436db1a27ccf46a063e9aacd7Sandeep Siddhartha            if (status != SoundTrigger.STATUS_OK) {
2702c0273e50a3162595e9a54030166f2369b039a5aSandeep Siddhartha                return status;
2718ecaf5f5cfd18e0436db1a27ccf46a063e9aacd7Sandeep Siddhartha            }
272cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha
27345c00b5877e908f44853783b42deb437cfd30d94Sandeep Siddhartha            // We leave the sound model loaded but not started, this helps us when we start
27445c00b5877e908f44853783b42deb437cfd30d94Sandeep Siddhartha            // back.
27545c00b5877e908f44853783b42deb437cfd30d94Sandeep Siddhartha            // Also clear the internal state once the recognition has been stopped.
276cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            internalClearStateLocked();
277cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            return status;
278d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep        }
279d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep    }
280d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep
281cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    /**
282cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha     * Stops all recognitions active currently and clears the internal state.
283cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha     */
284cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    void stopAllRecognitions() {
285cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        synchronized (mLock) {
286cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            if (moduleProperties == null || mModule == null) {
287cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                return;
288cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            }
2895e33fb057c20b84418d96574abe861e9d05956ebSandeep Siddhartha
290cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            if (mCurrentSoundModelHandle == INVALID_VALUE) {
291cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                return;
292cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            }
2935e33fb057c20b84418d96574abe861e9d05956ebSandeep Siddhartha
294cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            mRequested = false;
295cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            int status = updateRecognitionLocked(false /* don't notify for synchronous calls */);
296cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            internalClearStateLocked();
297cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        }
2985e33fb057c20b84418d96574abe861e9d05956ebSandeep Siddhartha    }
2995e33fb057c20b84418d96574abe861e9d05956ebSandeep Siddhartha
300d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep    //---- SoundTrigger.StatusListener methods
301d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep    @Override
302d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep    public void onRecognition(RecognitionEvent event) {
303cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        if (event == null || !(event instanceof KeyphraseRecognitionEvent)) {
3046817337118655d5792e36e954b123e6daa4174a6Sandeep Siddhartha            Slog.w(TAG, "Invalid recognition event!");
3058ecaf5f5cfd18e0436db1a27ccf46a063e9aacd7Sandeep Siddhartha            return;
3068ecaf5f5cfd18e0436db1a27ccf46a063e9aacd7Sandeep Siddhartha        }
307d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep
3086817337118655d5792e36e954b123e6daa4174a6Sandeep Siddhartha        if (DBG) Slog.d(TAG, "onRecognition: " + event);
309cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        synchronized (mLock) {
310cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            if (mActiveListener == null) {
311cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                Slog.w(TAG, "received onRecognition event without any listener for it");
312cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                return;
313cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            }
314cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            switch (event.status) {
315cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                // Fire aborts/failures to all listeners since it's not tied to a keyphrase.
316cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                case SoundTrigger.RECOGNITION_STATUS_ABORT:
317cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                    onRecognitionAbortLocked();
318cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                    break;
319cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                case SoundTrigger.RECOGNITION_STATUS_FAILURE:
320cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                    onRecognitionFailureLocked();
321cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                    break;
322cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                case SoundTrigger.RECOGNITION_STATUS_SUCCESS:
323cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                    onRecognitionSuccessLocked((KeyphraseRecognitionEvent) event);
324cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                    break;
325cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            }
326d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep        }
327d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep    }
328d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep
329cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    @Override
330d3b8223377b8046280e4c09e728edc600171f941Eric Laurent    public void onSoundModelUpdate(SoundModelEvent event) {
331d3b8223377b8046280e4c09e728edc600171f941Eric Laurent        if (event == null) {
332d3b8223377b8046280e4c09e728edc600171f941Eric Laurent            Slog.w(TAG, "Invalid sound model event!");
333d3b8223377b8046280e4c09e728edc600171f941Eric Laurent            return;
334d3b8223377b8046280e4c09e728edc600171f941Eric Laurent        }
335d3b8223377b8046280e4c09e728edc600171f941Eric Laurent        if (DBG) Slog.d(TAG, "onSoundModelUpdate: " + event);
336cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        synchronized (mLock) {
337cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            onSoundModelUpdatedLocked(event);
338cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        }
339d3b8223377b8046280e4c09e728edc600171f941Eric Laurent    }
340d3b8223377b8046280e4c09e728edc600171f941Eric Laurent
341cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    @Override
342d3b8223377b8046280e4c09e728edc600171f941Eric Laurent    public void onServiceStateChange(int state) {
343d3b8223377b8046280e4c09e728edc600171f941Eric Laurent        if (DBG) Slog.d(TAG, "onServiceStateChange, state: " + state);
344cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        synchronized (mLock) {
345cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            onServiceStateChangedLocked(SoundTrigger.SERVICE_STATE_DISABLED == state);
346cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        }
347d3b8223377b8046280e4c09e728edc600171f941Eric Laurent    }
348d3b8223377b8046280e4c09e728edc600171f941Eric Laurent
349d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep    @Override
350d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep    public void onServiceDied() {
351cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        Slog.e(TAG, "onServiceDied!!");
352cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        synchronized (mLock) {
353cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            onServiceDiedLocked();
354cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        }
355cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    }
356cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha
357cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    private void onCallStateChangedLocked(boolean callActive) {
358cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        if (mCallActive == callActive) {
359cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            // We consider multiple call states as being active
360cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            // so we check if something really changed or not here.
361cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            return;
362cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        }
363cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        mCallActive = callActive;
364cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        updateRecognitionLocked(true /* notify */);
365cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    }
366cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha
367efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha    private void onPowerSaveModeChangedLocked(boolean isPowerSaveMode) {
368efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha        if (mIsPowerSaveMode == isPowerSaveMode) {
369efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha            return;
370efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha        }
371efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha        mIsPowerSaveMode = isPowerSaveMode;
372efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha        updateRecognitionLocked(true /* notify */);
373efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha    }
374efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha
375cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    private void onSoundModelUpdatedLocked(SoundModelEvent event) {
376cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        // TODO: Handle sound model update here.
377cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    }
378cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha
379cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    private void onServiceStateChangedLocked(boolean disabled) {
380cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        if (disabled == mServiceDisabled) {
381cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            return;
382cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        }
383cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        mServiceDisabled = disabled;
384cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        updateRecognitionLocked(true /* notify */);
385cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    }
386cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha
387cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    private void onRecognitionAbortLocked() {
388cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        Slog.w(TAG, "Recognition aborted");
389cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        // No-op
390cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        // This is handled via service state changes instead.
391cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    }
392cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha
393cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    private void onRecognitionFailureLocked() {
394cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        Slog.w(TAG, "Recognition failure");
395cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        try {
396cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            if (mActiveListener != null) {
397cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                mActiveListener.onError(STATUS_ERROR);
398cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            }
399cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        } catch (RemoteException e) {
400cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            Slog.w(TAG, "RemoteException in onError", e);
401cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        } finally {
402cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            internalClearStateLocked();
403cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        }
404cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    }
405cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha
406cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    private void onRecognitionSuccessLocked(KeyphraseRecognitionEvent event) {
407cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        Slog.i(TAG, "Recognition success");
408cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        KeyphraseRecognitionExtra[] keyphraseExtras =
409cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                ((KeyphraseRecognitionEvent) event).keyphraseExtras;
410cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        if (keyphraseExtras == null || keyphraseExtras.length == 0) {
411cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            Slog.w(TAG, "Invalid keyphrase recognition event!");
412cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            return;
413cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        }
414cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        // TODO: Handle more than one keyphrase extras.
415cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        if (mKeyphraseId != keyphraseExtras[0].id) {
416cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            Slog.w(TAG, "received onRecognition event for a different keyphrase");
417cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            return;
418cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        }
419cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha
420cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        try {
421cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            if (mActiveListener != null) {
422cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                mActiveListener.onDetected((KeyphraseRecognitionEvent) event);
423cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            }
424cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        } catch (RemoteException e) {
425cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            Slog.w(TAG, "RemoteException in onDetected", e);
426cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        }
427cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha
428cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        mStarted = false;
429cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        mRequested = mRecognitionConfig.allowMultipleTriggers;
430cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        // TODO: Remove this block if the lower layer supports multiple triggers.
431cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        if (mRequested) {
432cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            updateRecognitionLocked(true /* notify */);
433cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        }
434cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    }
435cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha
436cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    private void onServiceDiedLocked() {
437cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        try {
438cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            if (mActiveListener != null) {
439cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                mActiveListener.onError(SoundTrigger.STATUS_DEAD_OBJECT);
440cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            }
441cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        } catch (RemoteException e) {
442cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            Slog.w(TAG, "RemoteException in onError", e);
443cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        } finally {
44445c00b5877e908f44853783b42deb437cfd30d94Sandeep Siddhartha            internalClearSoundModelLocked();
445cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            internalClearStateLocked();
4462475e38c10a02973665752e0b829153a5c493b28Eric Laurent            if (mModule != null) {
4472475e38c10a02973665752e0b829153a5c493b28Eric Laurent                mModule.detach();
4482475e38c10a02973665752e0b829153a5c493b28Eric Laurent                mModule = null;
4492475e38c10a02973665752e0b829153a5c493b28Eric Laurent            }
450cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        }
451cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    }
452cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha
453cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    private int updateRecognitionLocked(boolean notify) {
454cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        if (mModule == null || moduleProperties == null
455cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                || mCurrentSoundModelHandle == INVALID_VALUE || mActiveListener == null) {
456cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            // Nothing to do here.
457cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            return STATUS_OK;
458cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        }
459cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha
460efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha        boolean start = mRequested && !mCallActive && !mServiceDisabled && !mIsPowerSaveMode;
461cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        if (start == mStarted) {
462cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            // No-op.
463cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            return STATUS_OK;
464cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        }
465cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha
466cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        // See if the recognition needs to be started.
467cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        if (start) {
468cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            // Start recognition.
469cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            int status = mModule.startRecognition(mCurrentSoundModelHandle, mRecognitionConfig);
470cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            if (status != SoundTrigger.STATUS_OK) {
471cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                Slog.w(TAG, "startRecognition failed with " + status);
472cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                // Notify of error if needed.
473cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                if (notify) {
474cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                    try {
475cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                        mActiveListener.onError(status);
476cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                    } catch (RemoteException e) {
477cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                        Slog.w(TAG, "RemoteException in onError", e);
478cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                    }
479cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                }
480cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            } else {
481cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                mStarted = true;
482cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                // Notify of resume if needed.
483cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                if (notify) {
484cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                    try {
485cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                        mActiveListener.onRecognitionResumed();
486cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                    } catch (RemoteException e) {
487cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                        Slog.w(TAG, "RemoteException in onRecognitionResumed", e);
488cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                    }
489cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                }
490cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            }
491cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            return status;
492cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        } else {
493cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            // Stop recognition.
494cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            int status = mModule.stopRecognition(mCurrentSoundModelHandle);
495cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            if (status != SoundTrigger.STATUS_OK) {
496cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                Slog.w(TAG, "stopRecognition call failed with " + status);
497cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                if (notify) {
498cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                    try {
499cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                        mActiveListener.onError(status);
500cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                    } catch (RemoteException e) {
501cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                        Slog.w(TAG, "RemoteException in onError", e);
502cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                    }
503cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                }
504cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            } else {
505cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                mStarted = false;
506cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                // Notify of pause if needed.
507cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                if (notify) {
508cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                    try {
509cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                        mActiveListener.onRecognitionPaused();
510cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                    } catch (RemoteException e) {
511cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                        Slog.w(TAG, "RemoteException in onRecognitionPaused", e);
512cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha                    }
5136817337118655d5792e36e954b123e6daa4174a6Sandeep Siddhartha                }
5146817337118655d5792e36e954b123e6daa4174a6Sandeep Siddhartha            }
515cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha            return status;
5166817337118655d5792e36e954b123e6daa4174a6Sandeep Siddhartha        }
517d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep    }
518cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha
519cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    private void internalClearStateLocked() {
520cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        mStarted = false;
521cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        mRequested = false;
522cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha
523cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        mKeyphraseId = INVALID_VALUE;
524cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        mRecognitionConfig = null;
525cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        mActiveListener = null;
526cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha
527cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        // Unregister from call state changes.
528cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha        mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
529efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha
530efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha        // Unregister from power save mode changes.
531efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha        if (mPowerSaveModeListener != null) {
532efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha            mContext.unregisterReceiver(mPowerSaveModeListener);
533efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha            mPowerSaveModeListener = null;
534efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha        }
535efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha    }
536efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha
53745c00b5877e908f44853783b42deb437cfd30d94Sandeep Siddhartha    private void internalClearSoundModelLocked() {
53845c00b5877e908f44853783b42deb437cfd30d94Sandeep Siddhartha        mCurrentSoundModelHandle = INVALID_VALUE;
53945c00b5877e908f44853783b42deb437cfd30d94Sandeep Siddhartha        mCurrentSoundModel = null;
54045c00b5877e908f44853783b42deb437cfd30d94Sandeep Siddhartha    }
54145c00b5877e908f44853783b42deb437cfd30d94Sandeep Siddhartha
542efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha    class MyCallStateListener extends PhoneStateListener {
543efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha        @Override
544efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha        public void onCallStateChanged(int state, String arg1) {
545efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha            if (DBG) Slog.d(TAG, "onCallStateChanged: " + state);
546efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha            synchronized (mLock) {
547efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha                onCallStateChangedLocked(TelephonyManager.CALL_STATE_IDLE != state);
548efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha            }
549efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha        }
550efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha    }
551efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha
552efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha    class PowerSaveModeListener extends BroadcastReceiver {
553efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha        @Override
554efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha        public void onReceive(Context context, Intent intent) {
555efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha            if (!PowerManager.ACTION_POWER_SAVE_MODE_CHANGED.equals(intent.getAction())) {
556efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha                return;
557efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha            }
558efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha            boolean active = mPowerManager.isPowerSaveMode();
559efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha            if (DBG) Slog.d(TAG, "onPowerSaveModeChanged: " + active);
560efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha            synchronized (mLock) {
561efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha                onPowerSaveModeChangedLocked(active);
562efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha            }
563efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha        }
564cb4e81c7fe1ec843d80f7604a688c71086c23685Sandeep Siddhartha    }
5656b8556d2e320b631d3741bf064796efddb6e51dfSandeep Siddhartha
5666b8556d2e320b631d3741bf064796efddb6e51dfSandeep Siddhartha    void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
5676b8556d2e320b631d3741bf064796efddb6e51dfSandeep Siddhartha        synchronized (mLock) {
5686b8556d2e320b631d3741bf064796efddb6e51dfSandeep Siddhartha            pw.print("  module properties=");
5696b8556d2e320b631d3741bf064796efddb6e51dfSandeep Siddhartha            pw.println(moduleProperties == null ? "null" : moduleProperties);
5706b8556d2e320b631d3741bf064796efddb6e51dfSandeep Siddhartha            pw.print("  keyphrase ID="); pw.println(mKeyphraseId);
5716b8556d2e320b631d3741bf064796efddb6e51dfSandeep Siddhartha            pw.print("  sound model handle="); pw.println(mCurrentSoundModelHandle);
5726b8556d2e320b631d3741bf064796efddb6e51dfSandeep Siddhartha            pw.print("  sound model UUID=");
57345c00b5877e908f44853783b42deb437cfd30d94Sandeep Siddhartha            pw.println(mCurrentSoundModel == null ? "null" : mCurrentSoundModel.uuid);
5746b8556d2e320b631d3741bf064796efddb6e51dfSandeep Siddhartha            pw.print("  current listener=");
5756b8556d2e320b631d3741bf064796efddb6e51dfSandeep Siddhartha            pw.println(mActiveListener == null ? "null" : mActiveListener.asBinder());
5766b8556d2e320b631d3741bf064796efddb6e51dfSandeep Siddhartha
5776b8556d2e320b631d3741bf064796efddb6e51dfSandeep Siddhartha            pw.print("  requested="); pw.println(mRequested);
5786b8556d2e320b631d3741bf064796efddb6e51dfSandeep Siddhartha            pw.print("  started="); pw.println(mStarted);
5796b8556d2e320b631d3741bf064796efddb6e51dfSandeep Siddhartha            pw.print("  call active="); pw.println(mCallActive);
580efe0f9c7f2bdc10cfd28c186e68676e27b6944a1Sandeep Siddhartha            pw.print("  power save mode active="); pw.println(mIsPowerSaveMode);
5816b8556d2e320b631d3741bf064796efddb6e51dfSandeep Siddhartha            pw.print("  service disabled="); pw.println(mServiceDisabled);
5826b8556d2e320b631d3741bf064796efddb6e51dfSandeep Siddhartha        }
5836b8556d2e320b631d3741bf064796efddb6e51dfSandeep Siddhartha    }
584d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep}
585