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