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