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