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