• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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().getAttributes().setFitInsetsTypes(0 /* types */);
1141         mWindow.getWindow().addFlags(
1142                 WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED |
1143                         WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
1144                         WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
1145 
1146         mThemeAttrs = mContext.obtainStyledAttributes(android.R.styleable.VoiceInteractionSession);
1147         mRootView = mInflater.inflate(
1148                 com.android.internal.R.layout.voice_interaction_session, null);
1149         mRootView.setSystemUiVisibility(
1150                 View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
1151                         | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
1152         mWindow.setContentView(mRootView);
1153         mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
1154 
1155         mContentFrame = (FrameLayout)mRootView.findViewById(android.R.id.content);
1156 
1157         mWindow.getWindow().setLayout(MATCH_PARENT, MATCH_PARENT);
1158         mWindow.setToken(mToken);
1159     }
1160 
ensureWindowAdded()1161     void ensureWindowAdded() {
1162         if (mUiEnabled && !mWindowAdded) {
1163             mWindowAdded = true;
1164             ensureWindowCreated();
1165             View v = onCreateContentView();
1166             if (v != null) {
1167                 setContentView(v);
1168             }
1169         }
1170     }
1171 
ensureWindowHidden()1172     void ensureWindowHidden() {
1173         if (mWindow != null) {
1174             mWindow.hide();
1175         }
1176     }
1177 
1178     /**
1179      * Equivalent to {@link VoiceInteractionService#setDisabledShowContext
1180      * VoiceInteractionService.setDisabledShowContext(int)}.
1181      */
setDisabledShowContext(int flags)1182     public void setDisabledShowContext(int flags) {
1183         try {
1184             mSystemService.setDisabledShowContext(flags);
1185         } catch (RemoteException e) {
1186         }
1187     }
1188 
1189     /**
1190      * Equivalent to {@link VoiceInteractionService#getDisabledShowContext
1191      * VoiceInteractionService.getDisabledShowContext}.
1192      */
getDisabledShowContext()1193     public int getDisabledShowContext() {
1194         try {
1195             return mSystemService.getDisabledShowContext();
1196         } catch (RemoteException e) {
1197             return 0;
1198         }
1199     }
1200 
1201     /**
1202      * Return which show context flags have been disabled by the user through the system
1203      * settings UI, so the session will never get this data.  Returned flags are any combination of
1204      * {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST} and
1205      * {@link VoiceInteractionSession#SHOW_WITH_SCREENSHOT
1206      * VoiceInteractionSession.SHOW_WITH_SCREENSHOT}.  Note that this only tells you about
1207      * global user settings, not about restrictions that may be applied contextual based on
1208      * the current application the user is in or other transient states.
1209      */
getUserDisabledShowContext()1210     public int getUserDisabledShowContext() {
1211         try {
1212             return mSystemService.getUserDisabledShowContext();
1213         } catch (RemoteException e) {
1214             return 0;
1215         }
1216     }
1217 
1218     /**
1219      * Show the UI for this session.  This asks the system to go through the process of showing
1220      * your UI, which will eventually culminate in {@link #onShow}.  This is similar to calling
1221      * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
1222      * @param args Arbitrary arguments that will be propagated {@link #onShow}.
1223      * @param flags Indicates additional optional behavior that should be performed. May
1224      * be any combination of
1225      * {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST} and
1226      * {@link VoiceInteractionSession#SHOW_WITH_SCREENSHOT
1227      * VoiceInteractionSession.SHOW_WITH_SCREENSHOT}
1228      * to request that the system generate and deliver assist data on the current foreground
1229      * app as part of showing the session UI.
1230      */
show(Bundle args, int flags)1231     public void show(Bundle args, int flags) {
1232         if (mToken == null) {
1233             throw new IllegalStateException("Can't call before onCreate()");
1234         }
1235         try {
1236             mSystemService.showSessionFromSession(mToken, args, flags);
1237         } catch (RemoteException e) {
1238         }
1239     }
1240 
1241     /**
1242      * Hide the session's UI, if currently shown.  Call this when you are done with your
1243      * user interaction.
1244      */
hide()1245     public void hide() {
1246         if (mToken == null) {
1247             throw new IllegalStateException("Can't call before onCreate()");
1248         }
1249         try {
1250             mSystemService.hideSessionFromSession(mToken);
1251         } catch (RemoteException e) {
1252         }
1253     }
1254 
1255     /**
1256      * Control whether the UI layer for this session is enabled.  It is enabled by default.
1257      * If set to false, you will not be able to provide a UI through {@link #onCreateContentView()}.
1258      */
setUiEnabled(boolean enabled)1259     public void setUiEnabled(boolean enabled) {
1260         if (mUiEnabled != enabled) {
1261             mUiEnabled = enabled;
1262             if (mWindowVisible) {
1263                 if (enabled) {
1264                     ensureWindowAdded();
1265                     mWindow.show();
1266                 } else {
1267                     ensureWindowHidden();
1268                 }
1269             }
1270         }
1271     }
1272 
1273     /**
1274      * You can call this to customize the theme used by your IME's window.
1275      * This must be set before {@link #onCreate}, so you
1276      * will typically call it in your constructor with the resource ID
1277      * of your custom theme.
1278      */
setTheme(int theme)1279     public void setTheme(int theme) {
1280         if (mWindow != null) {
1281             throw new IllegalStateException("Must be called before onCreate()");
1282         }
1283         mTheme = theme;
1284     }
1285 
1286     /**
1287      * Ask that a new activity be started for voice interaction.  This will create a
1288      * new dedicated task in the activity manager for this voice interaction session;
1289      * this means that {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK}
1290      * will be set for you to make it a new task.
1291      *
1292      * <p>The newly started activity will be displayed to the user in a special way, as
1293      * a layer under the voice interaction UI.</p>
1294      *
1295      * <p>As the voice activity runs, it can retrieve a {@link android.app.VoiceInteractor}
1296      * through which it can perform voice interactions through your session.  These requests
1297      * for voice interactions will appear as callbacks on {@link #onGetSupportedCommands},
1298      * {@link #onRequestConfirmation}, {@link #onRequestPickOption},
1299      * {@link #onRequestCompleteVoice}, {@link #onRequestAbortVoice},
1300      * or {@link #onRequestCommand}
1301      *
1302      * <p>You will receive a call to {@link #onTaskStarted} when the task starts up
1303      * and {@link #onTaskFinished} when the last activity has finished.
1304      *
1305      * @param intent The Intent to start this voice interaction.  The given Intent will
1306      * always have {@link Intent#CATEGORY_VOICE Intent.CATEGORY_VOICE} added to it, since
1307      * this is part of a voice interaction.
1308      */
startVoiceActivity(Intent intent)1309     public void startVoiceActivity(Intent intent) {
1310         if (mToken == null) {
1311             throw new IllegalStateException("Can't call before onCreate()");
1312         }
1313         try {
1314             intent.migrateExtraStreamToClipData(mContext);
1315             intent.prepareToLeaveProcess(mContext);
1316             int res = mSystemService.startVoiceActivity(mToken, intent,
1317                     intent.resolveType(mContext.getContentResolver()),
1318                     mContext.getAttributionTag());
1319             Instrumentation.checkStartActivityResult(res, intent);
1320         } catch (RemoteException e) {
1321         }
1322     }
1323 
1324     /**
1325      * <p>Ask that a new assistant activity be started.  This will create a new task in the
1326      * in activity manager: this means that
1327      * {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK}
1328      * will be set for you to make it a new task.</p>
1329      *
1330      * <p>The newly started activity will be displayed on top of other activities in the system
1331      * in a new layer that is not affected by multi-window mode.  Tasks started from this activity
1332      * will go into the normal activity layer and not this new layer.</p>
1333      *
1334      * <p>By default, the system will create a window for the UI for this session.  If you are using
1335      * an assistant activity instead, then you can disable the window creation by calling
1336      * {@link #setUiEnabled} in {@link #onPrepareShow(Bundle, int)}.</p>
1337      */
startAssistantActivity(Intent intent)1338     public void startAssistantActivity(Intent intent) {
1339         if (mToken == null) {
1340             throw new IllegalStateException("Can't call before onCreate()");
1341         }
1342         try {
1343             intent.migrateExtraStreamToClipData(mContext);
1344             intent.prepareToLeaveProcess(mContext);
1345             int res = mSystemService.startAssistantActivity(mToken, intent,
1346                     intent.resolveType(mContext.getContentResolver()),
1347                     mContext.getAttributionTag());
1348             Instrumentation.checkStartActivityResult(res, intent);
1349         } catch (RemoteException e) {
1350         }
1351     }
1352 
1353     /**
1354      * Requests a list of supported actions from an app.
1355      *
1356      * @param activityId Ths activity id of the app to get the actions from.
1357      * @param resultExecutor The handler to receive the callback
1358      * @param cancellationSignal A signal to cancel the operation in progress,
1359      *     or {@code null} if none.
1360      * @param callback The callback to receive the response
1361      */
requestDirectActions(@onNull ActivityId activityId, @Nullable CancellationSignal cancellationSignal, @NonNull @CallbackExecutor Executor resultExecutor, @NonNull Consumer<List<DirectAction>> callback)1362     public final void requestDirectActions(@NonNull ActivityId activityId,
1363             @Nullable CancellationSignal cancellationSignal,
1364             @NonNull @CallbackExecutor Executor resultExecutor,
1365             @NonNull Consumer<List<DirectAction>> callback) {
1366         Preconditions.checkNotNull(activityId);
1367         Preconditions.checkNotNull(resultExecutor);
1368         Preconditions.checkNotNull(callback);
1369         if (mToken == null) {
1370             throw new IllegalStateException("Can't call before onCreate()");
1371         }
1372 
1373         if (cancellationSignal != null) {
1374             cancellationSignal.throwIfCanceled();
1375         }
1376 
1377         final RemoteCallback cancellationCallback = (cancellationSignal != null)
1378                 ? new RemoteCallback(b -> {
1379                     if (b != null) {
1380                         final IBinder cancellation = b.getBinder(
1381                                 VoiceInteractor.KEY_CANCELLATION_SIGNAL);
1382                         if (cancellation != null) {
1383                             cancellationSignal.setRemote(ICancellationSignal.Stub.asInterface(
1384                                     cancellation));
1385                         }
1386                     }
1387                 })
1388                 : null;
1389 
1390         try {
1391             mSystemService.requestDirectActions(mToken, activityId.getTaskId(),
1392                     activityId.getAssistToken(), cancellationCallback,
1393                     new RemoteCallback(createSafeResultListener((result) -> {
1394                 List<DirectAction> list;
1395                 if (result == null) {
1396                     list = Collections.emptyList();
1397                 } else {
1398                     final ParceledListSlice<DirectAction> pls = result.getParcelable(
1399                             DirectAction.KEY_ACTIONS_LIST);
1400                     if (pls != null) {
1401                         final List<DirectAction> receivedList = pls.getList();
1402                         list = (receivedList != null) ? receivedList : Collections.emptyList();
1403                     } else {
1404                         list = Collections.emptyList();
1405                     }
1406                 }
1407                 resultExecutor.execute(() -> callback.accept(list));
1408             })));
1409         } catch (RemoteException e) {
1410             e.rethrowFromSystemServer();
1411         }
1412     }
1413 
1414     /**
1415      * Called when the direct actions are invalidated.
1416      */
onDirectActionsInvalidated(@onNull ActivityId activityId)1417     public void onDirectActionsInvalidated(@NonNull ActivityId activityId) {
1418 
1419     }
1420 
1421     /**
1422      * Asks that an action be performed by the app. This will send a request to the app which
1423      * provided this action.
1424      *
1425      * <p> An action could take time to execute and the result is provided asynchronously
1426      * via a callback. If the action is taking longer and you want to cancel its execution
1427      * you can pass in a cancellation signal through which to notify the app to abort the
1428      * action.
1429      *
1430      * @param action The action to be performed.
1431      * @param extras Any optional extras sent to the app as part of the request
1432      * @param cancellationSignal A signal to cancel the operation in progress,
1433      *                          or {@code null} if none.
1434      * @param resultExecutor The handler to receive the callback.
1435      * @param resultListener The callback to receive the response.
1436      *
1437      * @see #requestDirectActions(ActivityId, CancellationSignal, Executor, Consumer)
1438      * @see Activity#onGetDirectActions(CancellationSignal, Consumer)
1439      */
performDirectAction(@onNull DirectAction action, @Nullable Bundle extras, @Nullable CancellationSignal cancellationSignal, @NonNull @CallbackExecutor Executor resultExecutor, @NonNull Consumer<Bundle> resultListener)1440     public final void performDirectAction(@NonNull DirectAction action, @Nullable Bundle extras,
1441             @Nullable CancellationSignal cancellationSignal,
1442             @NonNull @CallbackExecutor Executor resultExecutor,
1443             @NonNull Consumer<Bundle> resultListener) {
1444         if (mToken == null) {
1445             throw new IllegalStateException("Can't call before onCreate()");
1446         }
1447         Preconditions.checkNotNull(resultExecutor);
1448         Preconditions.checkNotNull(resultListener);
1449 
1450         if (cancellationSignal != null) {
1451             cancellationSignal.throwIfCanceled();
1452         }
1453 
1454         final RemoteCallback cancellationCallback = (cancellationSignal != null)
1455                 ? new RemoteCallback(createSafeResultListener(b -> {
1456                     if (b != null) {
1457                         final IBinder cancellation = b.getBinder(
1458                                 VoiceInteractor.KEY_CANCELLATION_SIGNAL);
1459                         if (cancellation != null) {
1460                             cancellationSignal.setRemote(ICancellationSignal.Stub.asInterface(
1461                                     cancellation));
1462                         }
1463                     }
1464                 }))
1465                 : null;
1466 
1467         final RemoteCallback resultCallback = new RemoteCallback(createSafeResultListener(b -> {
1468             if (b != null) {
1469                 resultExecutor.execute(() -> resultListener.accept(b));
1470             } else {
1471                 resultExecutor.execute(() -> resultListener.accept(Bundle.EMPTY));
1472             }
1473         }));
1474 
1475         try {
1476             mSystemService.performDirectAction(mToken, action.getId(), extras,
1477                     action.getTaskId(), action.getActivityId(), cancellationCallback,
1478                     resultCallback);
1479         } catch (RemoteException e) {
1480             e.rethrowFromSystemServer();
1481         }
1482     }
1483 
1484     /**
1485      * Set whether this session will keep the device awake while it is running a voice
1486      * activity.  By default, the system holds a wake lock for it while in this state,
1487      * so that it can work even if the screen is off.  Setting this to false removes that
1488      * wake lock, allowing the CPU to go to sleep.  This is typically used if the
1489      * session decides it has been waiting too long for a response from the user and
1490      * doesn't want to let this continue to drain the battery.
1491      *
1492      * <p>Passing false here will release the wake lock, and you can call later with
1493      * true to re-acquire it.  It will also be automatically re-acquired for you each
1494      * time you start a new voice activity task -- that is when you call
1495      * {@link #startVoiceActivity}.</p>
1496      */
setKeepAwake(boolean keepAwake)1497     public void setKeepAwake(boolean keepAwake) {
1498         if (mToken == null) {
1499             throw new IllegalStateException("Can't call before onCreate()");
1500         }
1501         try {
1502             mSystemService.setKeepAwake(mToken, keepAwake);
1503         } catch (RemoteException e) {
1504         }
1505     }
1506 
1507     /**
1508      * Request that all system dialogs (and status bar shade etc) be closed, allowing
1509      * access to the session's UI.  This will <em>not</em> cause the lock screen to be
1510      * dismissed.
1511      */
closeSystemDialogs()1512     public void closeSystemDialogs() {
1513         if (mToken == null) {
1514             throw new IllegalStateException("Can't call before onCreate()");
1515         }
1516         try {
1517             mSystemService.closeSystemDialogs(mToken);
1518         } catch (RemoteException e) {
1519         }
1520     }
1521 
1522     /**
1523      * Convenience for inflating views.
1524      */
getLayoutInflater()1525     public LayoutInflater getLayoutInflater() {
1526         ensureWindowCreated();
1527         return mInflater;
1528     }
1529 
1530     /**
1531      * Retrieve the window being used to show the session's UI.
1532      */
getWindow()1533     public Dialog getWindow() {
1534         ensureWindowCreated();
1535         return mWindow;
1536     }
1537 
1538     /**
1539      * Finish the session.  This completely destroys the session -- the next time it is shown,
1540      * an entirely new one will be created.  You do not normally call this function; instead,
1541      * use {@link #hide} and allow the system to destroy your session if it needs its RAM.
1542      */
finish()1543     public void finish() {
1544         if (mToken == null) {
1545             throw new IllegalStateException("Can't call before onCreate()");
1546         }
1547         try {
1548             mSystemService.finish(mToken);
1549         } catch (RemoteException e) {
1550         }
1551     }
1552 
1553     /**
1554      * Initiatize a new session.  At this point you don't know exactly what this
1555      * session will be used for; you will find that out in {@link #onShow}.
1556      */
onCreate()1557     public void onCreate() {
1558         doOnCreate();
1559     }
1560 
doOnCreate()1561     private void doOnCreate() {
1562         mTheme = mTheme != 0 ? mTheme
1563                 : com.android.internal.R.style.Theme_DeviceDefault_VoiceInteractionSession;
1564     }
1565 
1566     /**
1567      * Called prior to {@link #onShow} before any UI setup has occurred.  Not generally useful.
1568      *
1569      * @param args The arguments that were supplied to
1570      * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
1571      * @param showFlags The show flags originally provided to
1572      * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
1573      */
onPrepareShow(Bundle args, int showFlags)1574     public void onPrepareShow(Bundle args, int showFlags) {
1575     }
1576 
1577     /**
1578      * Called when the session UI is going to be shown.  This is called after
1579      * {@link #onCreateContentView} (if the session's content UI needed to be created) and
1580      * immediately prior to the window being shown.  This may be called while the window
1581      * is already shown, if a show request has come in while it is shown, to allow you to
1582      * update the UI to match the new show arguments.
1583      *
1584      * @param args The arguments that were supplied to
1585      * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
1586      * Some example keys include : "invocation_type", "invocation_phone_state",
1587      * "invocation_time_ms", Intent.EXTRA_TIME ("android.intent.extra.TIME") indicating timing
1588      * in milliseconds of the KeyEvent that triggered Assistant and
1589      * Intent.EXTRA_ASSIST_INPUT_DEVICE_ID (android.intent.extra.ASSIST_INPUT_DEVICE_ID)
1590      *  referring to the device that sent the request.
1591      * @param showFlags The show flags originally provided to
1592      * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
1593      */
onShow(Bundle args, int showFlags)1594     public void onShow(Bundle args, int showFlags) {
1595     }
1596 
1597     /**
1598      * Called immediately after stopping to show the session UI.
1599      */
onHide()1600     public void onHide() {
1601     }
1602 
1603     /**
1604      * Last callback to the session as it is being finished.
1605      */
onDestroy()1606     public void onDestroy() {
1607     }
1608 
1609     /**
1610      * Hook in which to create the session's UI.
1611      */
onCreateContentView()1612     public View onCreateContentView() {
1613         return null;
1614     }
1615 
setContentView(View view)1616     public void setContentView(View view) {
1617         ensureWindowCreated();
1618         mContentFrame.removeAllViews();
1619         mContentFrame.addView(view, new FrameLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
1620         mContentFrame.requestApplyInsets();
1621     }
1622 
doOnHandleAssist(int taskId, IBinder assistToken, Bundle data, AssistStructure structure, Throwable failure, AssistContent content, int index, int count)1623     void doOnHandleAssist(int taskId, IBinder assistToken, Bundle data, AssistStructure structure,
1624             Throwable failure, AssistContent content, int index, int count) {
1625         if (failure != null) {
1626             onAssistStructureFailure(failure);
1627         }
1628         AssistState assistState = new AssistState(new ActivityId(taskId, assistToken),
1629                 data, structure, content, index, count);
1630         onHandleAssist(assistState);
1631     }
1632 
1633     /**
1634      * Called when there has been a failure transferring the {@link AssistStructure} to
1635      * the assistant.  This may happen, for example, if the data is too large and results
1636      * in an out of memory exception, or the client has provided corrupt data.  This will
1637      * be called immediately before {@link #onHandleAssist} and the AssistStructure supplied
1638      * there afterwards will be null.
1639      *
1640      * @param failure The failure exception that was thrown when building the
1641      * {@link AssistStructure}.
1642      */
onAssistStructureFailure(Throwable failure)1643     public void onAssistStructureFailure(Throwable failure) {
1644     }
1645 
1646     /**
1647      * Called to receive data from the application that the user was currently viewing when
1648 -     * an assist session is started.  If the original show request did not specify
1649      * {@link #SHOW_WITH_ASSIST}, this method will not be called.
1650      *
1651      * @param data Arbitrary data supplied by the app through
1652      * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}.
1653      * May be null if assist data has been disabled by the user or device policy.
1654      * @param structure If available, the structure definition of all windows currently
1655      * displayed by the app.  May be null if assist data has been disabled by the user
1656      * or device policy; will be an empty stub if the application has disabled assist
1657      * by marking its window as secure.
1658      * @param content Additional content data supplied by the app through
1659      * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}.
1660      * May be null if assist data has been disabled by the user or device policy; will
1661      * not be automatically filled in with data from the app if the app has marked its
1662      * window as secure.
1663      *
1664      * @deprecated use {@link #onHandleAssist(AssistState)}
1665      */
1666     @Deprecated
onHandleAssist(@ullable Bundle data, @Nullable AssistStructure structure, @Nullable AssistContent content)1667     public void onHandleAssist(@Nullable Bundle data, @Nullable AssistStructure structure,
1668             @Nullable AssistContent content) {
1669     }
1670 
1671     /**
1672      * Called to receive data from the application that the user was currently viewing when
1673      * an assist session is started. If the original show request did not specify
1674      * {@link #SHOW_WITH_ASSIST}, {@link AssistState} parameter will only provide
1675      * {@link ActivityId}.
1676      *
1677      * <p>This method is called for all activities along with an index and count that indicates
1678      * which activity the data is for. {@code index} will be between 0 and {@code count}-1 and
1679      * this method is called once for each activity in no particular order. The {@code count}
1680      * indicates how many activities to expect assist data for, including the top focused one.
1681      * The focused activity can be determined by calling {@link AssistState#isFocused()}.
1682      *
1683      * <p>To be responsive to assist requests, process assist data as soon as it is received,
1684      * without waiting for all queued activities to return assist data.
1685      *
1686      * @param state The state object capturing the state of an activity.
1687      */
onHandleAssist(@onNull AssistState state)1688     public void onHandleAssist(@NonNull AssistState state) {
1689         if (state.getAssistData() == null && state.getAssistStructure() == null
1690                 && state.getAssistContent() == null) {
1691             return;
1692         } else if (state.getIndex() == 0) {
1693             onHandleAssist(state.getAssistData(), state.getAssistStructure(),
1694                     state.getAssistContent());
1695         } else {
1696             onHandleAssistSecondary(state.getAssistData(), state.getAssistStructure(),
1697                     state.getAssistContent(), state.getIndex(), state.getCount());
1698         }
1699     }
1700 
1701     /**
1702      * Called to receive data from other applications that the user was or is interacting with,
1703      * that are currently on the screen in a multi-window display environment, not including the
1704      * currently focused activity. This could be
1705      * a free-form window, a picture-in-picture window, or another window in a split-screen display.
1706      * <p>
1707      * This method is very similar to
1708      * {@link #onHandleAssist} except that it is called
1709      * for additional non-focused activities along with an index and count that indicates
1710      * which additional activity the data is for. {@code index} will be between 1 and
1711      * {@code count}-1 and this method is called once for each additional window, in no particular
1712      * order. The {@code count} indicates how many windows to expect assist data for, including the
1713      * top focused activity, which continues to be returned via {@link #onHandleAssist}.
1714      * <p>
1715      * To be responsive to assist requests, process assist data as soon as it is received,
1716      * without waiting for all queued activities to return assist data.
1717      *
1718      * @param data Arbitrary data supplied by the app through
1719      * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}.
1720      * May be null if assist data has been disabled by the user or device policy.
1721      * @param structure If available, the structure definition of all windows currently
1722      * displayed by the app.  May be null if assist data has been disabled by the user
1723      * or device policy; will be an empty stub if the application has disabled assist
1724      * by marking its window as secure.
1725      * @param content Additional content data supplied by the app through
1726      * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}.
1727      * May be null if assist data has been disabled by the user or device policy; will
1728      * not be automatically filled in with data from the app if the app has marked its
1729      * window as secure.
1730      * @param index the index of the additional activity that this data
1731      *        is for.
1732      * @param count the total number of additional activities for which the assist data is being
1733      *        returned, including the focused activity that is returned via
1734      *        {@link #onHandleAssist}.
1735      *
1736      * @deprecated use {@link #onHandleAssist(AssistState)}
1737      */
1738     @Deprecated
onHandleAssistSecondary(@ullable Bundle data, @Nullable AssistStructure structure, @Nullable AssistContent content, int index, int count)1739     public void onHandleAssistSecondary(@Nullable Bundle data, @Nullable AssistStructure structure,
1740             @Nullable AssistContent content, int index, int count) {
1741     }
1742 
1743     /**
1744      * Called to receive a screenshot of what the user was currently viewing when an assist
1745      * session is started.  May be null if screenshots are disabled by the user, policy,
1746      * or application.  If the original show request did not specify
1747      * {@link #SHOW_WITH_SCREENSHOT}, this method will not be called.
1748      */
onHandleScreenshot(@ullable Bitmap screenshot)1749     public void onHandleScreenshot(@Nullable Bitmap screenshot) {
1750     }
1751 
onKeyDown(int keyCode, KeyEvent event)1752     public boolean onKeyDown(int keyCode, KeyEvent event) {
1753         return false;
1754     }
1755 
onKeyLongPress(int keyCode, KeyEvent event)1756     public boolean onKeyLongPress(int keyCode, KeyEvent event) {
1757         return false;
1758     }
1759 
onKeyUp(int keyCode, KeyEvent event)1760     public boolean onKeyUp(int keyCode, KeyEvent event) {
1761         return false;
1762     }
1763 
onKeyMultiple(int keyCode, int count, KeyEvent event)1764     public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
1765         return false;
1766     }
1767 
1768     /**
1769      * Called when the user presses the back button while focus is in the session UI.  Note
1770      * that this will only happen if the session UI has requested input focus in its window;
1771      * otherwise, the back key will go to whatever window has focus and do whatever behavior
1772      * it normally has there.  The default implementation simply calls {@link #hide}.
1773      */
onBackPressed()1774     public void onBackPressed() {
1775         hide();
1776     }
1777 
1778     /**
1779      * Sessions automatically watch for requests that all system UI be closed (such as when
1780      * the user presses HOME), which will appear here.  The default implementation always
1781      * calls {@link #hide}.
1782      */
onCloseSystemDialogs()1783     public void onCloseSystemDialogs() {
1784         hide();
1785     }
1786 
1787     /**
1788      * Called when the lockscreen was shown.
1789      */
onLockscreenShown()1790     public void onLockscreenShown() {
1791         hide();
1792     }
1793 
1794     @Override
onConfigurationChanged(Configuration newConfig)1795     public void onConfigurationChanged(Configuration newConfig) {
1796     }
1797 
1798     @Override
onLowMemory()1799     public void onLowMemory() {
1800     }
1801 
1802     @Override
onTrimMemory(int level)1803     public void onTrimMemory(int level) {
1804     }
1805 
1806     /**
1807      * Compute the interesting insets into your UI.  The default implementation
1808      * sets {@link Insets#contentInsets outInsets.contentInsets.top} to the height
1809      * of the window, meaning it should not adjust content underneath.  The default touchable
1810      * insets are {@link Insets#TOUCHABLE_INSETS_FRAME}, meaning it consumes all touch
1811      * events within its window frame.
1812      *
1813      * @param outInsets Fill in with the current UI insets.
1814      */
onComputeInsets(Insets outInsets)1815     public void onComputeInsets(Insets outInsets) {
1816         outInsets.contentInsets.left = 0;
1817         outInsets.contentInsets.bottom = 0;
1818         outInsets.contentInsets.right = 0;
1819         View decor = getWindow().getWindow().getDecorView();
1820         outInsets.contentInsets.top = decor.getHeight();
1821         outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_FRAME;
1822         outInsets.touchableRegion.setEmpty();
1823     }
1824 
1825     /**
1826      * Called when a task initiated by {@link #startVoiceActivity(android.content.Intent)}
1827      * has actually started.
1828      *
1829      * @param intent The original {@link Intent} supplied to
1830      * {@link #startVoiceActivity(android.content.Intent)}.
1831      * @param taskId Unique ID of the now running task.
1832      */
onTaskStarted(Intent intent, int taskId)1833     public void onTaskStarted(Intent intent, int taskId) {
1834     }
1835 
1836     /**
1837      * Called when the last activity of a task initiated by
1838      * {@link #startVoiceActivity(android.content.Intent)} has finished.  The default
1839      * implementation calls {@link #finish()} on the assumption that this represents
1840      * the completion of a voice action.  You can override the implementation if you would
1841      * like a different behavior.
1842      *
1843      * @param intent The original {@link Intent} supplied to
1844      * {@link #startVoiceActivity(android.content.Intent)}.
1845      * @param taskId Unique ID of the finished task.
1846      */
onTaskFinished(Intent intent, int taskId)1847     public void onTaskFinished(Intent intent, int taskId) {
1848         hide();
1849     }
1850 
1851     /**
1852      * Request to query for what extended commands the session supports.
1853      *
1854      * @param commands An array of commands that are being queried.
1855      * @return Return an array of booleans indicating which of each entry in the
1856      * command array is supported.  A true entry in the array indicates the command
1857      * is supported; false indicates it is not.  The default implementation returns
1858      * an array of all false entries.
1859      */
onGetSupportedCommands(String[] commands)1860     public boolean[] onGetSupportedCommands(String[] commands) {
1861         return new boolean[commands.length];
1862     }
1863 
1864     /**
1865      * Request to confirm with the user before proceeding with an unrecoverable operation,
1866      * corresponding to a {@link android.app.VoiceInteractor.ConfirmationRequest
1867      * VoiceInteractor.ConfirmationRequest}.
1868      *
1869      * @param request The active request.
1870      */
onRequestConfirmation(ConfirmationRequest request)1871     public void onRequestConfirmation(ConfirmationRequest request) {
1872     }
1873 
1874     /**
1875      * Request for the user to pick one of N options, corresponding to a
1876      * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
1877      *
1878      * @param request The active request.
1879      */
onRequestPickOption(PickOptionRequest request)1880     public void onRequestPickOption(PickOptionRequest request) {
1881     }
1882 
1883     /**
1884      * Request to complete the voice interaction session because the voice activity successfully
1885      * completed its interaction using voice.  Corresponds to
1886      * {@link android.app.VoiceInteractor.CompleteVoiceRequest
1887      * VoiceInteractor.CompleteVoiceRequest}.  The default implementation just sends an empty
1888      * confirmation back to allow the activity to exit.
1889      *
1890      * @param request The active request.
1891      */
onRequestCompleteVoice(CompleteVoiceRequest request)1892     public void onRequestCompleteVoice(CompleteVoiceRequest request) {
1893     }
1894 
1895     /**
1896      * Request to abort the voice interaction session because the voice activity can not
1897      * complete its interaction using voice.  Corresponds to
1898      * {@link android.app.VoiceInteractor.AbortVoiceRequest
1899      * VoiceInteractor.AbortVoiceRequest}.  The default implementation just sends an empty
1900      * confirmation back to allow the activity to exit.
1901      *
1902      * @param request The active request.
1903      */
onRequestAbortVoice(AbortVoiceRequest request)1904     public void onRequestAbortVoice(AbortVoiceRequest request) {
1905     }
1906 
1907     /**
1908      * Process an arbitrary extended command from the caller,
1909      * corresponding to a {@link android.app.VoiceInteractor.CommandRequest
1910      * VoiceInteractor.CommandRequest}.
1911      *
1912      * @param request The active request.
1913      */
onRequestCommand(CommandRequest request)1914     public void onRequestCommand(CommandRequest request) {
1915     }
1916 
1917     /**
1918      * Called when the {@link android.app.VoiceInteractor} has asked to cancel a {@link Request}
1919      * that was previously delivered to {@link #onRequestConfirmation},
1920      * {@link #onRequestPickOption}, {@link #onRequestCompleteVoice}, {@link #onRequestAbortVoice},
1921      * or {@link #onRequestCommand}.
1922      *
1923      * @param request The request that is being canceled.
1924      */
onCancelRequest(Request request)1925     public void onCancelRequest(Request request) {
1926     }
1927 
1928     /**
1929      * Print the Service's state into the given stream.  This gets invoked by
1930      * {@link VoiceInteractionSessionService} when its Service
1931      * {@link android.app.Service#dump} method is called.
1932      *
1933      * @param prefix Text to print at the front of each line.
1934      * @param fd The raw file descriptor that the dump is being sent to.
1935      * @param writer The PrintWriter to which you should dump your state.  This will be
1936      * closed for you after you return.
1937      * @param args additional arguments to the dump request.
1938      */
dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)1939     public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
1940         writer.print(prefix); writer.print("mToken="); writer.println(mToken);
1941         writer.print(prefix); writer.print("mTheme=#"); writer.println(Integer.toHexString(mTheme));
1942         writer.print(prefix); writer.print("mUiEnabled="); writer.println(mUiEnabled);
1943         writer.print(" mInitialized="); writer.println(mInitialized);
1944         writer.print(prefix); writer.print("mWindowAdded="); writer.print(mWindowAdded);
1945         writer.print(" mWindowVisible="); writer.println(mWindowVisible);
1946         writer.print(prefix); writer.print("mWindowWasVisible="); writer.print(mWindowWasVisible);
1947         writer.print(" mInShowWindow="); writer.println(mInShowWindow);
1948         if (mActiveRequests.size() > 0) {
1949             writer.print(prefix); writer.println("Active requests:");
1950             String innerPrefix = prefix + "    ";
1951             for (int i=0; i<mActiveRequests.size(); i++) {
1952                 Request req = mActiveRequests.valueAt(i);
1953                 writer.print(prefix); writer.print("  #"); writer.print(i);
1954                 writer.print(": ");
1955                 writer.println(req);
1956                 req.dump(innerPrefix, fd, writer, args);
1957 
1958             }
1959         }
1960     }
1961 
createSafeResultListener( @onNull Consumer<Bundle> consumer)1962     private SafeResultListener createSafeResultListener(
1963             @NonNull Consumer<Bundle> consumer) {
1964         synchronized (this) {
1965             final SafeResultListener listener = new SafeResultListener(consumer, this);
1966             mRemoteCallbacks.put(listener, consumer);
1967             return listener;
1968         }
1969     }
1970 
removeSafeResultListener(@onNull SafeResultListener listener)1971     private Consumer<Bundle> removeSafeResultListener(@NonNull SafeResultListener listener) {
1972         synchronized (this) {
1973             return mRemoteCallbacks.remove(listener);
1974         }
1975     }
1976 
1977     /**
1978      * Represents assist state captured when this session was started.
1979      * It contains the various assist data objects and a reference to
1980      * the source activity.
1981      */
1982     @Immutable
1983     public static final class AssistState {
1984         private final @NonNull ActivityId mActivityId;
1985         private final int mIndex;
1986         private final int mCount;
1987         private final @Nullable Bundle mData;
1988         private final @Nullable AssistStructure mStructure;
1989         private final @Nullable AssistContent mContent;
1990 
AssistState(@onNull ActivityId activityId, @Nullable Bundle data, @Nullable AssistStructure structure, @Nullable AssistContent content, int index, int count)1991         AssistState(@NonNull ActivityId activityId, @Nullable Bundle data,
1992                 @Nullable AssistStructure structure, @Nullable AssistContent content,
1993                 int index, int count) {
1994             mActivityId = activityId;
1995             mIndex = index;
1996             mCount = count;
1997             mData = data;
1998             mStructure = structure;
1999             mContent = content;
2000         }
2001 
2002         /**
2003          * @return whether the source activity is focused.
2004          */
isFocused()2005         public boolean isFocused() {
2006             return mIndex == 0;
2007         }
2008 
2009         /**
2010          * @return the index of the activity that this state is for or -1
2011          *     if there was no assist data captured.
2012          */
getIndex()2013         public @IntRange(from = -1) int getIndex() {
2014             return mIndex;
2015         }
2016 
2017         /**
2018          * @return the total number of activities for which the assist data is
2019          * being returned.
2020          */
getCount()2021         public @IntRange(from = 0) int getCount() {
2022             return mCount;
2023         }
2024 
2025         /**
2026          * @return the id of the source activity
2027          */
getActivityId()2028         public @NonNull ActivityId getActivityId() {
2029             return mActivityId;
2030         }
2031 
2032         /**
2033          * @return Arbitrary data supplied by the app through
2034          * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}.
2035          * May be null if assist data has been disabled by the user or device policy; will be null
2036          * if the original show request did not specify {@link #SHOW_WITH_ASSIST}.
2037          */
getAssistData()2038         public @Nullable Bundle getAssistData() {
2039             return mData;
2040         }
2041 
2042         /**
2043          * @return If available, the structure definition of all windows currently
2044          * displayed by the app. May be null if assist data has been disabled by the user
2045          * or device policy; will be null if the original show request did not specify
2046          * {@link #SHOW_WITH_ASSIST}; will be an empty stub if the application has disabled assist
2047          * by marking its window as secure.
2048          */
getAssistStructure()2049         public @Nullable AssistStructure getAssistStructure() {
2050             return mStructure;
2051         }
2052 
2053         /**
2054          * @return Additional content data supplied by the app through
2055          * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}.
2056          * May be null if assist data has been disabled by the user or device policy; will be null
2057          * if the original show request did not specify {@link #SHOW_WITH_ASSIST}. Will not be
2058          * automatically filled in with data from the app if the app has marked its window as
2059          * secure.
2060          */
getAssistContent()2061         public @Nullable AssistContent getAssistContent() {
2062             return mContent;
2063         }
2064     }
2065 
2066     /**
2067      * Represents the id of an assist source activity. You can use
2068      * {@link #equals(Object)} to compare instances of this class.
2069      */
2070     public static class ActivityId {
2071         private final int mTaskId;
2072         private final IBinder mAssistToken;
2073 
ActivityId(int taskId, IBinder assistToken)2074         ActivityId(int taskId, IBinder assistToken) {
2075             mTaskId = taskId;
2076             mAssistToken = assistToken;
2077         }
2078 
getTaskId()2079         int getTaskId() {
2080             return mTaskId;
2081         }
2082 
getAssistToken()2083         IBinder getAssistToken() {
2084             return mAssistToken;
2085         }
2086 
2087         @Override
equals(@ullable Object o)2088         public boolean equals(@Nullable Object o) {
2089             if (this == o) {
2090                 return true;
2091             }
2092             if (o == null || getClass() != o.getClass()) {
2093                 return false;
2094             }
2095 
2096             ActivityId that = (ActivityId) o;
2097 
2098             if (mTaskId != that.mTaskId) {
2099                 return false;
2100             }
2101             return mAssistToken != null
2102                     ? mAssistToken.equals(that.mAssistToken)
2103                     : that.mAssistToken == null;
2104         }
2105 
2106         @Override
hashCode()2107         public int hashCode() {
2108             int result = mTaskId;
2109             result = 31 * result + (mAssistToken != null ? mAssistToken.hashCode() : 0);
2110             return result;
2111         }
2112     }
2113 
2114     private static class SafeResultListener implements RemoteCallback.OnResultListener {
2115         private final @NonNull WeakReference<VoiceInteractionSession> mWeakSession;
2116 
SafeResultListener(@onNull Consumer<Bundle> action, @NonNull VoiceInteractionSession session)2117         SafeResultListener(@NonNull Consumer<Bundle> action,
2118                 @NonNull VoiceInteractionSession session) {
2119             mWeakSession = new WeakReference<>(session);
2120         }
2121 
2122         @Override
onResult(Bundle result)2123         public void onResult(Bundle result) {
2124             final VoiceInteractionSession session = mWeakSession.get();
2125             if (session != null) {
2126                 final Consumer<Bundle> consumer = session.removeSafeResultListener(this);
2127                 if (consumer != null) {
2128                     consumer.accept(result);
2129                 }
2130             }
2131         }
2132     }
2133 }
2134