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