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 android.annotation.Nullable;
20 import android.app.Activity;
21 import android.app.Dialog;
22 import android.app.Instrumentation;
23 import android.app.VoiceInteractor;
24 import android.app.assist.AssistContent;
25 import android.app.assist.AssistStructure;
26 import android.content.ComponentCallbacks2;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.res.Configuration;
30 import android.content.res.TypedArray;
31 import android.graphics.Bitmap;
32 import android.graphics.Rect;
33 import android.graphics.Region;
34 import android.inputmethodservice.SoftInputWindow;
35 import android.os.Binder;
36 import android.os.Bundle;
37 import android.os.Handler;
38 import android.os.IBinder;
39 import android.os.Message;
40 import android.os.RemoteException;
41 import android.os.UserHandle;
42 import android.util.ArrayMap;
43 import android.util.DebugUtils;
44 import android.util.Log;
45 import android.view.Gravity;
46 import android.view.KeyEvent;
47 import android.view.LayoutInflater;
48 import android.view.View;
49 import android.view.ViewGroup;
50 import android.view.ViewTreeObserver;
51 import android.view.WindowManager;
52 import android.widget.FrameLayout;
53 
54 import com.android.internal.app.IVoiceInteractionManagerService;
55 import com.android.internal.app.IVoiceInteractionSessionShowCallback;
56 import com.android.internal.app.IVoiceInteractor;
57 import com.android.internal.app.IVoiceInteractorCallback;
58 import com.android.internal.app.IVoiceInteractorRequest;
59 import com.android.internal.os.HandlerCaller;
60 import com.android.internal.os.SomeArgs;
61 
62 import java.io.FileDescriptor;
63 import java.io.PrintWriter;
64 import java.lang.ref.WeakReference;
65 
66 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
67 
68 /**
69  * An active voice interaction session, providing a facility for the implementation
70  * to interact with the user in the voice interaction layer.  The user interface is
71  * initially shown by default, and can be created be overriding {@link #onCreateContentView()}
72  * in which the UI can be built.
73  *
74  * <p>A voice interaction session can be self-contained, ultimately calling {@link #finish}
75  * when done.  It can also initiate voice interactions with applications by calling
76  * {@link #startVoiceActivity}</p>.
77  */
78 public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCallbacks2 {
79     static final String TAG = "VoiceInteractionSession";
80     static final boolean DEBUG = false;
81 
82     /**
83      * Flag received in {@link #onShow}: originator requested that the session be started with
84      * assist data from the currently focused activity.
85      */
86     public static final int SHOW_WITH_ASSIST = 1<<0;
87 
88     /**
89      * Flag received in {@link #onShow}: originator requested that the session be started with
90      * a screen shot of the currently focused activity.
91      */
92     public static final int SHOW_WITH_SCREENSHOT = 1<<1;
93 
94     /**
95      * Flag for use with {@link #onShow}: indicates that the session has been started from the
96      * system assist gesture.
97      */
98     public static final int SHOW_SOURCE_ASSIST_GESTURE = 1<<2;
99 
100     /**
101      * Flag for use with {@link #onShow}: indicates that the application itself has invoked
102      * the assistant.
103      */
104     public static final int SHOW_SOURCE_APPLICATION = 1<<3;
105 
106     /**
107      * Flag for use with {@link #onShow}: indicates that an Activity has invoked the voice
108      * interaction service for a local interaction using
109      * {@link Activity#startLocalVoiceInteraction(Bundle)}.
110      */
111     public static final int SHOW_SOURCE_ACTIVITY = 1<<4;
112 
113     // Keys for Bundle values
114     /** @hide */
115     public static final String KEY_DATA = "data";
116     /** @hide */
117     public static final String KEY_STRUCTURE = "structure";
118     /** @hide */
119     public static final String KEY_CONTENT = "content";
120     /** @hide */
121     public static final String KEY_RECEIVER_EXTRAS = "receiverExtras";
122 
123     final Context mContext;
124     final HandlerCaller mHandlerCaller;
125 
126     final KeyEvent.DispatcherState mDispatcherState = new KeyEvent.DispatcherState();
127 
128     IVoiceInteractionManagerService mSystemService;
129     IBinder mToken;
130 
131     int mTheme = 0;
132     LayoutInflater mInflater;
133     TypedArray mThemeAttrs;
134     View mRootView;
135     FrameLayout mContentFrame;
136     SoftInputWindow mWindow;
137 
138     boolean mUiEnabled = true;
139     boolean mInitialized;
140     boolean mWindowAdded;
141     boolean mWindowVisible;
142     boolean mWindowWasVisible;
143     boolean mInShowWindow;
144 
145     final ArrayMap<IBinder, Request> mActiveRequests = new ArrayMap<IBinder, Request>();
146 
147     final Insets mTmpInsets = new Insets();
148 
149     final WeakReference<VoiceInteractionSession> mWeakRef
150             = new WeakReference<VoiceInteractionSession>(this);
151 
152     final IVoiceInteractor mInteractor = new IVoiceInteractor.Stub() {
153         @Override
154         public IVoiceInteractorRequest startConfirmation(String callingPackage,
155                 IVoiceInteractorCallback callback, VoiceInteractor.Prompt prompt, Bundle extras) {
156             ConfirmationRequest request = new ConfirmationRequest(callingPackage,
157                     Binder.getCallingUid(), callback, VoiceInteractionSession.this,
158                     prompt, extras);
159             addRequest(request);
160             mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_CONFIRMATION,
161                     request));
162             return request.mInterface;
163         }
164 
165         @Override
166         public IVoiceInteractorRequest startPickOption(String callingPackage,
167                 IVoiceInteractorCallback callback, VoiceInteractor.Prompt prompt,
168                 VoiceInteractor.PickOptionRequest.Option[] options, Bundle extras) {
169             PickOptionRequest request = new PickOptionRequest(callingPackage,
170                     Binder.getCallingUid(), callback, VoiceInteractionSession.this,
171                     prompt, options, extras);
172             addRequest(request);
173             mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_PICK_OPTION,
174                     request));
175             return request.mInterface;
176         }
177 
178         @Override
179         public IVoiceInteractorRequest startCompleteVoice(String callingPackage,
180                 IVoiceInteractorCallback callback, VoiceInteractor.Prompt message, Bundle extras) {
181             CompleteVoiceRequest request = new CompleteVoiceRequest(callingPackage,
182                     Binder.getCallingUid(), callback, VoiceInteractionSession.this,
183                     message, extras);
184             addRequest(request);
185             mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_COMPLETE_VOICE,
186                     request));
187             return request.mInterface;
188         }
189 
190         @Override
191         public IVoiceInteractorRequest startAbortVoice(String callingPackage,
192                 IVoiceInteractorCallback callback, VoiceInteractor.Prompt message, Bundle extras) {
193             AbortVoiceRequest request = new AbortVoiceRequest(callingPackage,
194                     Binder.getCallingUid(), callback, VoiceInteractionSession.this,
195                     message, extras);
196             addRequest(request);
197             mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_ABORT_VOICE,
198                     request));
199             return request.mInterface;
200         }
201 
202         @Override
203         public IVoiceInteractorRequest startCommand(String callingPackage,
204                 IVoiceInteractorCallback callback, String command, Bundle extras) {
205             CommandRequest request = new CommandRequest(callingPackage,
206                     Binder.getCallingUid(), callback, VoiceInteractionSession.this,
207                     command, extras);
208             addRequest(request);
209             mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_START_COMMAND,
210                     request));
211             return request.mInterface;
212         }
213 
214         @Override
215         public boolean[] supportsCommands(String callingPackage, String[] commands) {
216             Message msg = mHandlerCaller.obtainMessageIOO(MSG_SUPPORTS_COMMANDS,
217                     0, commands, null);
218             SomeArgs args = mHandlerCaller.sendMessageAndWait(msg);
219             if (args != null) {
220                 boolean[] res = (boolean[])args.arg1;
221                 args.recycle();
222                 return res;
223             }
224             return new boolean[commands.length];
225         }
226     };
227 
228     final IVoiceInteractionSession mSession = new IVoiceInteractionSession.Stub() {
229         @Override
230         public void show(Bundle sessionArgs, int flags,
231                 IVoiceInteractionSessionShowCallback showCallback) {
232             mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIOO(MSG_SHOW,
233                     flags, sessionArgs, showCallback));
234         }
235 
236         @Override
237         public void hide() {
238             // Remove any pending messages to show the session
239             mHandlerCaller.removeMessages(MSG_SHOW);
240             mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_HIDE));
241         }
242 
243         @Override
244         public void handleAssist(final Bundle data, final AssistStructure structure,
245                 final AssistContent content, final int index, final int count) {
246             // We want to pre-warm the AssistStructure before handing it off to the main
247             // thread.  We also want to do this on a separate thread, so that if the app
248             // is for some reason slow (due to slow filling in of async children in the
249             // structure), we don't block other incoming IPCs (such as the screenshot) to
250             // us (since we are a oneway interface, they get serialized).  (Okay?)
251             Thread retriever = new Thread("AssistStructure retriever") {
252                 @Override
253                 public void run() {
254                     Throwable failure = null;
255                     if (structure != null) {
256                         try {
257                             structure.ensureData();
258                         } catch (Throwable e) {
259                             Log.w(TAG, "Failure retrieving AssistStructure", e);
260                             failure = e;
261                         }
262                     }
263                     mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageOOOOII(MSG_HANDLE_ASSIST,
264                             data, failure == null ? structure : null, failure, content,
265                             index, count));
266                 }
267             };
268             retriever.start();
269         }
270 
271         @Override
272         public void handleScreenshot(Bitmap screenshot) {
273             mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageO(MSG_HANDLE_SCREENSHOT,
274                     screenshot));
275         }
276 
277         @Override
278         public void taskStarted(Intent intent, int taskId) {
279             mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIO(MSG_TASK_STARTED,
280                     taskId, intent));
281         }
282 
283         @Override
284         public void taskFinished(Intent intent, int taskId) {
285             mHandlerCaller.sendMessage(mHandlerCaller.obtainMessageIO(MSG_TASK_FINISHED,
286                     taskId, intent));
287         }
288 
289         @Override
290         public void closeSystemDialogs() {
291             mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_CLOSE_SYSTEM_DIALOGS));
292         }
293 
294         @Override
295         public void onLockscreenShown() {
296             mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_ON_LOCKSCREEN_SHOWN));
297         }
298 
299         @Override
300         public void destroy() {
301             mHandlerCaller.sendMessage(mHandlerCaller.obtainMessage(MSG_DESTROY));
302         }
303     };
304 
305     /**
306      * Base class representing a request from a voice-driver app to perform a particular
307      * voice operation with the user.  See related subclasses for the types of requests
308      * that are possible.
309      */
310     public static class Request {
311         final IVoiceInteractorRequest mInterface = new IVoiceInteractorRequest.Stub() {
312             @Override
313             public void cancel() throws RemoteException {
314                 VoiceInteractionSession session = mSession.get();
315                 if (session != null) {
316                     session.mHandlerCaller.sendMessage(
317                             session.mHandlerCaller.obtainMessageO(MSG_CANCEL, Request.this));
318                 }
319             }
320         };
321         final String mCallingPackage;
322         final int mCallingUid;
323         final IVoiceInteractorCallback mCallback;
324         final WeakReference<VoiceInteractionSession> mSession;
325         final Bundle mExtras;
326 
Request(String packageName, int uid, IVoiceInteractorCallback callback, VoiceInteractionSession session, Bundle extras)327         Request(String packageName, int uid, IVoiceInteractorCallback callback,
328                 VoiceInteractionSession session, Bundle extras) {
329             mCallingPackage = packageName;
330             mCallingUid = uid;
331             mCallback = callback;
332             mSession = session.mWeakRef;
333             mExtras = extras;
334         }
335 
336         /**
337          * Return the uid of the application that initiated the request.
338          */
getCallingUid()339         public int getCallingUid() {
340             return mCallingUid;
341         }
342 
343         /**
344          * Return the package name of the application that initiated the request.
345          */
getCallingPackage()346         public String getCallingPackage() {
347             return mCallingPackage;
348         }
349 
350         /**
351          * Return any additional extra information that was supplied as part of the request.
352          */
getExtras()353         public Bundle getExtras() {
354             return mExtras;
355         }
356 
357         /**
358          * Check whether this request is currently active.  A request becomes inactive after
359          * calling {@link #cancel} or a final result method that completes the request.  After
360          * this point, further interactions with the request will result in
361          * {@link java.lang.IllegalStateException} errors; you should not catch these errors,
362          * but can use this method if you need to determine the state of the request.  Returns
363          * true if the request is still active.
364          */
isActive()365         public boolean isActive() {
366             VoiceInteractionSession session = mSession.get();
367             if (session == null) {
368                 return false;
369             }
370             return session.isRequestActive(mInterface.asBinder());
371         }
372 
finishRequest()373         void finishRequest() {
374             VoiceInteractionSession session = mSession.get();
375             if (session == null) {
376                 throw new IllegalStateException("VoiceInteractionSession has been destroyed");
377             }
378             Request req = session.removeRequest(mInterface.asBinder());
379             if (req == null) {
380                 throw new IllegalStateException("Request not active: " + this);
381             } else if (req != this) {
382                 throw new IllegalStateException("Current active request " + req
383                         + " not same as calling request " + this);
384             }
385         }
386 
387         /**
388          * Ask the app to cancel this current request.
389          * This also finishes the request (it is no longer active).
390          */
cancel()391         public void cancel() {
392             try {
393                 if (DEBUG) Log.d(TAG, "sendCancelResult: req=" + mInterface);
394                 finishRequest();
395                 mCallback.deliverCancel(mInterface);
396             } catch (RemoteException e) {
397             }
398         }
399 
400         @Override
toString()401         public String toString() {
402             StringBuilder sb = new StringBuilder(128);
403             DebugUtils.buildShortClassTag(this, sb);
404             sb.append(" ");
405             sb.append(mInterface.asBinder());
406             sb.append(" pkg=");
407             sb.append(mCallingPackage);
408             sb.append(" uid=");
409             UserHandle.formatUid(sb, mCallingUid);
410             sb.append('}');
411             return sb.toString();
412         }
413 
dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)414         void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
415             writer.print(prefix); writer.print("mInterface=");
416             writer.println(mInterface.asBinder());
417             writer.print(prefix); writer.print("mCallingPackage="); writer.print(mCallingPackage);
418             writer.print(" mCallingUid="); UserHandle.formatUid(writer, mCallingUid);
419             writer.println();
420             writer.print(prefix); writer.print("mCallback=");
421             writer.println(mCallback.asBinder());
422             if (mExtras != null) {
423                 writer.print(prefix); writer.print("mExtras=");
424                 writer.println(mExtras);
425             }
426         }
427     }
428 
429     /**
430      * A request for confirmation from the user of an operation, as per
431      * {@link android.app.VoiceInteractor.ConfirmationRequest
432      * VoiceInteractor.ConfirmationRequest}.
433      */
434     public static final class ConfirmationRequest extends Request {
435         final VoiceInteractor.Prompt mPrompt;
436 
ConfirmationRequest(String packageName, int uid, IVoiceInteractorCallback callback, VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras)437         ConfirmationRequest(String packageName, int uid, IVoiceInteractorCallback callback,
438                 VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras) {
439             super(packageName, uid, callback, session, extras);
440             mPrompt = prompt;
441         }
442 
443         /**
444          * Return the prompt informing the user of what will happen, as per
445          * {@link android.app.VoiceInteractor.ConfirmationRequest
446          * VoiceInteractor.ConfirmationRequest}.
447          */
448         @Nullable
getVoicePrompt()449         public VoiceInteractor.Prompt getVoicePrompt() {
450             return mPrompt;
451         }
452 
453         /**
454          * Return the prompt informing the user of what will happen, as per
455          * {@link android.app.VoiceInteractor.ConfirmationRequest
456          * VoiceInteractor.ConfirmationRequest}.
457          * @deprecated Prefer {@link #getVoicePrompt()} which allows multiple voice prompts.
458          */
459         @Deprecated
460         @Nullable
getPrompt()461         public CharSequence getPrompt() {
462             return (mPrompt != null ? mPrompt.getVoicePromptAt(0) : null);
463         }
464 
465         /**
466          * Report that the voice interactor has confirmed the operation with the user, resulting
467          * in a call to
468          * {@link android.app.VoiceInteractor.ConfirmationRequest#onConfirmationResult
469          * VoiceInteractor.ConfirmationRequest.onConfirmationResult}.
470          * This finishes the request (it is no longer active).
471          */
sendConfirmationResult(boolean confirmed, Bundle result)472         public void sendConfirmationResult(boolean confirmed, Bundle result) {
473             try {
474                 if (DEBUG) Log.d(TAG, "sendConfirmationResult: req=" + mInterface
475                         + " confirmed=" + confirmed + " result=" + result);
476                 finishRequest();
477                 mCallback.deliverConfirmationResult(mInterface, confirmed, result);
478             } catch (RemoteException e) {
479             }
480         }
481 
dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)482         void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
483             super.dump(prefix, fd, writer, args);
484             writer.print(prefix); writer.print("mPrompt=");
485             writer.println(mPrompt);
486         }
487     }
488 
489     /**
490      * A request for the user to pick from a set of option, as per
491      * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
492      */
493     public static final class PickOptionRequest extends Request {
494         final VoiceInteractor.Prompt mPrompt;
495         final VoiceInteractor.PickOptionRequest.Option[] mOptions;
496 
PickOptionRequest(String packageName, int uid, IVoiceInteractorCallback callback, VoiceInteractionSession session, VoiceInteractor.Prompt prompt, VoiceInteractor.PickOptionRequest.Option[] options, Bundle extras)497         PickOptionRequest(String packageName, int uid, IVoiceInteractorCallback callback,
498                 VoiceInteractionSession session, VoiceInteractor.Prompt prompt,
499                 VoiceInteractor.PickOptionRequest.Option[] options, Bundle extras) {
500             super(packageName, uid, callback, session, extras);
501             mPrompt = prompt;
502             mOptions = options;
503         }
504 
505         /**
506          * Return the prompt informing the user of what they are picking, as per
507          * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
508          */
509         @Nullable
getVoicePrompt()510         public VoiceInteractor.Prompt getVoicePrompt() {
511             return mPrompt;
512         }
513 
514         /**
515          * Return the prompt informing the user of what they are picking, as per
516          * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
517          * @deprecated Prefer {@link #getVoicePrompt()} which allows multiple voice prompts.
518          */
519         @Deprecated
520         @Nullable
getPrompt()521         public CharSequence getPrompt() {
522             return (mPrompt != null ? mPrompt.getVoicePromptAt(0) : null);
523         }
524 
525         /**
526          * Return the set of options the user is picking from, as per
527          * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
528          */
getOptions()529         public VoiceInteractor.PickOptionRequest.Option[] getOptions() {
530             return mOptions;
531         }
532 
sendPickOptionResult(boolean finished, VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result)533         void sendPickOptionResult(boolean finished,
534                 VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) {
535             try {
536                 if (DEBUG) Log.d(TAG, "sendPickOptionResult: req=" + mInterface
537                         + " finished=" + finished + " selections=" + selections
538                         + " result=" + result);
539                 if (finished) {
540                     finishRequest();
541                 }
542                 mCallback.deliverPickOptionResult(mInterface, finished, selections, result);
543             } catch (RemoteException e) {
544             }
545         }
546 
547         /**
548          * Report an intermediate option selection from the request, without completing it (the
549          * request is still active and the app is waiting for the final option selection),
550          * resulting in a call to
551          * {@link android.app.VoiceInteractor.PickOptionRequest#onPickOptionResult
552          * VoiceInteractor.PickOptionRequest.onPickOptionResult} with false for finished.
553          */
sendIntermediatePickOptionResult( VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result)554         public void sendIntermediatePickOptionResult(
555                 VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) {
556             sendPickOptionResult(false, selections, result);
557         }
558 
559         /**
560          * Report the final option selection for the request, completing the request
561          * and resulting in a call to
562          * {@link android.app.VoiceInteractor.PickOptionRequest#onPickOptionResult
563          * VoiceInteractor.PickOptionRequest.onPickOptionResult} with false for finished.
564          * This finishes the request (it is no longer active).
565          */
sendPickOptionResult( VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result)566         public void sendPickOptionResult(
567                 VoiceInteractor.PickOptionRequest.Option[] selections, Bundle result) {
568             sendPickOptionResult(true, selections, result);
569         }
570 
dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)571         void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
572             super.dump(prefix, fd, writer, args);
573             writer.print(prefix); writer.print("mPrompt=");
574             writer.println(mPrompt);
575             if (mOptions != null) {
576                 writer.print(prefix); writer.println("Options:");
577                 for (int i=0; i<mOptions.length; i++) {
578                     VoiceInteractor.PickOptionRequest.Option op = mOptions[i];
579                     writer.print(prefix); writer.print("  #"); writer.print(i); writer.println(":");
580                     writer.print(prefix); writer.print("    mLabel=");
581                     writer.println(op.getLabel());
582                     writer.print(prefix); writer.print("    mIndex=");
583                     writer.println(op.getIndex());
584                     if (op.countSynonyms() > 0) {
585                         writer.print(prefix); writer.println("    Synonyms:");
586                         for (int j=0; j<op.countSynonyms(); j++) {
587                             writer.print(prefix); writer.print("      #"); writer.print(j);
588                             writer.print(": "); writer.println(op.getSynonymAt(j));
589                         }
590                     }
591                     if (op.getExtras() != null) {
592                         writer.print(prefix); writer.print("    mExtras=");
593                         writer.println(op.getExtras());
594                     }
595                 }
596             }
597         }
598     }
599 
600     /**
601      * A request to simply inform the user that the voice operation has completed, as per
602      * {@link android.app.VoiceInteractor.CompleteVoiceRequest
603      * VoiceInteractor.CompleteVoiceRequest}.
604      */
605     public static final class CompleteVoiceRequest extends Request {
606         final VoiceInteractor.Prompt mPrompt;
607 
CompleteVoiceRequest(String packageName, int uid, IVoiceInteractorCallback callback, VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras)608         CompleteVoiceRequest(String packageName, int uid, IVoiceInteractorCallback callback,
609                 VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras) {
610             super(packageName, uid, callback, session, extras);
611             mPrompt = prompt;
612         }
613 
614         /**
615          * Return the message informing the user of the completion, as per
616          * {@link android.app.VoiceInteractor.CompleteVoiceRequest
617          * VoiceInteractor.CompleteVoiceRequest}.
618          */
619         @Nullable
getVoicePrompt()620         public VoiceInteractor.Prompt getVoicePrompt() {
621             return mPrompt;
622         }
623 
624         /**
625          * Return the message informing the user of the completion, as per
626          * {@link android.app.VoiceInteractor.CompleteVoiceRequest
627          * VoiceInteractor.CompleteVoiceRequest}.
628          * @deprecated Prefer {@link #getVoicePrompt()} which allows a separate visual message.
629          */
630         @Deprecated
631         @Nullable
getMessage()632         public CharSequence getMessage() {
633             return (mPrompt != null ? mPrompt.getVoicePromptAt(0) : null);
634         }
635 
636         /**
637          * Report that the voice interactor has finished completing the voice operation, resulting
638          * in a call to
639          * {@link android.app.VoiceInteractor.CompleteVoiceRequest#onCompleteResult
640          * VoiceInteractor.CompleteVoiceRequest.onCompleteResult}.
641          * This finishes the request (it is no longer active).
642          */
sendCompleteResult(Bundle result)643         public void sendCompleteResult(Bundle result) {
644             try {
645                 if (DEBUG) Log.d(TAG, "sendCompleteVoiceResult: req=" + mInterface
646                         + " result=" + result);
647                 finishRequest();
648                 mCallback.deliverCompleteVoiceResult(mInterface, result);
649             } catch (RemoteException e) {
650             }
651         }
652 
dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)653         void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
654             super.dump(prefix, fd, writer, args);
655             writer.print(prefix); writer.print("mPrompt=");
656             writer.println(mPrompt);
657         }
658     }
659 
660     /**
661      * A request to report that the current user interaction can not be completed with voice, as per
662      * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}.
663      */
664     public static final class AbortVoiceRequest extends Request {
665         final VoiceInteractor.Prompt mPrompt;
666 
AbortVoiceRequest(String packageName, int uid, IVoiceInteractorCallback callback, VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras)667         AbortVoiceRequest(String packageName, int uid, IVoiceInteractorCallback callback,
668                 VoiceInteractionSession session, VoiceInteractor.Prompt prompt, Bundle extras) {
669             super(packageName, uid, callback, session, extras);
670             mPrompt = prompt;
671         }
672 
673         /**
674          * Return the message informing the user of the problem, as per
675          * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}.
676          */
677         @Nullable
getVoicePrompt()678         public VoiceInteractor.Prompt getVoicePrompt() {
679             return mPrompt;
680         }
681 
682         /**
683          * Return the message informing the user of the problem, as per
684          * {@link android.app.VoiceInteractor.AbortVoiceRequest VoiceInteractor.AbortVoiceRequest}.
685          * @deprecated Prefer {@link #getVoicePrompt()} which allows a separate visual message.
686          */
687         @Deprecated
688         @Nullable
getMessage()689         public CharSequence getMessage() {
690             return (mPrompt != null ? mPrompt.getVoicePromptAt(0) : null);
691         }
692 
693         /**
694          * Report that the voice interactor has finished aborting the voice operation, resulting
695          * in a call to
696          * {@link android.app.VoiceInteractor.AbortVoiceRequest#onAbortResult
697          * VoiceInteractor.AbortVoiceRequest.onAbortResult}.  This finishes the request (it
698          * is no longer active).
699          */
sendAbortResult(Bundle result)700         public void sendAbortResult(Bundle result) {
701             try {
702                 if (DEBUG) Log.d(TAG, "sendConfirmResult: req=" + mInterface
703                         + " result=" + result);
704                 finishRequest();
705                 mCallback.deliverAbortVoiceResult(mInterface, result);
706             } catch (RemoteException e) {
707             }
708         }
709 
dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)710         void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
711             super.dump(prefix, fd, writer, args);
712             writer.print(prefix); writer.print("mPrompt=");
713             writer.println(mPrompt);
714         }
715     }
716 
717     /**
718      * A generic vendor-specific request, as per
719      * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}.
720      */
721     public static final class CommandRequest extends Request {
722         final String mCommand;
723 
CommandRequest(String packageName, int uid, IVoiceInteractorCallback callback, VoiceInteractionSession session, String command, Bundle extras)724         CommandRequest(String packageName, int uid, IVoiceInteractorCallback callback,
725                 VoiceInteractionSession session, String command, Bundle extras) {
726             super(packageName, uid, callback, session, extras);
727             mCommand = command;
728         }
729 
730         /**
731          * Return the command that is being executed, as per
732          * {@link android.app.VoiceInteractor.CommandRequest VoiceInteractor.CommandRequest}.
733          */
getCommand()734         public String getCommand() {
735             return mCommand;
736         }
737 
sendCommandResult(boolean finished, Bundle result)738         void sendCommandResult(boolean finished, Bundle result) {
739             try {
740                 if (DEBUG) Log.d(TAG, "sendCommandResult: req=" + mInterface
741                         + " result=" + result);
742                 if (finished) {
743                     finishRequest();
744                 }
745                 mCallback.deliverCommandResult(mInterface, finished, result);
746             } catch (RemoteException e) {
747             }
748         }
749 
750         /**
751          * Report an intermediate result of the request, without completing it (the request
752          * is still active and the app is waiting for the final result), resulting in a call to
753          * {@link android.app.VoiceInteractor.CommandRequest#onCommandResult
754          * VoiceInteractor.CommandRequest.onCommandResult} with false for isCompleted.
755          */
sendIntermediateResult(Bundle result)756         public void sendIntermediateResult(Bundle result) {
757             sendCommandResult(false, result);
758         }
759 
760         /**
761          * Report the final result of the request, completing the request and resulting in a call to
762          * {@link android.app.VoiceInteractor.CommandRequest#onCommandResult
763          * VoiceInteractor.CommandRequest.onCommandResult} with true for isCompleted.
764          * This finishes the request (it is no longer active).
765          */
sendResult(Bundle result)766         public void sendResult(Bundle result) {
767             sendCommandResult(true, result);
768         }
769 
dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)770         void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
771             super.dump(prefix, fd, writer, args);
772             writer.print(prefix); writer.print("mCommand=");
773             writer.println(mCommand);
774         }
775     }
776 
777     static final int MSG_START_CONFIRMATION = 1;
778     static final int MSG_START_PICK_OPTION = 2;
779     static final int MSG_START_COMPLETE_VOICE = 3;
780     static final int MSG_START_ABORT_VOICE = 4;
781     static final int MSG_START_COMMAND = 5;
782     static final int MSG_SUPPORTS_COMMANDS = 6;
783     static final int MSG_CANCEL = 7;
784 
785     static final int MSG_TASK_STARTED = 100;
786     static final int MSG_TASK_FINISHED = 101;
787     static final int MSG_CLOSE_SYSTEM_DIALOGS = 102;
788     static final int MSG_DESTROY = 103;
789     static final int MSG_HANDLE_ASSIST = 104;
790     static final int MSG_HANDLE_SCREENSHOT = 105;
791     static final int MSG_SHOW = 106;
792     static final int MSG_HIDE = 107;
793     static final int MSG_ON_LOCKSCREEN_SHOWN = 108;
794 
795     class MyCallbacks implements HandlerCaller.Callback, SoftInputWindow.Callback {
796         @Override
executeMessage(Message msg)797         public void executeMessage(Message msg) {
798             SomeArgs args = null;
799             switch (msg.what) {
800                 case MSG_START_CONFIRMATION:
801                     if (DEBUG) Log.d(TAG, "onConfirm: req=" + msg.obj);
802                     onRequestConfirmation((ConfirmationRequest) msg.obj);
803                     break;
804                 case MSG_START_PICK_OPTION:
805                     if (DEBUG) Log.d(TAG, "onPickOption: req=" + msg.obj);
806                     onRequestPickOption((PickOptionRequest) msg.obj);
807                     break;
808                 case MSG_START_COMPLETE_VOICE:
809                     if (DEBUG) Log.d(TAG, "onCompleteVoice: req=" + msg.obj);
810                     onRequestCompleteVoice((CompleteVoiceRequest) msg.obj);
811                     break;
812                 case MSG_START_ABORT_VOICE:
813                     if (DEBUG) Log.d(TAG, "onAbortVoice: req=" + msg.obj);
814                     onRequestAbortVoice((AbortVoiceRequest) msg.obj);
815                     break;
816                 case MSG_START_COMMAND:
817                     if (DEBUG) Log.d(TAG, "onCommand: req=" + msg.obj);
818                     onRequestCommand((CommandRequest) msg.obj);
819                     break;
820                 case MSG_SUPPORTS_COMMANDS:
821                     args = (SomeArgs)msg.obj;
822                     if (DEBUG) Log.d(TAG, "onGetSupportedCommands: cmds=" + args.arg1);
823                     args.arg1 = onGetSupportedCommands((String[]) args.arg1);
824                     args.complete();
825                     args = null;
826                     break;
827                 case MSG_CANCEL:
828                     if (DEBUG) Log.d(TAG, "onCancel: req=" + ((Request)msg.obj));
829                     onCancelRequest((Request) msg.obj);
830                     break;
831                 case MSG_TASK_STARTED:
832                     if (DEBUG) Log.d(TAG, "onTaskStarted: intent=" + msg.obj
833                             + " taskId=" + msg.arg1);
834                     onTaskStarted((Intent) msg.obj, msg.arg1);
835                     break;
836                 case MSG_TASK_FINISHED:
837                     if (DEBUG) Log.d(TAG, "onTaskFinished: intent=" + msg.obj
838                             + " taskId=" + msg.arg1);
839                     onTaskFinished((Intent) msg.obj, msg.arg1);
840                     break;
841                 case MSG_CLOSE_SYSTEM_DIALOGS:
842                     if (DEBUG) Log.d(TAG, "onCloseSystemDialogs");
843                     onCloseSystemDialogs();
844                     break;
845                 case MSG_DESTROY:
846                     if (DEBUG) Log.d(TAG, "doDestroy");
847                     doDestroy();
848                     break;
849                 case MSG_HANDLE_ASSIST:
850                     args = (SomeArgs)msg.obj;
851                     if (DEBUG) Log.d(TAG, "onHandleAssist: data=" + args.arg1
852                             + " structure=" + args.arg2 + " content=" + args.arg3
853                             + " activityIndex=" + args.argi5 + " activityCount=" + args.argi6);
854                     if (args.argi5 == 0) {
855                         doOnHandleAssist((Bundle) args.arg1, (AssistStructure) args.arg2,
856                                 (Throwable) args.arg3, (AssistContent) args.arg4);
857                     } else {
858                         doOnHandleAssistSecondary((Bundle) args.arg1, (AssistStructure) args.arg2,
859                                 (Throwable) args.arg3, (AssistContent) args.arg4,
860                                 args.argi5, args.argi6);
861                     }
862                     break;
863                 case MSG_HANDLE_SCREENSHOT:
864                     if (DEBUG) Log.d(TAG, "onHandleScreenshot: " + msg.obj);
865                     onHandleScreenshot((Bitmap) msg.obj);
866                     break;
867                 case MSG_SHOW:
868                     args = (SomeArgs)msg.obj;
869                     if (DEBUG) Log.d(TAG, "doShow: args=" + args.arg1
870                             + " flags=" + msg.arg1
871                             + " showCallback=" + args.arg2);
872                     doShow((Bundle) args.arg1, msg.arg1,
873                             (IVoiceInteractionSessionShowCallback) args.arg2);
874                     break;
875                 case MSG_HIDE:
876                     if (DEBUG) Log.d(TAG, "doHide");
877                     doHide();
878                     break;
879                 case MSG_ON_LOCKSCREEN_SHOWN:
880                     if (DEBUG) Log.d(TAG, "onLockscreenShown");
881                     onLockscreenShown();
882                     break;
883             }
884             if (args != null) {
885                 args.recycle();
886             }
887         }
888 
889         @Override
onBackPressed()890         public void onBackPressed() {
891             VoiceInteractionSession.this.onBackPressed();
892         }
893     }
894 
895     final MyCallbacks mCallbacks = new MyCallbacks();
896 
897     /**
898      * Information about where interesting parts of the input method UI appear.
899      */
900     public static final class Insets {
901         /**
902          * This is the part of the UI that is the main content.  It is
903          * used to determine the basic space needed, to resize/pan the
904          * application behind.  It is assumed that this inset does not
905          * change very much, since any change will cause a full resize/pan
906          * of the application behind.  This value is relative to the top edge
907          * of the input method window.
908          */
909         public final Rect contentInsets = new Rect();
910 
911         /**
912          * This is the region of the UI that is touchable.  It is used when
913          * {@link #touchableInsets} is set to {@link #TOUCHABLE_INSETS_REGION}.
914          * The region should be specified relative to the origin of the window frame.
915          */
916         public final Region touchableRegion = new Region();
917 
918         /**
919          * Option for {@link #touchableInsets}: the entire window frame
920          * can be touched.
921          */
922         public static final int TOUCHABLE_INSETS_FRAME
923                 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_FRAME;
924 
925         /**
926          * Option for {@link #touchableInsets}: the area inside of
927          * the content insets can be touched.
928          */
929         public static final int TOUCHABLE_INSETS_CONTENT
930                 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_CONTENT;
931 
932         /**
933          * Option for {@link #touchableInsets}: the region specified by
934          * {@link #touchableRegion} can be touched.
935          */
936         public static final int TOUCHABLE_INSETS_REGION
937                 = ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION;
938 
939         /**
940          * Determine which area of the window is touchable by the user.  May
941          * be one of: {@link #TOUCHABLE_INSETS_FRAME},
942          * {@link #TOUCHABLE_INSETS_CONTENT}, or {@link #TOUCHABLE_INSETS_REGION}.
943          */
944         public int touchableInsets;
945     }
946 
947     final ViewTreeObserver.OnComputeInternalInsetsListener mInsetsComputer =
948             new ViewTreeObserver.OnComputeInternalInsetsListener() {
949         public void onComputeInternalInsets(ViewTreeObserver.InternalInsetsInfo info) {
950             onComputeInsets(mTmpInsets);
951             info.contentInsets.set(mTmpInsets.contentInsets);
952             info.visibleInsets.set(mTmpInsets.contentInsets);
953             info.touchableRegion.set(mTmpInsets.touchableRegion);
954             info.setTouchableInsets(mTmpInsets.touchableInsets);
955         }
956     };
957 
VoiceInteractionSession(Context context)958     public VoiceInteractionSession(Context context) {
959         this(context, new Handler());
960     }
961 
VoiceInteractionSession(Context context, Handler handler)962     public VoiceInteractionSession(Context context, Handler handler) {
963         mContext = context;
964         mHandlerCaller = new HandlerCaller(context, handler.getLooper(),
965                 mCallbacks, true);
966     }
967 
getContext()968     public Context getContext() {
969         return mContext;
970     }
971 
addRequest(Request req)972     void addRequest(Request req) {
973         synchronized (this) {
974             mActiveRequests.put(req.mInterface.asBinder(), req);
975         }
976     }
977 
isRequestActive(IBinder reqInterface)978     boolean isRequestActive(IBinder reqInterface) {
979         synchronized (this) {
980             return mActiveRequests.containsKey(reqInterface);
981         }
982     }
983 
removeRequest(IBinder reqInterface)984     Request removeRequest(IBinder reqInterface) {
985         synchronized (this) {
986             return mActiveRequests.remove(reqInterface);
987         }
988     }
989 
doCreate(IVoiceInteractionManagerService service, IBinder token)990     void doCreate(IVoiceInteractionManagerService service, IBinder token) {
991         mSystemService = service;
992         mToken = token;
993         onCreate();
994     }
995 
doShow(Bundle args, int flags, final IVoiceInteractionSessionShowCallback showCallback)996     void doShow(Bundle args, int flags, final IVoiceInteractionSessionShowCallback showCallback) {
997         if (DEBUG) Log.v(TAG, "Showing window: mWindowAdded=" + mWindowAdded
998                 + " mWindowVisible=" + mWindowVisible);
999 
1000         if (mInShowWindow) {
1001             Log.w(TAG, "Re-entrance in to showWindow");
1002             return;
1003         }
1004 
1005         try {
1006             mInShowWindow = true;
1007             onPrepareShow(args, flags);
1008             if (!mWindowVisible) {
1009                 ensureWindowAdded();
1010             }
1011             onShow(args, flags);
1012             if (!mWindowVisible) {
1013                 mWindowVisible = true;
1014                 if (mUiEnabled) {
1015                     mWindow.show();
1016                 }
1017             }
1018             if (showCallback != null) {
1019                 if (mUiEnabled) {
1020                     mRootView.invalidate();
1021                     mRootView.getViewTreeObserver().addOnPreDrawListener(
1022                             new ViewTreeObserver.OnPreDrawListener() {
1023                                 @Override
1024                                 public boolean onPreDraw() {
1025                                     mRootView.getViewTreeObserver().removeOnPreDrawListener(this);
1026                                     try {
1027                                         showCallback.onShown();
1028                                     } catch (RemoteException e) {
1029                                         Log.w(TAG, "Error calling onShown", e);
1030                                     }
1031                                     return true;
1032                                 }
1033                             });
1034                 } else {
1035                     try {
1036                         showCallback.onShown();
1037                     } catch (RemoteException e) {
1038                         Log.w(TAG, "Error calling onShown", e);
1039                     }
1040                 }
1041             }
1042         } finally {
1043             mWindowWasVisible = true;
1044             mInShowWindow = false;
1045         }
1046     }
1047 
doHide()1048     void doHide() {
1049         if (mWindowVisible) {
1050             ensureWindowHidden();
1051             mWindowVisible = false;
1052             onHide();
1053         }
1054     }
1055 
doDestroy()1056     void doDestroy() {
1057         onDestroy();
1058         if (mInitialized) {
1059             mRootView.getViewTreeObserver().removeOnComputeInternalInsetsListener(
1060                     mInsetsComputer);
1061             if (mWindowAdded) {
1062                 mWindow.dismiss();
1063                 mWindowAdded = false;
1064             }
1065             mInitialized = false;
1066         }
1067     }
1068 
ensureWindowCreated()1069     void ensureWindowCreated() {
1070         if (mInitialized) {
1071             return;
1072         }
1073 
1074         if (!mUiEnabled) {
1075             throw new IllegalStateException("setUiEnabled is false");
1076         }
1077 
1078         mInitialized = true;
1079         mInflater = (LayoutInflater)mContext.getSystemService(
1080                 Context.LAYOUT_INFLATER_SERVICE);
1081         mWindow = new SoftInputWindow(mContext, "VoiceInteractionSession", mTheme,
1082                 mCallbacks, this, mDispatcherState,
1083                 WindowManager.LayoutParams.TYPE_VOICE_INTERACTION, Gravity.BOTTOM, true);
1084         mWindow.getWindow().addFlags(
1085                 WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED |
1086                         WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN |
1087                         WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR);
1088 
1089         mThemeAttrs = mContext.obtainStyledAttributes(android.R.styleable.VoiceInteractionSession);
1090         mRootView = mInflater.inflate(
1091                 com.android.internal.R.layout.voice_interaction_session, null);
1092         mRootView.setSystemUiVisibility(
1093                 View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
1094                         | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
1095         mWindow.setContentView(mRootView);
1096         mRootView.getViewTreeObserver().addOnComputeInternalInsetsListener(mInsetsComputer);
1097 
1098         mContentFrame = (FrameLayout)mRootView.findViewById(android.R.id.content);
1099 
1100         mWindow.getWindow().setLayout(MATCH_PARENT, MATCH_PARENT);
1101         mWindow.setToken(mToken);
1102     }
1103 
ensureWindowAdded()1104     void ensureWindowAdded() {
1105         if (mUiEnabled && !mWindowAdded) {
1106             mWindowAdded = true;
1107             ensureWindowCreated();
1108             View v = onCreateContentView();
1109             if (v != null) {
1110                 setContentView(v);
1111             }
1112         }
1113     }
1114 
ensureWindowHidden()1115     void ensureWindowHidden() {
1116         if (mWindow != null) {
1117             mWindow.hide();
1118         }
1119     }
1120 
1121     /**
1122      * Equivalent to {@link VoiceInteractionService#setDisabledShowContext
1123      * VoiceInteractionService.setDisabledShowContext(int)}.
1124      */
setDisabledShowContext(int flags)1125     public void setDisabledShowContext(int flags) {
1126         try {
1127             mSystemService.setDisabledShowContext(flags);
1128         } catch (RemoteException e) {
1129         }
1130     }
1131 
1132     /**
1133      * Equivalent to {@link VoiceInteractionService#getDisabledShowContext
1134      * VoiceInteractionService.getDisabledShowContext}.
1135      */
getDisabledShowContext()1136     public int getDisabledShowContext() {
1137         try {
1138             return mSystemService.getDisabledShowContext();
1139         } catch (RemoteException e) {
1140             return 0;
1141         }
1142     }
1143 
1144     /**
1145      * Return which show context flags have been disabled by the user through the system
1146      * settings UI, so the session will never get this data.  Returned flags are any combination of
1147      * {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST} and
1148      * {@link VoiceInteractionSession#SHOW_WITH_SCREENSHOT
1149      * VoiceInteractionSession.SHOW_WITH_SCREENSHOT}.  Note that this only tells you about
1150      * global user settings, not about restrictions that may be applied contextual based on
1151      * the current application the user is in or other transient states.
1152      */
getUserDisabledShowContext()1153     public int getUserDisabledShowContext() {
1154         try {
1155             return mSystemService.getUserDisabledShowContext();
1156         } catch (RemoteException e) {
1157             return 0;
1158         }
1159     }
1160 
1161     /**
1162      * Show the UI for this session.  This asks the system to go through the process of showing
1163      * your UI, which will eventually culminate in {@link #onShow}.  This is similar to calling
1164      * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
1165      * @param args Arbitrary arguments that will be propagated {@link #onShow}.
1166      * @param flags Indicates additional optional behavior that should be performed.  May
1167      * be any combination of
1168      * {@link VoiceInteractionSession#SHOW_WITH_ASSIST VoiceInteractionSession.SHOW_WITH_ASSIST} and
1169      * {@link VoiceInteractionSession#SHOW_WITH_SCREENSHOT
1170      * VoiceInteractionSession.SHOW_WITH_SCREENSHOT}
1171      * to request that the system generate and deliver assist data on the current foreground
1172      * app as part of showing the session UI.
1173      */
show(Bundle args, int flags)1174     public void show(Bundle args, int flags) {
1175         if (mToken == null) {
1176             throw new IllegalStateException("Can't call before onCreate()");
1177         }
1178         try {
1179             mSystemService.showSessionFromSession(mToken, args, flags);
1180         } catch (RemoteException e) {
1181         }
1182     }
1183 
1184     /**
1185      * Hide the session's UI, if currently shown.  Call this when you are done with your
1186      * user interaction.
1187      */
hide()1188     public void hide() {
1189         if (mToken == null) {
1190             throw new IllegalStateException("Can't call before onCreate()");
1191         }
1192         try {
1193             mSystemService.hideSessionFromSession(mToken);
1194         } catch (RemoteException e) {
1195         }
1196     }
1197 
1198     /**
1199      * Control whether the UI layer for this session is enabled.  It is enabled by default.
1200      * If set to false, you will not be able to provide a UI through {@link #onCreateContentView()}.
1201      */
setUiEnabled(boolean enabled)1202     public void setUiEnabled(boolean enabled) {
1203         if (mUiEnabled != enabled) {
1204             mUiEnabled = enabled;
1205             if (mWindowVisible) {
1206                 if (enabled) {
1207                     ensureWindowAdded();
1208                     mWindow.show();
1209                 } else {
1210                     ensureWindowHidden();
1211                 }
1212             }
1213         }
1214     }
1215 
1216     /**
1217      * You can call this to customize the theme used by your IME's window.
1218      * This must be set before {@link #onCreate}, so you
1219      * will typically call it in your constructor with the resource ID
1220      * of your custom theme.
1221      */
setTheme(int theme)1222     public void setTheme(int theme) {
1223         if (mWindow != null) {
1224             throw new IllegalStateException("Must be called before onCreate()");
1225         }
1226         mTheme = theme;
1227     }
1228 
1229     /**
1230      * Ask that a new activity be started for voice interaction.  This will create a
1231      * new dedicated task in the activity manager for this voice interaction session;
1232      * this means that {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK}
1233      * will be set for you to make it a new task.
1234      *
1235      * <p>The newly started activity will be displayed to the user in a special way, as
1236      * a layer under the voice interaction UI.</p>
1237      *
1238      * <p>As the voice activity runs, it can retrieve a {@link android.app.VoiceInteractor}
1239      * through which it can perform voice interactions through your session.  These requests
1240      * for voice interactions will appear as callbacks on {@link #onGetSupportedCommands},
1241      * {@link #onRequestConfirmation}, {@link #onRequestPickOption},
1242      * {@link #onRequestCompleteVoice}, {@link #onRequestAbortVoice},
1243      * or {@link #onRequestCommand}
1244      *
1245      * <p>You will receive a call to {@link #onTaskStarted} when the task starts up
1246      * and {@link #onTaskFinished} when the last activity has finished.
1247      *
1248      * @param intent The Intent to start this voice interaction.  The given Intent will
1249      * always have {@link Intent#CATEGORY_VOICE Intent.CATEGORY_VOICE} added to it, since
1250      * this is part of a voice interaction.
1251      */
startVoiceActivity(Intent intent)1252     public void startVoiceActivity(Intent intent) {
1253         if (mToken == null) {
1254             throw new IllegalStateException("Can't call before onCreate()");
1255         }
1256         try {
1257             intent.migrateExtraStreamToClipData();
1258             intent.prepareToLeaveProcess(mContext);
1259             int res = mSystemService.startVoiceActivity(mToken, intent,
1260                     intent.resolveType(mContext.getContentResolver()));
1261             Instrumentation.checkStartActivityResult(res, intent);
1262         } catch (RemoteException e) {
1263         }
1264     }
1265 
1266 
1267 
1268     /**
1269      * <p>Ask that a new assistant activity be started.  This will create a new task in the
1270      * in activity manager: this means that
1271      * {@link Intent#FLAG_ACTIVITY_NEW_TASK Intent.FLAG_ACTIVITY_NEW_TASK}
1272      * will be set for you to make it a new task.</p>
1273      *
1274      * <p>The newly started activity will be displayed on top of other activities in the system
1275      * in a new layer that is not affected by multi-window mode.  Tasks started from this activity
1276      * will go into the normal activity layer and not this new layer.</p>
1277      *
1278      * <p>By default, the system will create a window for the UI for this session.  If you are using
1279      * an assistant activity instead, then you can disable the window creation by calling
1280      * {@link #setUiEnabled} in {@link #onPrepareShow(Bundle, int)}.</p>
1281      */
startAssistantActivity(Intent intent)1282     public void startAssistantActivity(Intent intent) {
1283         if (mToken == null) {
1284             throw new IllegalStateException("Can't call before onCreate()");
1285         }
1286         try {
1287             intent.migrateExtraStreamToClipData();
1288             intent.prepareToLeaveProcess(mContext);
1289             int res = mSystemService.startAssistantActivity(mToken, intent,
1290                     intent.resolveType(mContext.getContentResolver()));
1291             Instrumentation.checkStartActivityResult(res, intent);
1292         } catch (RemoteException e) {
1293         }
1294     }
1295 
1296     /**
1297      * Set whether this session will keep the device awake while it is running a voice
1298      * activity.  By default, the system holds a wake lock for it while in this state,
1299      * so that it can work even if the screen is off.  Setting this to false removes that
1300      * wake lock, allowing the CPU to go to sleep.  This is typically used if the
1301      * session decides it has been waiting too long for a response from the user and
1302      * doesn't want to let this continue to drain the battery.
1303      *
1304      * <p>Passing false here will release the wake lock, and you can call later with
1305      * true to re-acquire it.  It will also be automatically re-acquired for you each
1306      * time you start a new voice activity task -- that is when you call
1307      * {@link #startVoiceActivity}.</p>
1308      */
setKeepAwake(boolean keepAwake)1309     public void setKeepAwake(boolean keepAwake) {
1310         if (mToken == null) {
1311             throw new IllegalStateException("Can't call before onCreate()");
1312         }
1313         try {
1314             mSystemService.setKeepAwake(mToken, keepAwake);
1315         } catch (RemoteException e) {
1316         }
1317     }
1318 
1319     /**
1320      * Request that all system dialogs (and status bar shade etc) be closed, allowing
1321      * access to the session's UI.  This will <em>not</em> cause the lock screen to be
1322      * dismissed.
1323      */
closeSystemDialogs()1324     public void closeSystemDialogs() {
1325         if (mToken == null) {
1326             throw new IllegalStateException("Can't call before onCreate()");
1327         }
1328         try {
1329             mSystemService.closeSystemDialogs(mToken);
1330         } catch (RemoteException e) {
1331         }
1332     }
1333 
1334     /**
1335      * Convenience for inflating views.
1336      */
getLayoutInflater()1337     public LayoutInflater getLayoutInflater() {
1338         ensureWindowCreated();
1339         return mInflater;
1340     }
1341 
1342     /**
1343      * Retrieve the window being used to show the session's UI.
1344      */
getWindow()1345     public Dialog getWindow() {
1346         ensureWindowCreated();
1347         return mWindow;
1348     }
1349 
1350     /**
1351      * Finish the session.  This completely destroys the session -- the next time it is shown,
1352      * an entirely new one will be created.  You do not normally call this function; instead,
1353      * use {@link #hide} and allow the system to destroy your session if it needs its RAM.
1354      */
finish()1355     public void finish() {
1356         if (mToken == null) {
1357             throw new IllegalStateException("Can't call before onCreate()");
1358         }
1359         try {
1360             mSystemService.finish(mToken);
1361         } catch (RemoteException e) {
1362         }
1363     }
1364 
1365     /**
1366      * Initiatize a new session.  At this point you don't know exactly what this
1367      * session will be used for; you will find that out in {@link #onShow}.
1368      */
onCreate()1369     public void onCreate() {
1370         doOnCreate();
1371     }
1372 
doOnCreate()1373     private void doOnCreate() {
1374         mTheme = mTheme != 0 ? mTheme
1375                 : com.android.internal.R.style.Theme_DeviceDefault_VoiceInteractionSession;
1376     }
1377 
1378     /**
1379      * Called prior to {@link #onShow} before any UI setup has occurred.  Not generally useful.
1380      *
1381      * @param args The arguments that were supplied to
1382      * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
1383      * @param showFlags The show flags originally provided to
1384      * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
1385      */
onPrepareShow(Bundle args, int showFlags)1386     public void onPrepareShow(Bundle args, int showFlags) {
1387     }
1388 
1389     /**
1390      * Called when the session UI is going to be shown.  This is called after
1391      * {@link #onCreateContentView} (if the session's content UI needed to be created) and
1392      * immediately prior to the window being shown.  This may be called while the window
1393      * is already shown, if a show request has come in while it is shown, to allow you to
1394      * update the UI to match the new show arguments.
1395      *
1396      * @param args The arguments that were supplied to
1397      * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
1398      * @param showFlags The show flags originally provided to
1399      * {@link VoiceInteractionService#showSession VoiceInteractionService.showSession}.
1400      */
onShow(Bundle args, int showFlags)1401     public void onShow(Bundle args, int showFlags) {
1402     }
1403 
1404     /**
1405      * Called immediately after stopping to show the session UI.
1406      */
onHide()1407     public void onHide() {
1408     }
1409 
1410     /**
1411      * Last callback to the session as it is being finished.
1412      */
onDestroy()1413     public void onDestroy() {
1414     }
1415 
1416     /**
1417      * Hook in which to create the session's UI.
1418      */
onCreateContentView()1419     public View onCreateContentView() {
1420         return null;
1421     }
1422 
setContentView(View view)1423     public void setContentView(View view) {
1424         ensureWindowCreated();
1425         mContentFrame.removeAllViews();
1426         mContentFrame.addView(view, new FrameLayout.LayoutParams(
1427                 ViewGroup.LayoutParams.MATCH_PARENT,
1428                 ViewGroup.LayoutParams.MATCH_PARENT));
1429         mContentFrame.requestApplyInsets();
1430     }
1431 
doOnHandleAssist(Bundle data, AssistStructure structure, Throwable failure, AssistContent content)1432     void doOnHandleAssist(Bundle data, AssistStructure structure, Throwable failure,
1433             AssistContent content) {
1434         if (failure != null) {
1435             onAssistStructureFailure(failure);
1436         }
1437         onHandleAssist(data, structure, content);
1438     }
1439 
doOnHandleAssistSecondary(Bundle data, AssistStructure structure, Throwable failure, AssistContent content, int index, int count)1440     void doOnHandleAssistSecondary(Bundle data, AssistStructure structure, Throwable failure,
1441             AssistContent content, int index, int count) {
1442         if (failure != null) {
1443             onAssistStructureFailure(failure);
1444         }
1445         onHandleAssistSecondary(data, structure, content, index, count);
1446     }
1447 
1448     /**
1449      * Called when there has been a failure transferring the {@link AssistStructure} to
1450      * the assistant.  This may happen, for example, if the data is too large and results
1451      * in an out of memory exception, or the client has provided corrupt data.  This will
1452      * be called immediately before {@link #onHandleAssist} and the AssistStructure supplied
1453      * there afterwards will be null.
1454      *
1455      * @param failure The failure exception that was thrown when building the
1456      * {@link AssistStructure}.
1457      */
onAssistStructureFailure(Throwable failure)1458     public void onAssistStructureFailure(Throwable failure) {
1459     }
1460 
1461     /**
1462      * Called to receive data from the application that the user was currently viewing when
1463      * an assist session is started.  If the original show request did not specify
1464      * {@link #SHOW_WITH_ASSIST}, this method will not be called.
1465      *
1466      * @param data Arbitrary data supplied by the app through
1467      * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}.
1468      * May be null if assist data has been disabled by the user or device policy.
1469      * @param structure If available, the structure definition of all windows currently
1470      * displayed by the app.  May be null if assist data has been disabled by the user
1471      * or device policy; will be an empty stub if the application has disabled assist
1472      * by marking its window as secure.
1473      * @param content Additional content data supplied by the app through
1474      * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}.
1475      * May be null if assist data has been disabled by the user or device policy; will
1476      * not be automatically filled in with data from the app if the app has marked its
1477      * window as secure.
1478      */
onHandleAssist(@ullable Bundle data, @Nullable AssistStructure structure, @Nullable AssistContent content)1479     public void onHandleAssist(@Nullable Bundle data, @Nullable AssistStructure structure,
1480             @Nullable AssistContent content) {
1481     }
1482 
1483     /**
1484      * Called to receive data from other applications that the user was or is interacting with,
1485      * that are currently on the screen in a multi-window display environment, not including the
1486      * currently focused activity. This could be
1487      * a free-form window, a picture-in-picture window, or another window in a split-screen display.
1488      * <p>
1489      * This method is very similar to
1490      * {@link #onHandleAssist} except that it is called
1491      * for additional non-focused activities along with an index and count that indicates
1492      * which additional activity the data is for. {@code index} will be between 1 and
1493      * {@code count}-1 and this method is called once for each additional window, in no particular
1494      * order. The {@code count} indicates how many windows to expect assist data for, including the
1495      * top focused activity, which continues to be returned via {@link #onHandleAssist}.
1496      * <p>
1497      * To be responsive to assist requests, process assist data as soon as it is received,
1498      * without waiting for all queued activities to return assist data.
1499      *
1500      * @param data Arbitrary data supplied by the app through
1501      * {@link android.app.Activity#onProvideAssistData Activity.onProvideAssistData}.
1502      * May be null if assist data has been disabled by the user or device policy.
1503      * @param structure If available, the structure definition of all windows currently
1504      * displayed by the app.  May be null if assist data has been disabled by the user
1505      * or device policy; will be an empty stub if the application has disabled assist
1506      * by marking its window as secure.
1507      * @param content Additional content data supplied by the app through
1508      * {@link android.app.Activity#onProvideAssistContent Activity.onProvideAssistContent}.
1509      * May be null if assist data has been disabled by the user or device policy; will
1510      * not be automatically filled in with data from the app if the app has marked its
1511      * window as secure.
1512      * @param index the index of the additional activity that this data
1513      *        is for.
1514      * @param count the total number of additional activities for which the assist data is being
1515      *        returned, including the focused activity that is returned via
1516      *        {@link #onHandleAssist}.
1517      */
onHandleAssistSecondary(@ullable Bundle data, @Nullable AssistStructure structure, @Nullable AssistContent content, int index, int count)1518     public void onHandleAssistSecondary(@Nullable Bundle data, @Nullable AssistStructure structure,
1519             @Nullable AssistContent content, int index, int count) {
1520     }
1521 
1522     /**
1523      * Called to receive a screenshot of what the user was currently viewing when an assist
1524      * session is started.  May be null if screenshots are disabled by the user, policy,
1525      * or application.  If the original show request did not specify
1526      * {@link #SHOW_WITH_SCREENSHOT}, this method will not be called.
1527      */
onHandleScreenshot(@ullable Bitmap screenshot)1528     public void onHandleScreenshot(@Nullable Bitmap screenshot) {
1529     }
1530 
onKeyDown(int keyCode, KeyEvent event)1531     public boolean onKeyDown(int keyCode, KeyEvent event) {
1532         return false;
1533     }
1534 
onKeyLongPress(int keyCode, KeyEvent event)1535     public boolean onKeyLongPress(int keyCode, KeyEvent event) {
1536         return false;
1537     }
1538 
onKeyUp(int keyCode, KeyEvent event)1539     public boolean onKeyUp(int keyCode, KeyEvent event) {
1540         return false;
1541     }
1542 
onKeyMultiple(int keyCode, int count, KeyEvent event)1543     public boolean onKeyMultiple(int keyCode, int count, KeyEvent event) {
1544         return false;
1545     }
1546 
1547     /**
1548      * Called when the user presses the back button while focus is in the session UI.  Note
1549      * that this will only happen if the session UI has requested input focus in its window;
1550      * otherwise, the back key will go to whatever window has focus and do whatever behavior
1551      * it normally has there.  The default implementation simply calls {@link #hide}.
1552      */
onBackPressed()1553     public void onBackPressed() {
1554         hide();
1555     }
1556 
1557     /**
1558      * Sessions automatically watch for requests that all system UI be closed (such as when
1559      * the user presses HOME), which will appear here.  The default implementation always
1560      * calls {@link #hide}.
1561      */
onCloseSystemDialogs()1562     public void onCloseSystemDialogs() {
1563         hide();
1564     }
1565 
1566     /**
1567      * Called when the lockscreen was shown.
1568      */
onLockscreenShown()1569     public void onLockscreenShown() {
1570         hide();
1571     }
1572 
1573     @Override
onConfigurationChanged(Configuration newConfig)1574     public void onConfigurationChanged(Configuration newConfig) {
1575     }
1576 
1577     @Override
onLowMemory()1578     public void onLowMemory() {
1579     }
1580 
1581     @Override
onTrimMemory(int level)1582     public void onTrimMemory(int level) {
1583     }
1584 
1585     /**
1586      * Compute the interesting insets into your UI.  The default implementation
1587      * sets {@link Insets#contentInsets outInsets.contentInsets.top} to the height
1588      * of the window, meaning it should not adjust content underneath.  The default touchable
1589      * insets are {@link Insets#TOUCHABLE_INSETS_FRAME}, meaning it consumes all touch
1590      * events within its window frame.
1591      *
1592      * @param outInsets Fill in with the current UI insets.
1593      */
onComputeInsets(Insets outInsets)1594     public void onComputeInsets(Insets outInsets) {
1595         outInsets.contentInsets.left = 0;
1596         outInsets.contentInsets.bottom = 0;
1597         outInsets.contentInsets.right = 0;
1598         View decor = getWindow().getWindow().getDecorView();
1599         outInsets.contentInsets.top = decor.getHeight();
1600         outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_FRAME;
1601         outInsets.touchableRegion.setEmpty();
1602     }
1603 
1604     /**
1605      * Called when a task initiated by {@link #startVoiceActivity(android.content.Intent)}
1606      * has actually started.
1607      *
1608      * @param intent The original {@link Intent} supplied to
1609      * {@link #startVoiceActivity(android.content.Intent)}.
1610      * @param taskId Unique ID of the now running task.
1611      */
onTaskStarted(Intent intent, int taskId)1612     public void onTaskStarted(Intent intent, int taskId) {
1613     }
1614 
1615     /**
1616      * Called when the last activity of a task initiated by
1617      * {@link #startVoiceActivity(android.content.Intent)} has finished.  The default
1618      * implementation calls {@link #finish()} on the assumption that this represents
1619      * the completion of a voice action.  You can override the implementation if you would
1620      * like a different behavior.
1621      *
1622      * @param intent The original {@link Intent} supplied to
1623      * {@link #startVoiceActivity(android.content.Intent)}.
1624      * @param taskId Unique ID of the finished task.
1625      */
onTaskFinished(Intent intent, int taskId)1626     public void onTaskFinished(Intent intent, int taskId) {
1627         hide();
1628     }
1629 
1630     /**
1631      * Request to query for what extended commands the session supports.
1632      *
1633      * @param commands An array of commands that are being queried.
1634      * @return Return an array of booleans indicating which of each entry in the
1635      * command array is supported.  A true entry in the array indicates the command
1636      * is supported; false indicates it is not.  The default implementation returns
1637      * an array of all false entries.
1638      */
onGetSupportedCommands(String[] commands)1639     public boolean[] onGetSupportedCommands(String[] commands) {
1640         return new boolean[commands.length];
1641     }
1642 
1643     /**
1644      * Request to confirm with the user before proceeding with an unrecoverable operation,
1645      * corresponding to a {@link android.app.VoiceInteractor.ConfirmationRequest
1646      * VoiceInteractor.ConfirmationRequest}.
1647      *
1648      * @param request The active request.
1649      */
onRequestConfirmation(ConfirmationRequest request)1650     public void onRequestConfirmation(ConfirmationRequest request) {
1651     }
1652 
1653     /**
1654      * Request for the user to pick one of N options, corresponding to a
1655      * {@link android.app.VoiceInteractor.PickOptionRequest VoiceInteractor.PickOptionRequest}.
1656      *
1657      * @param request The active request.
1658      */
onRequestPickOption(PickOptionRequest request)1659     public void onRequestPickOption(PickOptionRequest request) {
1660     }
1661 
1662     /**
1663      * Request to complete the voice interaction session because the voice activity successfully
1664      * completed its interaction using voice.  Corresponds to
1665      * {@link android.app.VoiceInteractor.CompleteVoiceRequest
1666      * VoiceInteractor.CompleteVoiceRequest}.  The default implementation just sends an empty
1667      * confirmation back to allow the activity to exit.
1668      *
1669      * @param request The active request.
1670      */
onRequestCompleteVoice(CompleteVoiceRequest request)1671     public void onRequestCompleteVoice(CompleteVoiceRequest request) {
1672     }
1673 
1674     /**
1675      * Request to abort the voice interaction session because the voice activity can not
1676      * complete its interaction using voice.  Corresponds to
1677      * {@link android.app.VoiceInteractor.AbortVoiceRequest
1678      * VoiceInteractor.AbortVoiceRequest}.  The default implementation just sends an empty
1679      * confirmation back to allow the activity to exit.
1680      *
1681      * @param request The active request.
1682      */
onRequestAbortVoice(AbortVoiceRequest request)1683     public void onRequestAbortVoice(AbortVoiceRequest request) {
1684     }
1685 
1686     /**
1687      * Process an arbitrary extended command from the caller,
1688      * corresponding to a {@link android.app.VoiceInteractor.CommandRequest
1689      * VoiceInteractor.CommandRequest}.
1690      *
1691      * @param request The active request.
1692      */
onRequestCommand(CommandRequest request)1693     public void onRequestCommand(CommandRequest request) {
1694     }
1695 
1696     /**
1697      * Called when the {@link android.app.VoiceInteractor} has asked to cancel a {@link Request}
1698      * that was previously delivered to {@link #onRequestConfirmation},
1699      * {@link #onRequestPickOption}, {@link #onRequestCompleteVoice}, {@link #onRequestAbortVoice},
1700      * or {@link #onRequestCommand}.
1701      *
1702      * @param request The request that is being canceled.
1703      */
onCancelRequest(Request request)1704     public void onCancelRequest(Request request) {
1705     }
1706 
1707     /**
1708      * Print the Service's state into the given stream.  This gets invoked by
1709      * {@link VoiceInteractionSessionService} when its Service
1710      * {@link android.app.Service#dump} method is called.
1711      *
1712      * @param prefix Text to print at the front of each line.
1713      * @param fd The raw file descriptor that the dump is being sent to.
1714      * @param writer The PrintWriter to which you should dump your state.  This will be
1715      * closed for you after you return.
1716      * @param args additional arguments to the dump request.
1717      */
dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args)1718     public void dump(String prefix, FileDescriptor fd, PrintWriter writer, String[] args) {
1719         writer.print(prefix); writer.print("mToken="); writer.println(mToken);
1720         writer.print(prefix); writer.print("mTheme=#"); writer.println(Integer.toHexString(mTheme));
1721         writer.print(prefix); writer.print("mUiEnabled="); writer.println(mUiEnabled);
1722         writer.print(" mInitialized="); writer.println(mInitialized);
1723         writer.print(prefix); writer.print("mWindowAdded="); writer.print(mWindowAdded);
1724         writer.print(" mWindowVisible="); writer.println(mWindowVisible);
1725         writer.print(prefix); writer.print("mWindowWasVisible="); writer.print(mWindowWasVisible);
1726         writer.print(" mInShowWindow="); writer.println(mInShowWindow);
1727         if (mActiveRequests.size() > 0) {
1728             writer.print(prefix); writer.println("Active requests:");
1729             String innerPrefix = prefix + "    ";
1730             for (int i=0; i<mActiveRequests.size(); i++) {
1731                 Request req = mActiveRequests.valueAt(i);
1732                 writer.print(prefix); writer.print("  #"); writer.print(i);
1733                 writer.print(": ");
1734                 writer.println(req);
1735                 req.dump(innerPrefix, fd, writer, args);
1736 
1737             }
1738         }
1739     }
1740 }
1741