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