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