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