191097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn/**
291097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn * Copyright (C) 2014 The Android Open Source Project
391097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn *
491097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn * Licensed under the Apache License, Version 2.0 (the "License");
591097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn * you may not use this file except in compliance with the License.
691097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn * You may obtain a copy of the License at
791097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn *
891097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn *      http://www.apache.org/licenses/LICENSE-2.0
991097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn *
1091097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn * Unless required by applicable law or agreed to in writing, software
1191097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn * distributed under the License is distributed on an "AS IS" BASIS,
1291097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1391097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn * See the License for the specific language governing permissions and
1491097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn * limitations under the License.
1591097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn */
1691097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn
1791097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackbornpackage android.service.voice;
1891097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn
1991097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackbornimport android.annotation.SdkConstant;
2091097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackbornimport android.app.Service;
21fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackbornimport android.content.ComponentName;
2291097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackbornimport android.content.Context;
2391097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackbornimport android.content.Intent;
24d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeepimport android.hardware.soundtrigger.KeyphraseEnrollmentInfo;
2518f0d357f9693fe787a3e3777d8fdf01357a6e3fDianne Hackbornimport android.os.Bundle;
26fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackbornimport android.os.Handler;
2791097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackbornimport android.os.IBinder;
28fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackbornimport android.os.Message;
2991097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackbornimport android.os.RemoteException;
3091097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackbornimport android.os.ServiceManager;
31fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackbornimport android.provider.Settings;
326daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha
3322968950b814e66a6aa119ea92ae648884cbe0d9Sandeep Siddharthaimport com.android.internal.annotations.VisibleForTesting;
3491097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackbornimport com.android.internal.app.IVoiceInteractionManagerService;
3591097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn
366df952ec2208714d3206c54987eb388aee799be6Sandeep Siddharthaimport java.io.FileDescriptor;
376df952ec2208714d3206c54987eb388aee799be6Sandeep Siddharthaimport java.io.PrintWriter;
38dcf3068fcb55f101680e70a8a6f84f3b2c9cb1e3Sandeep Siddharthaimport java.util.Locale;
396df952ec2208714d3206c54987eb388aee799be6Sandeep Siddhartha
40f7a13df899c30ddddbbf63274bc28174f6391f29Sandeep Siddhartha
41c03c9167c2d9a1e22fb2b176b00a0524177fb037Dianne Hackborn/**
42c03c9167c2d9a1e22fb2b176b00a0524177fb037Dianne Hackborn * Top-level service of the current global voice interactor, which is providing
43d7c0395d26694c594c3e64b16ab647c971aeb422Dianne Hackborn * support for hotwording etc.
44c03c9167c2d9a1e22fb2b176b00a0524177fb037Dianne Hackborn * The current VoiceInteractionService that has been selected by the user is kept
45c03c9167c2d9a1e22fb2b176b00a0524177fb037Dianne Hackborn * always running by the system, to allow it to do things like listen for hotwords
46d7c0395d26694c594c3e64b16ab647c971aeb422Dianne Hackborn * in the background.
47c03c9167c2d9a1e22fb2b176b00a0524177fb037Dianne Hackborn *
48c03c9167c2d9a1e22fb2b176b00a0524177fb037Dianne Hackborn * <p>Because this service is always running, it should be kept as lightweight as
49c03c9167c2d9a1e22fb2b176b00a0524177fb037Dianne Hackborn * possible.  Heavy-weight operations (including showing UI) should be implemented
50d7c0395d26694c594c3e64b16ab647c971aeb422Dianne Hackborn * in the associated {@link android.service.voice.VoiceInteractionSessionService}
51d7c0395d26694c594c3e64b16ab647c971aeb422Dianne Hackborn * that only runs while the operation is active.
52c03c9167c2d9a1e22fb2b176b00a0524177fb037Dianne Hackborn */
5391097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackbornpublic class VoiceInteractionService extends Service {
5491097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn    /**
5591097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn     * The {@link Intent} that must be declared as handled by the service.
5691097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn     * To be supported, the service must also require the
5791097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn     * {@link android.Manifest.permission#BIND_VOICE_INTERACTION} permission so
5891097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn     * that other applications can not abuse it.
5991097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn     */
6091097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn    @SdkConstant(SdkConstant.SdkConstantType.SERVICE_ACTION)
6191097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn    public static final String SERVICE_INTERFACE =
6291097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn            "android.service.voice.VoiceInteractionService";
6391097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn
6491097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn    /**
6591097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn     * Name under which a VoiceInteractionService component publishes information about itself.
6691097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn     * This meta-data should reference an XML resource containing a
6791097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn     * <code>&lt;{@link
6891097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn     * android.R.styleable#VoiceInteractionService voice-interaction-service}&gt;</code> tag.
6991097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn     */
7091097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn    public static final String SERVICE_META_DATA = "android.voice_interaction";
7191097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn
7291097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn    IVoiceInteractionService mInterface = new IVoiceInteractionService.Stub() {
73fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn        @Override public void ready() {
74fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn            mHandler.sendEmptyMessage(MSG_READY);
75fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn        }
766daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha        @Override public void shutdown() {
776daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha            mHandler.sendEmptyMessage(MSG_SHUTDOWN);
786daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha        }
796daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha        @Override public void soundModelsChanged() {
806daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha            mHandler.sendEmptyMessage(MSG_SOUND_MODELS_CHANGED);
816daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha        }
8291097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn    };
8391097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn
84fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn    MyHandler mHandler;
85fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn
8691097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn    IVoiceInteractionManagerService mSystemService;
8791097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn
886daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha    private final Object mLock = new Object();
896daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha
90e912ac012e7146e9b0e8589bd9d88790e55372e3Sandeep Siddhartha    private KeyphraseEnrollmentInfo mKeyphraseEnrollmentInfo;
91e912ac012e7146e9b0e8589bd9d88790e55372e3Sandeep Siddhartha
926daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha    private AlwaysOnHotwordDetector mHotwordDetector;
936daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha
94fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn    static final int MSG_READY = 1;
956daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha    static final int MSG_SHUTDOWN = 2;
966daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha    static final int MSG_SOUND_MODELS_CHANGED = 3;
97fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn
98fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn    class MyHandler extends Handler {
99fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn        @Override
100fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn        public void handleMessage(Message msg) {
101fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn            switch (msg.what) {
102fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn                case MSG_READY:
103fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn                    onReady();
104fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn                    break;
1056daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha                case MSG_SHUTDOWN:
1066daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha                    onShutdownInternal();
1076daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha                    break;
1086daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha                case MSG_SOUND_MODELS_CHANGED:
1096daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha                    onSoundModelsChangedInternal();
1106daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha                    break;
111fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn                default:
112fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn                    super.handleMessage(msg);
113fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn            }
114fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn        }
115fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn    }
116fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn
117fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn    /**
118fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn     * Check whether the given service component is the currently active
119fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn     * VoiceInteractionService.
120fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn     */
121fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn    public static boolean isActiveService(Context context, ComponentName service) {
122fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn        String cur = Settings.Secure.getString(context.getContentResolver(),
123fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn                Settings.Secure.VOICE_INTERACTION_SERVICE);
124fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn        if (cur == null || cur.isEmpty()) {
125fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn            return false;
126fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn        }
127fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn        ComponentName curComp = ComponentName.unflattenFromString(cur);
128fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn        if (curComp == null) {
129fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn            return false;
130fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn        }
131e2c020a449cf1de10230d2ac31a083f342aa75c8Barnaby James        return curComp.equals(service);
132fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn    }
133fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn
134fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn    /**
135fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn     * Initiate the execution of a new {@link android.service.voice.VoiceInteractionSession}.
136fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn     * @param args Arbitrary arguments that will be propagated to the session.
137fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn     */
138c03c9167c2d9a1e22fb2b176b00a0524177fb037Dianne Hackborn    public void startSession(Bundle args) {
139fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn        if (mSystemService == null) {
140fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn            throw new IllegalStateException("Not available until onReady() is called");
141fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn        }
14291097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn        try {
143c03c9167c2d9a1e22fb2b176b00a0524177fb037Dianne Hackborn            mSystemService.startSession(mInterface, args);
14491097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn        } catch (RemoteException e) {
14591097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn        }
14691097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn    }
14791097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn
14891097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn    @Override
14991097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn    public void onCreate() {
15091097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn        super.onCreate();
151fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn        mHandler = new MyHandler();
15291097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn    }
15391097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn
15491097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn    @Override
15591097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn    public IBinder onBind(Intent intent) {
15691097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn        if (SERVICE_INTERFACE.equals(intent.getAction())) {
15791097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn            return mInterface.asBinder();
15891097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn        }
15991097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn        return null;
16091097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn    }
161e912ac012e7146e9b0e8589bd9d88790e55372e3Sandeep Siddhartha
162e912ac012e7146e9b0e8589bd9d88790e55372e3Sandeep Siddhartha    /**
163fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn     * Called during service initialization to tell you when the system is ready
1646daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha     * to receive interaction from it. You should generally do initialization here
1656daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha     * rather than in {@link #onCreate()}. Methods such as {@link #startSession(Bundle)} and
166dcf3068fcb55f101680e70a8a6f84f3b2c9cb1e3Sandeep Siddhartha     * {@link #createAlwaysOnHotwordDetector(String, Locale, android.service.voice.AlwaysOnHotwordDetector.Callback)}
1676daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha     * will not be operational until this point.
168fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn     */
169fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn    public void onReady() {
170fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn        mSystemService = IVoiceInteractionManagerService.Stub.asInterface(
171fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn                ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
172fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn        mKeyphraseEnrollmentInfo = new KeyphraseEnrollmentInfo(getPackageManager());
173fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn    }
174fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn
1756daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha    private void onShutdownInternal() {
1766daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha        onShutdown();
1776daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha        // Stop any active recognitions when shutting down.
1786daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha        // This ensures that if implementations forget to stop any active recognition,
1796daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha        // It's still guaranteed to have been stopped.
1806daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha        // This helps with cases where the voice interaction implementation is changed
1816daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha        // by the user.
1826daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha        safelyShutdownHotwordDetector();
1836daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha    }
1846daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha
1856daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha    /**
1866daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha     * Called during service de-initialization to tell you when the system is shutting the
1876daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha     * service down.
1885e33fb057c20b84418d96574abe861e9d05956ebSandeep Siddhartha     * At this point this service may no longer be the active {@link VoiceInteractionService}.
1896daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha     */
1906daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha    public void onShutdown() {
1916daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha    }
1926daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha
1936daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha    private void onSoundModelsChangedInternal() {
1946daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha        synchronized (this) {
1956daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha            if (mHotwordDetector != null) {
1966daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha                // TODO: Stop recognition if a sound model that was being recognized gets deleted.
1976daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha                mHotwordDetector.onSoundModelsChanged();
1986daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha            }
1996daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha        }
2006daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha    }
2016daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha
202fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn    /**
2036daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha     * Creates an {@link AlwaysOnHotwordDetector} for the given keyphrase and locale.
2046daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha     * This instance must be retained and used by the client.
2056daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha     * Calling this a second time invalidates the previously created hotword detector
2066daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha     * which can no longer be used to manage recognition.
2076daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha     *
208d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep     * @param keyphrase The keyphrase that's being used, for example "Hello Android".
209d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep     * @param locale The locale for which the enrollment needs to be performed.
210d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep     * @param callback The callback to notify of detection events.
211d7018200312e4e4dc3f67cf33dc90bf7ce585844Sandeep     * @return An always-on hotword detector for the given keyphrase and locale.
212e912ac012e7146e9b0e8589bd9d88790e55372e3Sandeep Siddhartha     */
2136daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha    public final AlwaysOnHotwordDetector createAlwaysOnHotwordDetector(
214dcf3068fcb55f101680e70a8a6f84f3b2c9cb1e3Sandeep Siddhartha            String keyphrase, Locale locale, AlwaysOnHotwordDetector.Callback callback) {
215fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn        if (mSystemService == null) {
216fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn            throw new IllegalStateException("Not available until onReady() is called");
217fee756ff91ab4d8f0e09ddb050d22d88ebb66ae7Dianne Hackborn        }
2186daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha        synchronized (mLock) {
2196daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha            // Allow only one concurrent recognition via the APIs.
2206daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha            safelyShutdownHotwordDetector();
2216daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha            mHotwordDetector = new AlwaysOnHotwordDetector(keyphrase, locale, callback,
2226daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha                    mKeyphraseEnrollmentInfo, mInterface, mSystemService);
2236daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha        }
2246daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha        return mHotwordDetector;
225e912ac012e7146e9b0e8589bd9d88790e55372e3Sandeep Siddhartha    }
22622968950b814e66a6aa119ea92ae648884cbe0d9Sandeep Siddhartha
22722968950b814e66a6aa119ea92ae648884cbe0d9Sandeep Siddhartha    /**
22822968950b814e66a6aa119ea92ae648884cbe0d9Sandeep Siddhartha     * @return Details of keyphrases available for enrollment.
22922968950b814e66a6aa119ea92ae648884cbe0d9Sandeep Siddhartha     * @hide
23022968950b814e66a6aa119ea92ae648884cbe0d9Sandeep Siddhartha     */
23122968950b814e66a6aa119ea92ae648884cbe0d9Sandeep Siddhartha    @VisibleForTesting
23222968950b814e66a6aa119ea92ae648884cbe0d9Sandeep Siddhartha    protected final KeyphraseEnrollmentInfo getKeyphraseEnrollmentInfo() {
23322968950b814e66a6aa119ea92ae648884cbe0d9Sandeep Siddhartha        return mKeyphraseEnrollmentInfo;
23422968950b814e66a6aa119ea92ae648884cbe0d9Sandeep Siddhartha    }
2356daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha
2366daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha    private void safelyShutdownHotwordDetector() {
2376daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha        try {
2386daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha            synchronized (mLock) {
2396daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha                if (mHotwordDetector != null) {
2406daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha                    mHotwordDetector.stopRecognition();
2416daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha                    mHotwordDetector.invalidate();
2426daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha                    mHotwordDetector = null;
2436daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha                }
2446daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha            }
2456daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha        } catch (Exception ex) {
2466daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha            // Ignore.
2476daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha        }
2486daae9622672e0b38fc2efed29f68061d749caccSandeep Siddhartha    }
2496df952ec2208714d3206c54987eb388aee799be6Sandeep Siddhartha
2506df952ec2208714d3206c54987eb388aee799be6Sandeep Siddhartha    @Override
2516df952ec2208714d3206c54987eb388aee799be6Sandeep Siddhartha    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2526df952ec2208714d3206c54987eb388aee799be6Sandeep Siddhartha        pw.println("VOICE INTERACTION");
2536df952ec2208714d3206c54987eb388aee799be6Sandeep Siddhartha        synchronized (mLock) {
2546df952ec2208714d3206c54987eb388aee799be6Sandeep Siddhartha            pw.println("  AlwaysOnHotwordDetector");
2556df952ec2208714d3206c54987eb388aee799be6Sandeep Siddhartha            if (mHotwordDetector == null) {
2566df952ec2208714d3206c54987eb388aee799be6Sandeep Siddhartha                pw.println("    NULL");
2576df952ec2208714d3206c54987eb388aee799be6Sandeep Siddhartha            } else {
2586df952ec2208714d3206c54987eb388aee799be6Sandeep Siddhartha                mHotwordDetector.dump("    ", pw);
2596df952ec2208714d3206c54987eb388aee799be6Sandeep Siddhartha            }
2606df952ec2208714d3206c54987eb388aee799be6Sandeep Siddhartha        }
2616df952ec2208714d3206c54987eb388aee799be6Sandeep Siddhartha    }
26291097de49b0f683b00e26a75dbc0ac6082344137Dianne Hackborn}
263