1/**
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.service.voice;
18
19import android.annotation.Nullable;
20import android.app.Activity;
21import android.app.Dialog;
22import android.app.Instrumentation;
23import android.app.VoiceInteractor;
24import android.app.assist.AssistContent;
25import android.app.assist.AssistStructure;
26import android.content.ComponentCallbacks2;
27import android.content.Context;
28import android.content.Intent;
29import android.content.res.Configuration;
30import android.content.res.TypedArray;
31import android.graphics.Bitmap;
32import android.graphics.Rect;
33import android.graphics.Region;
34import android.inputmethodservice.SoftInputWindow;
35import android.os.Binder;
36import android.os.Bundle;
37import android.os.Handler;
38import android.os.IBinder;
39import android.os.Message;
40import android.os.RemoteException;
41import android.os.UserHandle;
42import android.util.ArrayMap;
43import android.util.DebugUtils;
44import android.util.Log;
45import android.view.Gravity;
46import android.view.KeyEvent;
47import android.view.LayoutInflater;
48import android.view.View;
49import android.view.ViewGroup;
50import android.view.ViewTreeObserver;
51import android.view.WindowManager;
52import android.widget.FrameLayout;
53
54import com.android.internal.app.IVoiceInteractionManagerService;
55import com.android.internal.app.IVoiceInteractionSessionShowCallback;
56import com.android.internal.app.IVoiceInteractor;
57import com.android.internal.app.IVoiceInteractorCallback;
58import com.android.internal.app.IVoiceInteractorRequest;
59import com.android.internal.os.HandlerCaller;
60import com.android.internal.os.SomeArgs;
61
62import java.io.FileDescriptor;
63import java.io.PrintWriter;
64import java.lang.ref.WeakReference;
65
66import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
67
68/**
69 * An active voice interaction session, providing a facility for the implementation
70 * to interact with the user in the voice interaction layer.  The user interface is
71 * initially shown by default, and can be created be overriding {@link #onCreateContentView()}
72 * in which the UI can be built.
73 *
74 * <p>A voice interaction session can be self-contained, ultimately calling {@link #finish}
75 * when done.  It can also initiate voice interactions with applications by calling
76 * {@link #startVoiceActivity}</p>.
77 */
78public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCallbacks2 {
79    static final String TAG = "VoiceInteractionSession";
80    static final boolean DEBUG = false;
81
82    /**
83     * Flag received in {@link #onShow}: originator requested that the session be started with
84     * assist data from the currently focused activity.
85     */
86    public static final int SHOW_WITH_ASSIST = 1<<0;
87
88    /**
89     * Flag received in {@link #onShow}: originator requested that the session be started with
90     * a screen shot of the currently focused activity.
91     */
92    public static final int SHOW_WITH_SCREENSHOT = 1<<1;
93
94    /**
95     * Flag for use with {@link #onShow}: indicates that the session has been started from the
96     * system assist gesture.
97     */
98    public static final int SHOW_SOURCE_ASSIST_GESTURE = 1<<2;
99
100    /**
101     * Flag for use with {@link #onShow}: indicates that the application itself has invoked
102     * the assistant.
103     */
104    public static final int SHOW_SOURCE_APPLICATION = 1<<3;
105
106    /**
107     * Flag for use with {@link #onShow}: indicates that an Activity has invoked the voice
108     * interaction service for a local interaction using
109     * {@link Activity#startLocalVoiceInteraction(Bundle)}.
110     */
111    public static final int SHOW_SOURCE_ACTIVITY = 1<<4;
112
113    // Keys for Bundle values
114    /** @hide */
115    public static final String KEY_DATA = "data";
116    /** @hide */
117    public static final String KEY_STRUCTURE = "structure";
118    /** @hide */
119    public static final String KEY_CONTENT = "content";
120    /** @hide */
121    public static final String KEY_RECEIVER_EXTRAS = "receiverExtras";
122
123    final Context mContext;
124    final HandlerCaller mHandlerCaller;
125
126    final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState();
127
128    IVoiceInteractionManagerService mSystemService;
129    IBinder mToken;
130
131    int mTheme = 0;
132    LayoutInflater mInflater;
133    TypedArray mThemeAttrs;
134    View mRootView;
135    FrameLayout mContentFrame;
136    SoftInputWindow mWindow;
137
138    boolean mUiEnabled = true;
139    boolean mInitialized;
140    boolean mWindowAdded;
141    boolean mWindowVisible;
142    boolean mWindowWasVisible;
143    boolean mInShowWindow;
144
145    final ArrayMap<IBinder, Request> mActiveRequests = new ArrayMap<IBinder, Request>();
146
147    final Insets mTmpInsets = new Insets();
148
149    final WeakReference<VoiceInteractionSession> mWeakRef
150            = new WeakReference<VoiceInteractionSession>(this);
151
152    final IVoiceInteractor mInteractor = new IVoiceInteractor.Stub() {
153        @Override
154        public IVoiceInteractorRequest startConfirmation(String callingPackage,
155                IVoiceInteractorCallback callback, VoiceInteractor.Prompt prompt, Bundle extras) {
156            ConfirmationRequest request = new ConfirmationRequest(callingPackage,
157                    Binder.getCallingUid(), callback, VoiceInteractionSession.this,
158                    prompt, extras);
159            addRequest(request);
160            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_CONFIRMATION,
161                    request));
162            return request.mInterface;
163        }
164
165        @Override
166        public IVoiceInteractorRequest startPickOption(String callingPackage,
167                IVoiceInteractorCallback callback, VoiceInteractor.Prompt prompt,
168                VoiceInteractor.PickOptionRequest.Option[] options, Bundle extras) {
169            PickOptionRequest request = new PickOptionRequest(callingPackage,
170                    Binder.getCallingUid(), callback, VoiceInteractionSession.this,
171                    prompt, options, extras);
172            addRequest(request);
173            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_PICK_OPTION,
174                    request));
175            return request.mInterface;
176        }
177
178        @Override
179        public IVoiceInteractorRequest startCompleteVoice(String callingPackage,
180                IVoiceInteractorCallback callback, VoiceInteractor.Prompt message, Bundle extras) {
181            CompleteVoiceRequest request = new CompleteVoiceRequest(callingPackage,
182                    Binder.getCallingUid(), callback, VoiceInteractionSession.this,
183                    message, extras);
184            addRequest(request);
185            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_COMPLETE_VOICE,
186                    request));
187            return request.mInterface;
188        }
189
190        @Override
191        public IVoiceInteractorRequest startAbortVoice(String callingPackage,
192                IVoiceInteractorCallback callback, VoiceInteractor.Prompt message, Bundle extras) {
193            AbortVoiceRequest request = new AbortVoiceRequest(callingPackage,
194                    Binder.getCallingUid(), callback, VoiceInteractionSession.this,
195                    message, extras);
196            addRequest(request);
197            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_ABORT_VOICE,
198                    request));
199            return request.mInterface;
200        }
201
202        @Override
203        public IVoiceInteractorRequest startCommand(String callingPackage,
204                IVoiceInteractorCallback callback, String command, Bundle extras) {
205            CommandRequest request = new CommandRequest(callingPackage,
206                    Binder.getCallingUid(), callback, VoiceInteractionSession.this,
207                    command, extras);
208            addRequest(request);
209            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_COMMAND,
210                    request));
211            return request.mInterface;
212        }
213
214        @Override
215        public boolean[] supportsCommands(String callingPackage, String[] commands) {
216            Message msg = mHandlerCaller.obtainMessageIOO(MSG_SUPPORTS_COMMANDS,
217                    0, commands, null);
218            SomeArgs args = mHandlerCaller.sendMessageAndWait(msg);
219            if (args != null) {
220                boolean[] res = (boolean[])args.arg1;
221                args.recycle();
222                return res;
223            }
224            return new boolean[commands.length];
225        }
226    };
227
228    final IVoiceInteractionSession mSession = new IVoiceInteractionSession.Stub() {
229        @Override
230        public void show(Bundle sessionArgs, int flags,
231                IVoiceInteractionSessionShowCallback showCallback) {
232            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOO(MSG_SHOW,
233                    flags, sessionArgs, showCallback));
234        }
235
236        @Override
237        public void hide() {
238            // Remove any pending messages to show the session
239            mHandlerCaller.removeMessages(MSG_SHOW);
240            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_HIDE));
241        }
242
243        @Override
244        public void handleAssist(final Bundle data, final AssistStructure structure,
245                final AssistContent content, final int index, final int count) {
246            // We want to pre-warm the AssistStructure before handing it off to the main
247            // thread.  We also want to do this on a separate thread, so that if the app
248            // is for some reason slow (due to slow filling in of async children in the
249            // structure), we don't block other incoming IPCs (such as the screenshot) to
250            // us (since we are a oneway interface, they get serialized).  (Okay?)
251            Thread retriever = new Thread("AssistStructure retriever") {
252                @Override
253                public void run() {
254                    Throwable failure = null;
255                    if (structure != null) {
256                        try {
257                            structure.ensureData();
258                        } catch (Throwable e) {
259                            Log.w(TAG, "Failure retrieving AssistStructure", e);
260                            failure = e;
261                        }
262                    }
263                    mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOOII(MSG_HANDLE_ASSIST,
264                            data, failure == null ? structure : null, failure, content,
265                            index, count));
266                }
267            };
268            retriever.start();
269        }
270
271        @Override
272        public void handleScreenshot(Bitmap screenshot) {
273            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_HANDLE_SCREENSHOT,
274                    screenshot));
275        }
276
277        @Override
278        public void taskStarted(Intent intent, int taskId) {
279            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIO(MSG_TASK_STARTED,
280                    taskId, intent));
281        }
282
283        @Override
284        public void taskFinished(Intent intent, int taskId) {
285            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIO(MSG_TASK_FINISHED,
286                    taskId, intent));
287        }
288
289        @Override
290        public void closeSystemDialogs() {
291            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_CLOSE_SYSTEM_DIALOGS));
292        }
293
294        @Override
295        public void onLockscreenShown() {
296            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_ON_LOCKSCREEN_SHOWN));
297        }
298
299        @Override
300        public void destroy() {
301            mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_DESTROY));
302        }
303    };
304
305    /**
306     * Base class representing a request from a voice-driver app to perform a particular
307     * voice operation with the user.  See related subclasses for the types of requests
308     * that are possible.
309     */
310    public static class Request {
311        final IVoiceInteractorRequest mInterface = new IVoiceInteractorRequest.Stub() {
312            @Override
313            public void cancel() throws RemoteException {
314                VoiceInteractionSession session = mSession.get();
315                if (session != null) {
316                    session.mHandlerCaller.sendMessage(
317                            session.mHandlerCaller.obtainMessageO(MSG_CANCEL, Request.this));
318                }
319            }
320        };
321        final String mCallingPackage;
322        final int mCallingUid;
323        final IVoiceInteractorCallback mCallback;
324        final WeakReference<VoiceInteractionSession> mSession;
325        final Bundle mExtras;
326
327        Request(String packageName, int uid, IVoiceInteractorCallback callback,
328                VoiceInteractionSession session, Bundle extras) {
329            mCallingPackage = packageName;
330            mCallingUid = uid;
331            mCallback = callback;
332            mSession = session.mWeakRef;
333            mExtras = extras;
334        }
335
336        /**
337         * Return the uid of the application that initiated the request.
338         */
339        public int getCallingUid() {
340            return mCallingUid;
341        }
342
343        /**
344         * Return the package name of the application that initiated the request.
345         */
346        public String getCallingPackage() {
347            return mCallingPackage;
348        }
349
350        /**
351         * Return any additional extra information that was supplied as part of the request.
352         */
353        public Bundle getExtras() {
354            return mExtras;
355        }
356
357        /**
358         * Check whether this request is currently active.  A request becomes inactive after
359         * calling {@link #cancel} or a final result method that completes the request.  After
360         * this point, further interactions with the request will result in
361         * {@link java.lang.IllegalStateException} errors; you should not catch these errors,
362         * but can use this method if you need to determine the state of the request.  Returns
363         * true if the request is still active.
364         */
365        public boolean isActive() {
366            VoiceInteractionSession session = mSession.get();
367            if (session == null) {
368                return false;
369            }
370            return session.isRequestActive(mInterface.asBinder());
371        }
372
373        void finishRequest() {
374            VoiceInteractionSession session = mSession.get();
375            if (session == null) {
376                throw new IllegalStateException("VoiceInteractionSession has been destroyed");
377            }
378            Request req = session.removeRequest(mInterface.asBinder());
379            if (req == null) {
380                throw new IllegalStateException("Request not active: " + this);
381            } else if (req != this) {
382                throw new IllegalStateException("Current active request " + req
383                        + " not same as calling request " + this);
384            }
385        }
386
387        /**
388         * Ask the app to cancel this current request.
389         * This also finishes the request (it is no longer active).
390         */
391        public void cancel() {
392            try {
393                if (DEBUG) Log.d(TAG, "sendCancelResult: req=" + mInterface);
394                finishRequest();
395                mCallback.deliverCancel(mInterface);
396            } catch (RemoteException e) {
397            }
398        }
399
400        @Override
401        public String toString() {
402            StringBuilder sb = new StringBuilder(128);
403            DebugUtils.buildShortClassTag(this, sb);
404            sb.append(" ");
405            sb.append(mInterface.asBinder());
406            sb.append(" pkg=");
407            sb.append(mCallingPackage);
408            sb.append(" uid=");
409            UserHandle.formatUid(sb, mCallingUid);
410            sb.append('}');
411            return sb.toString();
412        }
413
414        void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
415            writer.print(prefix); writer.print("mInterface=");
416            writer.println(mInterface.asBinder());
417            writer.print(prefix); writer.print("mCallingPackage="); writer.print(mCallingPackage);
418            writer.print(" mCallingUid="); UserHandle.formatUid(writer, mCallingUid);
419            writer.println();
420            writer.print(prefix); writer.print("mCallback=");
421            writer.println(mCallback.asBinder());
422            if (mExtras != null) {
423                writer.print(prefix); writer.print("mExtras=");
424                writer.println(mExtras);
425            }
426        }
427    }
428
429    /**
430     * A request for confirmation from the user of an operation, as per
431     * {@link android.app.VoiceInteractor.ConfirmationRequest
432     * VoiceInteractor.ConfirmationRequest}.
433     */
434    public static final class ConfirmationRequest extends Request {
435        final VoiceInteractor.Prompt mPrompt;
436
437        ConfirmationRequest(String packageName, int uid, IVoiceInteractorCallback callback,
438                VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras) {
439            super(packageName, uid, callback, session, extras);
440            mPrompt = prompt;
441        }
442
443        /**
444         * Return the prompt informing the user of what will happen, as per
445         * {@link android.app.VoiceInteractor.ConfirmationRequest
446         * VoiceInteractor.ConfirmationRequest}.
447         */
448        @Nullable
449        public VoiceInteractor.Prompt getVoicePrompt() {
450            return mPrompt;
451        }
452
453        /**
454         * Return the prompt informing the user of what will happen, as per
455         * {@link android.app.VoiceInteractor.ConfirmationRequest
456         * VoiceInteractor.ConfirmationRequest}.
457         * @deprecated Prefer {@link #getVoicePrompt()} which allows multiple voice prompts.
458         */
459        @Deprecated
460        @Nullable
461        public CharSequence getPrompt() {
462            return (mPrompt != null ? mPrompt.getVoicePromptAt(0) : null);
463        }
464
465        /**
466         * Report that the voice interactor has confirmed the operation with the user, resulting
467         * in a call to
468         * {@link android.app.VoiceInteractor.ConfirmationRequest#onConfirmationResult
469         * VoiceInteractor.ConfirmationRequest.onConfirmationResult}.
470         * This finishes the request (it is no longer active).
471         */
472        public void sendConfirmationResult(boolean confirmed, Bundle result) {
473            try {
474                if (DEBUG) Log.d(TAG, "sendConfirmationResult: req=" + mInterface
475                        + " confirmed=" + confirmed + " result=" + result);
476                finishRequest();
477                mCallback.deliverConfirmationResult(mInterface, confirmed, result);
478            } catch (RemoteException e) {
479            }
480        }
481
482        void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
483            super.dump(prefix, fd, writer, args);
484            writer.print(prefix); writer.print("mPrompt=");
485            writer.println(mPrompt);
486        }
487    }
488
489    /**
490     * A request for the user to pick from a set of option, as per
491     * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
492     */
493    public static final class PickOptionRequest extends Request {
494        final VoiceInteractor.Prompt mPrompt;
495        final VoiceInteractor.PickOptionRequest.Option[] mOptions;
496
497        PickOptionRequest(String packageName, int uid, IVoiceInteractorCallback callback,
498                VoiceInteractionSession session, VoiceInteractor.Prompt prompt,
499                VoiceInteractor.PickOptionRequest.Option[] options, Bundle extras) {
500            super(packageName, uid, callback, session, extras);
501            mPrompt = prompt;
502            mOptions = options;
503        }
504
505        /**
506         * Return the prompt informing the user of what they are picking, as per
507         * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
508         */
509        @Nullable
510        public VoiceInteractor.Prompt getVoicePrompt() {
511            return mPrompt;
512        }
513
514        /**
515         * Return the prompt informing the user of what they are picking, as per
516         * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
517         * @deprecated Prefer {@link #getVoicePrompt()} which allows multiple voice prompts.
518         */
519        @Deprecated
520        @Nullable
521        public CharSequence getPrompt() {
522            return (mPrompt != null ? mPrompt.getVoicePromptAt(0) : null);
523        }
524
525        /**
526         * Return the set of options the user is picking from, as per
527         * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
528         */
529        public VoiceInteractor.PickOptionRequest.Option[] getOptions() {
530            return mOptions;
531        }
532
533        void sendPickOptionResult(boolean finished,
534                VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) {
535            try {
536                if (DEBUG) Log.d(TAG, "sendPickOptionResult: req=" + mInterface
537                        + " finished=" + finished + " selections=" + selections
538                        + " result=" + result);
539                if (finished) {
540                    finishRequest();
541                }
542                mCallback.deliverPickOptionResult(mInterface, finished, selections, result);
543            } catch (RemoteException e) {
544            }
545        }
546
547        /**
548         * Report an intermediate option selection from the request, without completing it (the
549         * request is still active and the app is waiting for the final option selection),
550         * resulting in a call to
551         * {@link android.app.VoiceInteractor.PickOptionRequest#onPickOptionResult
552         * VoiceInteractor.PickOptionRequest.onPickOptionResult} with false for finished.
553         */
554        public void sendIntermediatePickOptionResult(
555                VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) {
556            sendPickOptionResult(false, selections, result);
557        }
558
559        /**
560         * Report the final option selection for the request, completing the request
561         * and resulting in a call to
562         * {@link android.app.VoiceInteractor.PickOptionRequest#onPickOptionResult
563         * VoiceInteractor.PickOptionRequest.onPickOptionResult} with false for finished.
564         * This finishes the request (it is no longer active).
565         */
566        public void sendPickOptionResult(
567                VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) {
568            sendPickOptionResult(true, selections, result);
569        }
570
571        void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
572            super.dump(prefix, fd, writer, args);
573            writer.print(prefix); writer.print("mPrompt=");
574            writer.println(mPrompt);
575            if (mOptions != null) {
576                writer.print(prefix); writer.println("Options:");
577                for (int i=0; i<mOptions.length; i++) {
578                    VoiceInteractor.PickOptionRequest.Option op = mOptions[i];
579                    writer.print(prefix); writer.print("  #"); writer.print(i); writer.println(":");
580                    writer.print(prefix); writer.print("    mLabel=");
581                    writer.println(op.getLabel());
582                    writer.print(prefix); writer.print("    mIndex=");
583                    writer.println(op.getIndex());
584                    if (op.countSynonyms() > 0) {
585                        writer.print(prefix); writer.println("    Synonyms:");
586                        for (int j=0; j<op.countSynonyms(); j++) {
587                            writer.print(prefix); writer.print("      #"); writer.print(j);
588                            writer.print(": "); writer.println(op.getSynonymAt(j));
589                        }
590                    }
591                    if (op.getExtras() != null) {
592                        writer.print(prefix); writer.print("    mExtras=");
593                        writer.println(op.getExtras());
594                    }
595                }
596            }
597        }
598    }
599
600    /**
601     * A request to simply inform the user that the voice operation has completed, as per
602     * {@link android.app.VoiceInteractor.CompleteVoiceRequest
603     * VoiceInteractor.CompleteVoiceRequest}.
604     */
605    public static final class CompleteVoiceRequest extends Request {
606        final VoiceInteractor.Prompt mPrompt;
607
608        CompleteVoiceRequest(String packageName, int uid, IVoiceInteractorCallback callback,
609                VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras) {
610            super(packageName, uid, callback, session, extras);
611            mPrompt = prompt;
612        }
613
614        /**
615         * Return the message informing the user of the completion, as per
616         * {@link android.app.VoiceInteractor.CompleteVoiceRequest
617         * VoiceInteractor.CompleteVoiceRequest}.
618         */
619        @Nullable
620        public VoiceInteractor.Prompt getVoicePrompt() {
621            return mPrompt;
622        }
623
624        /**
625         * Return the message informing the user of the completion, as per
626         * {@link android.app.VoiceInteractor.CompleteVoiceRequest
627         * VoiceInteractor.CompleteVoiceRequest}.
628         * @deprecated Prefer {@link #getVoicePrompt()} which allows a separate visual message.
629         */
630        @Deprecated
631        @Nullable
632        public CharSequence getMessage() {
633            return (mPrompt != null ? mPrompt.getVoicePromptAt(0) : null);
634        }
635
636        /**
637         * Report that the voice interactor has finished completing the voice operation, resulting
638         * in a call to
639         * {@link android.app.VoiceInteractor.CompleteVoiceRequest#onCompleteResult
640         * VoiceInteractor.CompleteVoiceRequest.onCompleteResult}.
641         * This finishes the request (it is no longer active).
642         */
643        public void sendCompleteResult(Bundle result) {
644            try {
645                if (DEBUG) Log.d(TAG, "sendCompleteVoiceResult: req=" + mInterface
646                        + " result=" + result);
647                finishRequest();
648                mCallback.deliverCompleteVoiceResult(mInterface, result);
649            } catch (RemoteException e) {
650            }
651        }
652
653        void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
654            super.dump(prefix, fd, writer, args);
655            writer.print(prefix); writer.print("mPrompt=");
656            writer.println(mPrompt);
657        }
658    }
659
660    /**
661     * A request to report that the current user interaction can not be completed with voice, as per
662     * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}.
663     */
664    public static final class AbortVoiceRequest extends Request {
665        final VoiceInteractor.Prompt mPrompt;
666
667        AbortVoiceRequest(String packageName, int uid, IVoiceInteractorCallback callback,
668                VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras) {
669            super(packageName, uid, callback, session, extras);
670            mPrompt = prompt;
671        }
672
673        /**
674         * Return the message informing the user of the problem, as per
675         * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}.
676         */
677        @Nullable
678        public VoiceInteractor.Prompt getVoicePrompt() {
679            return mPrompt;
680        }
681
682        /**
683         * Return the message informing the user of the problem, as per
684         * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}.
685         * @deprecated Prefer {@link #getVoicePrompt()} which allows a separate visual message.
686         */
687        @Deprecated
688        @Nullable
689        public CharSequence getMessage() {
690            return (mPrompt != null ? mPrompt.getVoicePromptAt(0) : null);
691        }
692
693        /**
694         * Report that the voice interactor has finished aborting the voice operation, resulting
695         * in a call to
696         * {@link android.app.VoiceInteractor.AbortVoiceRequest#onAbortResult
697         * VoiceInteractor.AbortVoiceRequest.onAbortResult}.  This finishes the request (it
698         * is no longer active).
699         */
700        public void sendAbortResult(Bundle result) {
701            try {
702                if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface
703                        + " result=" + result);
704                finishRequest();
705                mCallback.deliverAbortVoiceResult(mInterface, result);
706            } catch (RemoteException e) {
707            }
708        }
709
710        void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
711            super.dump(prefix, fd, writer, args);
712            writer.print(prefix); writer.print("mPrompt=");
713            writer.println(mPrompt);
714        }
715    }
716
717    /**
718     * A generic vendor-specific request, as per
719     * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}.
720     */
721    public static final class CommandRequest extends Request {
722        final String mCommand;
723
724        CommandRequest(String packageName, int uid, IVoiceInteractorCallback callback,
725                VoiceInteractionSession session, String command, Bundle extras) {
726            super(packageName, uid, callback, session, extras);
727            mCommand = command;
728        }
729
730        /**
731         * Return the command that is being executed, as per
732         * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}.
733         */
734        public String getCommand() {
735            return mCommand;
736        }
737
738        void sendCommandResult(boolean finished, Bundle result) {
739            try {
740                if (DEBUG) Log.d(TAG, "sendCommandResult: req=" + mInterface
741                        + " result=" + result);
742                if (finished) {
743                    finishRequest();
744                }
745                mCallback.deliverCommandResult(mInterface, finished, result);
746            } catch (RemoteException e) {
747            }
748        }
749
750        /**
751         * Report an intermediate result of the request, without completing it (the request
752         * is still active and the app is waiting for the final result), resulting in a call to
753         * {@link android.app.VoiceInteractor.CommandRequest#onCommandResult
754         * VoiceInteractor.CommandRequest.onCommandResult} with false for isCompleted.
755         */
756        public void sendIntermediateResult(Bundle result) {
757            sendCommandResult(false, result);
758        }
759
760        /**
761         * Report the final result of the request, completing the request and resulting in a call to
762         * {@link android.app.VoiceInteractor.CommandRequest#onCommandResult
763         * VoiceInteractor.CommandRequest.onCommandResult} with true for isCompleted.
764         * This finishes the request (it is no longer active).
765         */
766        public void sendResult(Bundle result) {
767            sendCommandResult(true, result);
768        }
769
770        void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
771            super.dump(prefix, fd, writer, args);
772            writer.print(prefix); writer.print("mCommand=");
773            writer.println(mCommand);
774        }
775    }
776
777    static final int MSG_START_CONFIRMATION = 1;
778    static final int MSG_START_PICK_OPTION = 2;
779    static final int MSG_START_COMPLETE_VOICE = 3;
780    static final int MSG_START_ABORT_VOICE = 4;
781    static final int MSG_START_COMMAND = 5;
782    static final int MSG_SUPPORTS_COMMANDS = 6;
783    static final int MSG_CANCEL = 7;
784
785    static final int MSG_TASK_STARTED = 100;
786    static final int MSG_TASK_FINISHED = 101;
787    static final int MSG_CLOSE_SYSTEM_DIALOGS = 102;
788    static final int MSG_DESTROY = 103;
789    static final int MSG_HANDLE_ASSIST = 104;
790    static final int MSG_HANDLE_SCREENSHOT = 105;
791    static final int MSG_SHOW = 106;
792    static final int MSG_HIDE = 107;
793    static final int MSG_ON_LOCKSCREEN_SHOWN = 108;
794
795    class MyCallbacks implements HandlerCaller.Callback, SoftInputWindow.Callback {
796        @Override
797        public void executeMessage(Message msg) {
798            SomeArgs args = null;
799            switch (msg.what) {
800                case MSG_START_CONFIRMATION:
801                    if (DEBUG) Log.d(TAG, "onConfirm: req=" + msg.obj);
802                    onRequestConfirmation((ConfirmationRequest) msg.obj);
803                    break;
804                case MSG_START_PICK_OPTION:
805                    if (DEBUG) Log.d(TAG, "onPickOption: req=" + msg.obj);
806                    onRequestPickOption((PickOptionRequest) msg.obj);
807                    break;
808                case MSG_START_COMPLETE_VOICE:
809                    if (DEBUG) Log.d(TAG, "onCompleteVoice: req=" + msg.obj);
810                    onRequestCompleteVoice((CompleteVoiceRequest) msg.obj);
811                    break;
812                case MSG_START_ABORT_VOICE:
813                    if (DEBUG) Log.d(TAG, "onAbortVoice: req=" + msg.obj);
814                    onRequestAbortVoice((AbortVoiceRequest) msg.obj);
815                    break;
816                case MSG_START_COMMAND:
817                    if (DEBUG) Log.d(TAG, "onCommand: req=" + msg.obj);
818                    onRequestCommand((CommandRequest) msg.obj);
819                    break;
820                case MSG_SUPPORTS_COMMANDS:
821                    args = (SomeArgs)msg.obj;
822                    if (DEBUG) Log.d(TAG, "onGetSupportedCommands: cmds=" + args.arg1);
823                    args.arg1 = onGetSupportedCommands((String[]) args.arg1);
824                    args.complete();
825                    args = null;
826                    break;
827                case MSG_CANCEL:
828                    if (DEBUG) Log.d(TAG, "onCancel: req=" + ((Request)msg.obj));
829                    onCancelRequest((Request) msg.obj);
830                    break;
831                case MSG_TASK_STARTED:
832                    if (DEBUG) Log.d(TAG, "onTaskStarted: intent=" + msg.obj
833                            + " taskId=" + msg.arg1);
834                    onTaskStarted((Intent) msg.obj, msg.arg1);
835                    break;
836                case MSG_TASK_FINISHED:
837                    if (DEBUG) Log.d(TAG, "onTaskFinished: intent=" + msg.obj
838                            + " taskId=" + msg.arg1);
839                    onTaskFinished((Intent) msg.obj, msg.arg1);
840                    break;
841                case MSG_CLOSE_SYSTEM_DIALOGS:
842                    if (DEBUG) Log.d(TAG, "onCloseSystemDialogs");
843                    onCloseSystemDialogs();
844                    break;
845                case MSG_DESTROY:
846                    if (DEBUG) Log.d(TAG, "doDestroy");
847                    doDestroy();
848                    break;
849                case MSG_HANDLE_ASSIST:
850                    args = (SomeArgs)msg.obj;
851                    if (DEBUG) Log.d(TAG, "onHandleAssist: data=" + args.arg1
852                            + " structure=" + args.arg2 + " content=" + args.arg3
853                            + " activityIndex=" + args.argi5 + " activityCount=" + args.argi6);
854                    if (args.argi5 == 0) {
855                        doOnHandleAssist((Bundle) args.arg1, (AssistStructure) args.arg2,
856                                (Throwable) args.arg3, (AssistContent) args.arg4);
857                    } else {
858                        doOnHandleAssistSecondary((Bundle) args.arg1, (AssistStructure) args.arg2,
859                                (Throwable) args.arg3, (AssistContent) args.arg4,
860                                args.argi5, args.argi6);
861                    }
862                    break;
863                case MSG_HANDLE_SCREENSHOT:
864                    if (DEBUG) Log.d(TAG, "onHandleScreenshot: " + msg.obj);
865                    onHandleScreenshot((Bitmap) msg.obj);
866                    break;
867                case MSG_SHOW:
868                    args = (SomeArgs)msg.obj;
869                    if (DEBUG) Log.d(TAG, "doShow: args=" + args.arg1
870                            + " flags=" + msg.arg1
871                            + " showCallback=" + args.arg2);
872                    doShow((Bundle) args.arg1, msg.arg1,
873                            (IVoiceInteractionSessionShowCallback) args.arg2);
874                    break;
875                case MSG_HIDE:
876                    if (DEBUG) Log.d(TAG, "doHide");
877                    doHide();
878                    break;
879                case MSG_ON_LOCKSCREEN_SHOWN:
880                    if (DEBUG) Log.d(TAG, "onLockscreenShown");
881                    onLockscreenShown();
882                    break;
883            }
884            if (args != null) {
885                args.recycle();
886            }
887        }
888
889        @Override
890        public void onBackPressed() {
891            VoiceInteractionSession.this.onBackPressed();
892        }
893    }
894
895    final MyCallbacks mCallbacks = new MyCallbacks();
896
897    /**
898     * Information about where interesting parts of the input method UI appear.
899     */
900    public static final class Insets {
901        /**
902         * This is the part of the UI that is the main content.  It is
903         * used to determine the basic space needed, to resize/pan the
904         * application behind.  It is assumed that this inset does not
905         * change very much, since any change will cause a full resize/pan
906         * of the application behind.  This value is relative to the top edge
907         * of the input method window.
908         */
909        public final Rect contentInsets = new Rect();
910
911        /**
912         * This is the region of the UI that is touchable.  It is used when
913         * {@link #touchableInsets} is set to {@link #TOUCHABLE_INSETS_REGION}.
914         * The region should be specified relative to the origin of the window frame.
915         */
916        public final Region touchableRegion = new Region();
917
918        /**
919         * Option for {@link #touchableInsets}: the entire window frame
920         * can be touched.
921         */
922        public static final int TOUCHABLE_INSETS_FRAME
923                = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
924
925        /**
926         * Option for {@link #touchableInsets}: the area inside of
927         * the content insets can be touched.
928         */
929        public static final int TOUCHABLE_INSETS_CONTENT
930                = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
931
932        /**
933         * Option for {@link #touchableInsets}: the region specified by
934         * {@link #touchableRegion} can be touched.
935         */
936        public static final int TOUCHABLE_INSETS_REGION
937                = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
938
939        /**
940         * Determine which area of the window is touchable by the user.  May
941         * be one of: {@link #TOUCHABLE_INSETS_FRAME},
942         * {@link #TOUCHABLE_INSETS_CONTENT}, or {@link #TOUCHABLE_INSETS_REGION}.
943         */
944        public int touchableInsets;
945    }
946
947    final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer =
948            new ViewTreeObserver.OnComputeInternalInsetsListener() {
949        public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
950            onComputeInsets(mTmpInsets);
951            info.contentInsets.set(mTmpInsets.contentInsets);
952            info.visibleInsets.set(mTmpInsets.contentInsets);
953            info.touchableRegion.set(mTmpInsets.touchableRegion);
954            info.setTouchableInsets(mTmpInsets.touchableInsets);
955        }
956    };
957
958    public VoiceInteractionSession(Context context) {
959        this(context, new Handler());
960    }
961
962    public VoiceInteractionSession(Context context, Handler handler) {
963        mContext = context;
964        mHandlerCaller = new HandlerCaller(context, handler.getLooper(),
965                mCallbacks, true);
966    }
967
968    public Context getContext() {
969        return mContext;
970    }
971
972    void addRequest(Request req) {
973        synchronized (this) {
974            mActiveRequests.put(req.mInterface.asBinder(), req);
975        }
976    }
977
978    boolean isRequestActive(IBinder reqInterface) {
979        synchronized (this) {
980            return mActiveRequests.containsKey(reqInterface);
981        }
982    }
983
984    Request removeRequest(IBinder reqInterface) {
985        synchronized (this) {
986            return mActiveRequests.remove(reqInterface);
987        }
988    }
989
990    void doCreate(IVoiceInteractionManagerService service, IBinder token) {
991        mSystemService = service;
992        mToken = token;
993        onCreate();
994    }
995
996    void doShow(Bundle args, int flags, final IVoiceInteractionSessionShowCallback showCallback) {
997        if (DEBUG) Log.v(TAG, "Showing window: mWindowAdded=" + mWindowAdded
998                + " mWindowVisible=" + mWindowVisible);
999
1000        if (mInShowWindow) {
1001            Log.w(TAG, "Re-entrance in to showWindow");
1002            return;
1003        }
1004
1005        try {
1006            mInShowWindow = true;
1007            onPrepareShow(args, flags);
1008            if (!mWindowVisible) {
1009                ensureWindowAdded();
1010            }
1011            onShow(args, flags);
1012            if (!mWindowVisible) {
1013                mWindowVisible = true;
1014                if (mUiEnabled) {
1015                    mWindow.show();
1016                }
1017            }
1018            if (showCallback != null) {
1019                if (mUiEnabled) {
1020                    mRootView.invalidate();
1021                    mRootView.getViewTreeObserver().addOnPreDrawListener(
1022                            new ViewTreeObserver.OnPreDrawListener() {
1023                                @Override
1024                                public boolean onPreDraw() {
1025                                    mRootView.getViewTreeObserver().removeOnPreDrawListener(this);
1026                                    try {
1027                                        showCallback.onShown();
1028                                    } catch (RemoteException e) {
1029                                        Log.w(TAG, "Error calling onShown", e);
1030                                    }
1031                                    return true;
1032                                }
1033                            });
1034                } else {
1035                    try {
1036                        showCallback.onShown();
1037                    } catch (RemoteException e) {
1038                        Log.w(TAG, "Error calling onShown", e);
1039                    }
1040                }
1041            }
1042        } finally {
1043            mWindowWasVisible = true;
1044            mInShowWindow = false;
1045        }
1046    }
1047
1048    void doHide() {
1049        if (mWindowVisible) {
1050            ensureWindowHidden();
1051            mWindowVisible = false;
1052            onHide();
1053        }
1054    }
1055
1056    void doDestroy() {
1057        onDestroy();
1058        if (mInitialized) {
1059            mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
1060                    mInsetsComputer);
1061            if (mWindowAdded) {
1062                mWindow.dismiss();
1063                mWindowAdded = false;
1064            }
1065            mInitialized = false;
1066        }
1067    }
1068
1069    void ensureWindowCreated() {
1070        if (mInitialized) {
1071            return;
1072        }
1073
1074        if (!mUiEnabled) {
1075            throw new IllegalStateException("setUiEnabled is false");
1076        }
1077
1078        mInitialized = true;
1079        mInflater = (LayoutInflater)mContext.getSystemService(
1080                Context.LAYOUT_INFLATER_SERVICE);
1081        mWindow = new SoftInputWindow(mContext, "VoiceInteractionSession", mTheme,
1082                mCallbacks, this, mDispatcherState,
1083                WindowManager.LayoutParams.TYPE_VOICE_INTERACTION, Gravity.BOTTOM, true);
1084        mWindow.getWindow().addFlags(
1085                WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED |
1086                        WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
1087                        WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
1088
1089        mThemeAttrs = mContext.obtainStyledAttributes(android.R.styleable.VoiceInteractionSession);
1090        mRootView = mInflater.inflate(
1091                com.android.internal.R.layout.voice_interaction_session, null);
1092        mRootView.setSystemUiVisibility(
1093                View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
1094                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
1095        mWindow.setContentView(mRootView);
1096        mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
1097
1098        mContentFrame = (FrameLayout)mRootView.findViewById(android.R.id.content);
1099
1100        mWindow.getWindow().setLayout(MATCH_PARENT, MATCH_PARENT);
1101        mWindow.setToken(mToken);
1102    }
1103
1104    void ensureWindowAdded() {
1105        if (mUiEnabled && !mWindowAdded) {
1106            mWindowAdded = true;
1107            ensureWindowCreated();
1108            View v = onCreateContentView();
1109            if (v != null) {
1110                setContentView(v);
1111            }
1112        }
1113    }
1114
1115    void ensureWindowHidden() {
1116        if (mWindow != null) {
1117            mWindow.hide();
1118        }
1119    }
1120
1121    /**
1122     * Equivalent to {@link VoiceInteractionService#setDisabledShowContext
1123     * VoiceInteractionService.setDisabledShowContext(int)}.
1124     */
1125    public void setDisabledShowContext(int flags) {
1126        try {
1127            mSystemService.setDisabledShowContext(flags);
1128        } catch (RemoteException e) {
1129        }
1130    }
1131
1132    /**
1133     * Equivalent to {@link VoiceInteractionService#getDisabledShowContext
1134     * VoiceInteractionService.getDisabledShowContext}.
1135     */
1136    public int getDisabledShowContext() {
1137        try {
1138            return mSystemService.getDisabledShowContext();
1139        } catch (RemoteException e) {
1140            return 0;
1141        }
1142    }
1143
1144    /**
1145     * Return which show context flags have been disabled by the user through the system
1146     * settings UI, so the session will never get this data.  Returned flags are any combination of
1147     * {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST} and
1148     * {@link VoiceInteractionSession#SHOW_WITH_SCREENSHOT
1149     * VoiceInteractionSession.SHOW_WITH_SCREENSHOT}.  Note that this only tells you about
1150     * global user settings, not about restrictions that may be applied contextual based on
1151     * the current application the user is in or other transient states.
1152     */
1153    public int getUserDisabledShowContext() {
1154        try {
1155            return mSystemService.getUserDisabledShowContext();
1156        } catch (RemoteException e) {
1157            return 0;
1158        }
1159    }
1160
1161    /**
1162     * Show the UI for this session.  This asks the system to go through the process of showing
1163     * your UI, which will eventually culminate in {@link #onShow}.  This is similar to calling
1164     * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
1165     * @param args Arbitrary arguments that will be propagated {@link #onShow}.
1166     * @param flags Indicates additional optional behavior that should be performed.  May
1167     * be any combination of
1168     * {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST} and
1169     * {@link VoiceInteractionSession#SHOW_WITH_SCREENSHOT
1170     * VoiceInteractionSession.SHOW_WITH_SCREENSHOT}
1171     * to request that the system generate and deliver assist data on the current foreground
1172     * app as part of showing the session UI.
1173     */
1174    public void show(Bundle args, int flags) {
1175        if (mToken == null) {
1176            throw new IllegalStateException("Can't call before onCreate()");
1177        }
1178        try {
1179            mSystemService.showSessionFromSession(mToken, args, flags);
1180        } catch (RemoteException e) {
1181        }
1182    }
1183
1184    /**
1185     * Hide the session's UI, if currently shown.  Call this when you are done with your
1186     * user interaction.
1187     */
1188    public void hide() {
1189        if (mToken == null) {
1190            throw new IllegalStateException("Can't call before onCreate()");
1191        }
1192        try {
1193            mSystemService.hideSessionFromSession(mToken);
1194        } catch (RemoteException e) {
1195        }
1196    }
1197
1198    /**
1199     * Control whether the UI layer for this session is enabled.  It is enabled by default.
1200     * If set to false, you will not be able to provide a UI through {@link #onCreateContentView()}.
1201     */
1202    public void setUiEnabled(boolean enabled) {
1203        if (mUiEnabled != enabled) {
1204            mUiEnabled = enabled;
1205            if (mWindowVisible) {
1206                if (enabled) {
1207                    ensureWindowAdded();
1208                    mWindow.show();
1209                } else {
1210                    ensureWindowHidden();
1211                }
1212            }
1213        }
1214    }
1215
1216    /**
1217     * You can call this to customize the theme used by your IME's window.
1218     * This must be set before {@link #onCreate}, so you
1219     * will typically call it in your constructor with the resource ID
1220     * of your custom theme.
1221     */
1222    public void setTheme(int theme) {
1223        if (mWindow != null) {
1224            throw new IllegalStateException("Must be called before onCreate()");
1225        }
1226        mTheme = theme;
1227    }
1228
1229    /**
1230     * Ask that a new activity be started for voice interaction.  This will create a
1231     * new dedicated task in the activity manager for this voice interaction session;
1232     * this means that {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK}
1233     * will be set for you to make it a new task.
1234     *
1235     * <p>The newly started activity will be displayed to the user in a special way, as
1236     * a layer under the voice interaction UI.</p>
1237     *
1238     * <p>As the voice activity runs, it can retrieve a {@link android.app.VoiceInteractor}
1239     * through which it can perform voice interactions through your session.  These requests
1240     * for voice interactions will appear as callbacks on {@link #onGetSupportedCommands},
1241     * {@link #onRequestConfirmation}, {@link #onRequestPickOption},
1242     * {@link #onRequestCompleteVoice}, {@link #onRequestAbortVoice},
1243     * or {@link #onRequestCommand}
1244     *
1245     * <p>You will receive a call to {@link #onTaskStarted} when the task starts up
1246     * and {@link #onTaskFinished} when the last activity has finished.
1247     *
1248     * @param intent The Intent to start this voice interaction.  The given Intent will
1249     * always have {@link Intent#CATEGORY_VOICE Intent.CATEGORY_VOICE} added to it, since
1250     * this is part of a voice interaction.
1251     */
1252    public void startVoiceActivity(Intent intent) {
1253        if (mToken == null) {
1254            throw new IllegalStateException("Can't call before onCreate()");
1255        }
1256        try {
1257            intent.migrateExtraStreamToClipData();
1258            intent.prepareToLeaveProcess(mContext);
1259            int res = mSystemService.startVoiceActivity(mToken, intent,
1260                    intent.resolveType(mContext.getContentResolver()));
1261            Instrumentation.checkStartActivityResult(res, intent);
1262        } catch (RemoteException e) {
1263        }
1264    }
1265
1266
1267
1268    /**
1269     * <p>Ask that a new assistant activity be started.  This will create a new task in the
1270     * in activity manager: this means that
1271     * {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK}
1272     * will be set for you to make it a new task.</p>
1273     *
1274     * <p>The newly started activity will be displayed on top of other activities in the system
1275     * in a new layer that is not affected by multi-window mode.  Tasks started from this activity
1276     * will go into the normal activity layer and not this new layer.</p>
1277     *
1278     * <p>By default, the system will create a window for the UI for this session.  If you are using
1279     * an assistant activity instead, then you can disable the window creation by calling
1280     * {@link #setUiEnabled} in {@link #onPrepareShow(Bundle, int)}.</p>
1281     */
1282    public void startAssistantActivity(Intent intent) {
1283        if (mToken == null) {
1284            throw new IllegalStateException("Can't call before onCreate()");
1285        }
1286        try {
1287            intent.migrateExtraStreamToClipData();
1288            intent.prepareToLeaveProcess(mContext);
1289            int res = mSystemService.startAssistantActivity(mToken, intent,
1290                    intent.resolveType(mContext.getContentResolver()));
1291            Instrumentation.checkStartActivityResult(res, intent);
1292        } catch (RemoteException e) {
1293        }
1294    }
1295
1296    /**
1297     * Set whether this session will keep the device awake while it is running a voice
1298     * activity.  By default, the system holds a wake lock for it while in this state,
1299     * so that it can work even if the screen is off.  Setting this to false removes that
1300     * wake lock, allowing the CPU to go to sleep.  This is typically used if the
1301     * session decides it has been waiting too long for a response from the user and
1302     * doesn't want to let this continue to drain the battery.
1303     *
1304     * <p>Passing false here will release the wake lock, and you can call later with
1305     * true to re-acquire it.  It will also be automatically re-acquired for you each
1306     * time you start a new voice activity task -- that is when you call
1307     * {@link #startVoiceActivity}.</p>
1308     */
1309    public void setKeepAwake(boolean keepAwake) {
1310        if (mToken == null) {
1311            throw new IllegalStateException("Can't call before onCreate()");
1312        }
1313        try {
1314            mSystemService.setKeepAwake(mToken, keepAwake);
1315        } catch (RemoteException e) {
1316        }
1317    }
1318
1319    /**
1320     * Request that all system dialogs (and status bar shade etc) be closed, allowing
1321     * access to the session's UI.  This will <em>not</em> cause the lock screen to be
1322     * dismissed.
1323     */
1324    public void closeSystemDialogs() {
1325        if (mToken == null) {
1326            throw new IllegalStateException("Can't call before onCreate()");
1327        }
1328        try {
1329            mSystemService.closeSystemDialogs(mToken);
1330        } catch (RemoteException e) {
1331        }
1332    }
1333
1334    /**
1335     * Convenience for inflating views.
1336     */
1337    public LayoutInflater getLayoutInflater() {
1338        ensureWindowCreated();
1339        return mInflater;
1340    }
1341
1342    /**
1343     * Retrieve the window being used to show the session's UI.
1344     */
1345    public Dialog getWindow() {
1346        ensureWindowCreated();
1347        return mWindow;
1348    }
1349
1350    /**
1351     * Finish the session.  This completely destroys the session -- the next time it is shown,
1352     * an entirely new one will be created.  You do not normally call this function; instead,
1353     * use {@link #hide} and allow the system to destroy your session if it needs its RAM.
1354     */
1355    public void finish() {
1356        if (mToken == null) {
1357            throw new IllegalStateException("Can't call before onCreate()");
1358        }
1359        try {
1360            mSystemService.finish(mToken);
1361        } catch (RemoteException e) {
1362        }
1363    }
1364
1365    /**
1366     * Initiatize a new session.  At this point you don't know exactly what this
1367     * session will be used for; you will find that out in {@link #onShow}.
1368     */
1369    public void onCreate() {
1370        doOnCreate();
1371    }
1372
1373    private void doOnCreate() {
1374        mTheme = mTheme != 0 ? mTheme
1375                : com.android.internal.R.style.Theme_DeviceDefault_VoiceInteractionSession;
1376    }
1377
1378    /**
1379     * Called prior to {@link #onShow} before any UI setup has occurred.  Not generally useful.
1380     *
1381     * @param args The arguments that were supplied to
1382     * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
1383     * @param showFlags The show flags originally provided to
1384     * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
1385     */
1386    public void onPrepareShow(Bundle args, int showFlags) {
1387    }
1388
1389    /**
1390     * Called when the session UI is going to be shown.  This is called after
1391     * {@link #onCreateContentView} (if the session's content UI needed to be created) and
1392     * immediately prior to the window being shown.  This may be called while the window
1393     * is already shown, if a show request has come in while it is shown, to allow you to
1394     * update the UI to match the new show arguments.
1395     *
1396     * @param args The arguments that were supplied to
1397     * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
1398     * @param showFlags The show flags originally provided to
1399     * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
1400     */
1401    public void onShow(Bundle args, int showFlags) {
1402    }
1403
1404    /**
1405     * Called immediately after stopping to show the session UI.
1406     */
1407    public void onHide() {
1408    }
1409
1410    /**
1411     * Last callback to the session as it is being finished.
1412     */
1413    public void onDestroy() {
1414    }
1415
1416    /**
1417     * Hook in which to create the session's UI.
1418     */
1419    public View onCreateContentView() {
1420        return null;
1421    }
1422
1423    public void setContentView(View view) {
1424        ensureWindowCreated();
1425        mContentFrame.removeAllViews();
1426        mContentFrame.addView(view, new FrameLayout.LayoutParams(
1427                ViewGroup.LayoutParams.MATCH_PARENT,
1428                ViewGroup.LayoutParams.MATCH_PARENT));
1429        mContentFrame.requestApplyInsets();
1430    }
1431
1432    void doOnHandleAssist(Bundle data, AssistStructure structure, Throwable failure,
1433            AssistContent content) {
1434        if (failure != null) {
1435            onAssistStructureFailure(failure);
1436        }
1437        onHandleAssist(data, structure, content);
1438    }
1439
1440    void doOnHandleAssistSecondary(Bundle data, AssistStructure structure, Throwable failure,
1441            AssistContent content, int index, int count) {
1442        if (failure != null) {
1443            onAssistStructureFailure(failure);
1444        }
1445        onHandleAssistSecondary(data, structure, content, index, count);
1446    }
1447
1448    /**
1449     * Called when there has been a failure transferring the {@link AssistStructure} to
1450     * the assistant.  This may happen, for example, if the data is too large and results
1451     * in an out of memory exception, or the client has provided corrupt data.  This will
1452     * be called immediately before {@link #onHandleAssist} and the AssistStructure supplied
1453     * there afterwards will be null.
1454     *
1455     * @param failure The failure exception that was thrown when building the
1456     * {@link AssistStructure}.
1457     */
1458    public void onAssistStructureFailure(Throwable failure) {
1459    }
1460
1461    /**
1462     * Called to receive data from the application that the user was currently viewing when
1463     * an assist session is started.  If the original show request did not specify
1464     * {@link #SHOW_WITH_ASSIST}, this method will not be called.
1465     *
1466     * @param data Arbitrary data supplied by the app through
1467     * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}.
1468     * May be null if assist data has been disabled by the user or device policy.
1469     * @param structure If available, the structure definition of all windows currently
1470     * displayed by the app.  May be null if assist data has been disabled by the user
1471     * or device policy; will be an empty stub if the application has disabled assist
1472     * by marking its window as secure.
1473     * @param content Additional content data supplied by the app through
1474     * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}.
1475     * May be null if assist data has been disabled by the user or device policy; will
1476     * not be automatically filled in with data from the app if the app has marked its
1477     * window as secure.
1478     */
1479    public void onHandleAssist(@Nullable Bundle data, @Nullable AssistStructure structure,
1480            @Nullable AssistContent content) {
1481    }
1482
1483    /**
1484     * Called to receive data from other applications that the user was or is interacting with,
1485     * that are currently on the screen in a multi-window display environment, not including the
1486     * currently focused activity. This could be
1487     * a free-form window, a picture-in-picture window, or another window in a split-screen display.
1488     * <p>
1489     * This method is very similar to
1490     * {@link #onHandleAssist} except that it is called
1491     * for additional non-focused activities along with an index and count that indicates
1492     * which additional activity the data is for. {@code index} will be between 1 and
1493     * {@code count}-1 and this method is called once for each additional window, in no particular
1494     * order. The {@code count} indicates how many windows to expect assist data for, including the
1495     * top focused activity, which continues to be returned via {@link #onHandleAssist}.
1496     * <p>
1497     * To be responsive to assist requests, process assist data as soon as it is received,
1498     * without waiting for all queued activities to return assist data.
1499     *
1500     * @param data Arbitrary data supplied by the app through
1501     * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}.
1502     * May be null if assist data has been disabled by the user or device policy.
1503     * @param structure If available, the structure definition of all windows currently
1504     * displayed by the app.  May be null if assist data has been disabled by the user
1505     * or device policy; will be an empty stub if the application has disabled assist
1506     * by marking its window as secure.
1507     * @param content Additional content data supplied by the app through
1508     * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}.
1509     * May be null if assist data has been disabled by the user or device policy; will
1510     * not be automatically filled in with data from the app if the app has marked its
1511     * window as secure.
1512     * @param index the index of the additional activity that this data
1513     *        is for.
1514     * @param count the total number of additional activities for which the assist data is being
1515     *        returned, including the focused activity that is returned via
1516     *        {@link #onHandleAssist}.
1517     */
1518    public void onHandleAssistSecondary(@Nullable Bundle data, @Nullable AssistStructure structure,
1519            @Nullable AssistContent content, int index, int count) {
1520    }
1521
1522    /**
1523     * Called to receive a screenshot of what the user was currently viewing when an assist
1524     * session is started.  May be null if screenshots are disabled by the user, policy,
1525     * or application.  If the original show request did not specify
1526     * {@link #SHOW_WITH_SCREENSHOT}, this method will not be called.
1527     */
1528    public void onHandleScreenshot(@Nullable Bitmap screenshot) {
1529    }
1530
1531    public boolean onKeyDown(int keyCode, KeyEvent event) {
1532        return false;
1533    }
1534
1535    public boolean onKeyLongPress(int keyCode, KeyEvent event) {
1536        return false;
1537    }
1538
1539    public boolean onKeyUp(int keyCode, KeyEvent event) {
1540        return false;
1541    }
1542
1543    public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
1544        return false;
1545    }
1546
1547    /**
1548     * Called when the user presses the back button while focus is in the session UI.  Note
1549     * that this will only happen if the session UI has requested input focus in its window;
1550     * otherwise, the back key will go to whatever window has focus and do whatever behavior
1551     * it normally has there.  The default implementation simply calls {@link #hide}.
1552     */
1553    public void onBackPressed() {
1554        hide();
1555    }
1556
1557    /**
1558     * Sessions automatically watch for requests that all system UI be closed (such as when
1559     * the user presses HOME), which will appear here.  The default implementation always
1560     * calls {@link #hide}.
1561     */
1562    public void onCloseSystemDialogs() {
1563        hide();
1564    }
1565
1566    /**
1567     * Called when the lockscreen was shown.
1568     */
1569    public void onLockscreenShown() {
1570        hide();
1571    }
1572
1573    @Override
1574    public void onConfigurationChanged(Configuration newConfig) {
1575    }
1576
1577    @Override
1578    public void onLowMemory() {
1579    }
1580
1581    @Override
1582    public void onTrimMemory(int level) {
1583    }
1584
1585    /**
1586     * Compute the interesting insets into your UI.  The default implementation
1587     * sets {@link Insets#contentInsets outInsets.contentInsets.top} to the height
1588     * of the window, meaning it should not adjust content underneath.  The default touchable
1589     * insets are {@link Insets#TOUCHABLE_INSETS_FRAME}, meaning it consumes all touch
1590     * events within its window frame.
1591     *
1592     * @param outInsets Fill in with the current UI insets.
1593     */
1594    public void onComputeInsets(Insets outInsets) {
1595        outInsets.contentInsets.left = 0;
1596        outInsets.contentInsets.bottom = 0;
1597        outInsets.contentInsets.right = 0;
1598        View decor = getWindow().getWindow().getDecorView();
1599        outInsets.contentInsets.top = decor.getHeight();
1600        outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_FRAME;
1601        outInsets.touchableRegion.setEmpty();
1602    }
1603
1604    /**
1605     * Called when a task initiated by {@link #startVoiceActivity(android.content.Intent)}
1606     * has actually started.
1607     *
1608     * @param intent The original {@link Intent} supplied to
1609     * {@link #startVoiceActivity(android.content.Intent)}.
1610     * @param taskId Unique ID of the now running task.
1611     */
1612    public void onTaskStarted(Intent intent, int taskId) {
1613    }
1614
1615    /**
1616     * Called when the last activity of a task initiated by
1617     * {@link #startVoiceActivity(android.content.Intent)} has finished.  The default
1618     * implementation calls {@link #finish()} on the assumption that this represents
1619     * the completion of a voice action.  You can override the implementation if you would
1620     * like a different behavior.
1621     *
1622     * @param intent The original {@link Intent} supplied to
1623     * {@link #startVoiceActivity(android.content.Intent)}.
1624     * @param taskId Unique ID of the finished task.
1625     */
1626    public void onTaskFinished(Intent intent, int taskId) {
1627        hide();
1628    }
1629
1630    /**
1631     * Request to query for what extended commands the session supports.
1632     *
1633     * @param commands An array of commands that are being queried.
1634     * @return Return an array of booleans indicating which of each entry in the
1635     * command array is supported.  A true entry in the array indicates the command
1636     * is supported; false indicates it is not.  The default implementation returns
1637     * an array of all false entries.
1638     */
1639    public boolean[] onGetSupportedCommands(String[] commands) {
1640        return new boolean[commands.length];
1641    }
1642
1643    /**
1644     * Request to confirm with the user before proceeding with an unrecoverable operation,
1645     * corresponding to a {@link android.app.VoiceInteractor.ConfirmationRequest
1646     * VoiceInteractor.ConfirmationRequest}.
1647     *
1648     * @param request The active request.
1649     */
1650    public void onRequestConfirmation(ConfirmationRequest request) {
1651    }
1652
1653    /**
1654     * Request for the user to pick one of N options, corresponding to a
1655     * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
1656     *
1657     * @param request The active request.
1658     */
1659    public void onRequestPickOption(PickOptionRequest request) {
1660    }
1661
1662    /**
1663     * Request to complete the voice interaction session because the voice activity successfully
1664     * completed its interaction using voice.  Corresponds to
1665     * {@link android.app.VoiceInteractor.CompleteVoiceRequest
1666     * VoiceInteractor.CompleteVoiceRequest}.  The default implementation just sends an empty
1667     * confirmation back to allow the activity to exit.
1668     *
1669     * @param request The active request.
1670     */
1671    public void onRequestCompleteVoice(CompleteVoiceRequest request) {
1672    }
1673
1674    /**
1675     * Request to abort the voice interaction session because the voice activity can not
1676     * complete its interaction using voice.  Corresponds to
1677     * {@link android.app.VoiceInteractor.AbortVoiceRequest
1678     * VoiceInteractor.AbortVoiceRequest}.  The default implementation just sends an empty
1679     * confirmation back to allow the activity to exit.
1680     *
1681     * @param request The active request.
1682     */
1683    public void onRequestAbortVoice(AbortVoiceRequest request) {
1684    }
1685
1686    /**
1687     * Process an arbitrary extended command from the caller,
1688     * corresponding to a {@link android.app.VoiceInteractor.CommandRequest
1689     * VoiceInteractor.CommandRequest}.
1690     *
1691     * @param request The active request.
1692     */
1693    public void onRequestCommand(CommandRequest request) {
1694    }
1695
1696    /**
1697     * Called when the {@link android.app.VoiceInteractor} has asked to cancel a {@link Request}
1698     * that was previously delivered to {@link #onRequestConfirmation},
1699     * {@link #onRequestPickOption}, {@link #onRequestCompleteVoice}, {@link #onRequestAbortVoice},
1700     * or {@link #onRequestCommand}.
1701     *
1702     * @param request The request that is being canceled.
1703     */
1704    public void onCancelRequest(Request request) {
1705    }
1706
1707    /**
1708     * Print the Service's state into the given stream.  This gets invoked by
1709     * {@link VoiceInteractionSessionService} when its Service
1710     * {@link android.app.Service#dump} method is called.
1711     *
1712     * @param prefix Text to print at the front of each line.
1713     * @param fd The raw file descriptor that the dump is being sent to.
1714     * @param writer The PrintWriter to which you should dump your state.  This will be
1715     * closed for you after you return.
1716     * @param args additional arguments to the dump request.
1717     */
1718    public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
1719        writer.print(prefix); writer.print("mToken="); writer.println(mToken);
1720        writer.print(prefix); writer.print("mTheme=#"); writer.println(Integer.toHexString(mTheme));
1721        writer.print(prefix); writer.print("mUiEnabled="); writer.println(mUiEnabled);
1722        writer.print(" mInitialized="); writer.println(mInitialized);
1723        writer.print(prefix); writer.print("mWindowAdded="); writer.print(mWindowAdded);
1724        writer.print(" mWindowVisible="); writer.println(mWindowVisible);
1725        writer.print(prefix); writer.print("mWindowWasVisible="); writer.print(mWindowWasVisible);
1726        writer.print(" mInShowWindow="); writer.println(mInShowWindow);
1727        if (mActiveRequests.size() > 0) {
1728            writer.print(prefix); writer.println("Active requests:");
1729            String innerPrefix = prefix + "    ";
1730            for (int i=0; i<mActiveRequests.size(); i++) {
1731                Request req = mActiveRequests.valueAt(i);
1732                writer.print(prefix); writer.print("  #"); writer.print(i);
1733                writer.print(": ");
1734                writer.println(req);
1735                req.dump(innerPrefix, fd, writer, args);
1736
1737            }
1738        }
1739    }
1740}
1741