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