1 /* 2 * Copyright (C) 2019 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.view; 18 19 import android.annotation.FlaggedApi; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.annotation.TestApi; 23 import android.content.Context; 24 import android.content.res.Configuration; 25 import android.graphics.PixelFormat; 26 import android.graphics.Rect; 27 import android.os.IBinder; 28 import android.os.Parcel; 29 import android.os.Parcelable; 30 import android.os.RemoteException; 31 import android.util.Log; 32 import android.view.accessibility.IAccessibilityEmbeddedConnection; 33 import android.window.ISurfaceSyncGroup; 34 import android.window.InputTransferToken; 35 import android.window.WindowTokenClient; 36 37 import com.android.window.flags.Flags; 38 39 import dalvik.system.CloseGuard; 40 41 import java.util.Objects; 42 import java.util.concurrent.CompletableFuture; 43 import java.util.concurrent.ExecutionException; 44 import java.util.concurrent.TimeUnit; 45 import java.util.concurrent.TimeoutException; 46 47 /** 48 * Utility class for adding a View hierarchy to a {@link SurfaceControl}. The View hierarchy 49 * will render in to a root SurfaceControl, and receive input based on the SurfaceControl's 50 * placement on-screen. The primary usage of this class is to embed a View hierarchy from 51 * one process in to another. After the SurfaceControlViewHost has been set up in the embedded 52 * content provider, we can send the {@link SurfaceControlViewHost.SurfacePackage} 53 * to the host process. The host process can then attach the hierarchy to a SurfaceView within 54 * its own by calling 55 * {@link SurfaceView#setChildSurfacePackage}. 56 */ 57 public class SurfaceControlViewHost { 58 private final static String TAG = "SurfaceControlViewHost"; 59 private final ViewRootImpl mViewRoot; 60 private final CloseGuard mCloseGuard = CloseGuard.get(); 61 private final WindowlessWindowManager mWm; 62 63 private SurfaceControl mSurfaceControl; 64 private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection; 65 private boolean mReleased = false; 66 67 private final class ISurfaceControlViewHostImpl extends ISurfaceControlViewHost.Stub { 68 @Override onConfigurationChanged(Configuration configuration)69 public void onConfigurationChanged(Configuration configuration) { 70 if (mViewRoot == null) { 71 return; 72 } 73 mViewRoot.mHandler.post(() -> { 74 mWm.setConfiguration(configuration); 75 if (mViewRoot != null) { 76 mViewRoot.forceWmRelayout(); 77 } 78 }); 79 } 80 81 @Override onDispatchDetachedFromWindow()82 public void onDispatchDetachedFromWindow() { 83 if (mViewRoot == null) { 84 return; 85 } 86 mViewRoot.mHandler.post(() -> { 87 release(); 88 }); 89 } 90 91 @Override onInsetsChanged(InsetsState state, Rect frame)92 public void onInsetsChanged(InsetsState state, Rect frame) { 93 if (mViewRoot != null) { 94 mViewRoot.mHandler.post(() -> { 95 mViewRoot.setOverrideInsetsFrame(frame); 96 }); 97 } 98 mWm.setInsetsState(state); 99 } 100 101 @Override getSurfaceSyncGroup()102 public ISurfaceSyncGroup getSurfaceSyncGroup() { 103 CompletableFuture<ISurfaceSyncGroup> surfaceSyncGroup = new CompletableFuture<>(); 104 // If the call came from in process and it's already running on the UI thread, return 105 // results immediately instead of posting to the main thread. If we post to the main 106 // thread, it will block itself and the return value will always be null. 107 if (Thread.currentThread() == mViewRoot.mThread) { 108 return mViewRoot.getOrCreateSurfaceSyncGroup().mISurfaceSyncGroup; 109 } else { 110 mViewRoot.mHandler.post( 111 () -> surfaceSyncGroup.complete( 112 mViewRoot.getOrCreateSurfaceSyncGroup().mISurfaceSyncGroup)); 113 } 114 try { 115 return surfaceSyncGroup.get(1, TimeUnit.SECONDS); 116 } catch (InterruptedException | ExecutionException | TimeoutException e) { 117 Log.e(TAG, "Failed to get SurfaceSyncGroup for SCVH", e); 118 } 119 return null; 120 } 121 122 @Override attachParentInterface(@ullable ISurfaceControlViewHostParent parentInterface)123 public void attachParentInterface(@Nullable ISurfaceControlViewHostParent parentInterface) { 124 mViewRoot.mHandler.post(() -> mWm.setParentInterface(parentInterface)); 125 } 126 } 127 128 private ISurfaceControlViewHost mRemoteInterface = new ISurfaceControlViewHostImpl(); 129 130 private ViewRootImpl.ConfigChangedCallback mConfigChangedCallback; 131 132 /** 133 * Package encapsulating a Surface hierarchy which contains interactive view 134 * elements. It's expected to get this object from 135 * {@link SurfaceControlViewHost#getSurfacePackage} afterwards it can be embedded within 136 * a SurfaceView by calling {@link SurfaceView#setChildSurfacePackage}. 137 * 138 * Note that each {@link SurfacePackage} must be released by calling 139 * {@link SurfacePackage#release}. However, if you use the recommended flow, 140 * the framework will automatically handle the lifetime for you. 141 * 142 * 1. When sending the package to the remote process, return it from an AIDL method 143 * or manually use FLAG_WRITE_RETURN_VALUE in writeToParcel. This will automatically 144 * release the package in the local process. 145 * 2. In the remote process, consume the package using SurfaceView. This way the 146 * SurfaceView will take over the lifetime and call {@link SurfacePackage#release} 147 * for the user. 148 * 149 * One final note: The {@link SurfacePackage} lifetime is totally de-coupled 150 * from the lifetime of the underlying {@link SurfaceControlViewHost}. Regardless 151 * of the lifetime of the package the user should still call 152 * {@link SurfaceControlViewHost#release} when finished. 153 */ 154 public static final class SurfacePackage implements Parcelable { 155 private SurfaceControl mSurfaceControl; 156 private final IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection; 157 private final InputTransferToken mInputTransferToken; 158 @NonNull 159 private final ISurfaceControlViewHost mRemoteInterface; 160 SurfacePackage(SurfaceControl sc, IAccessibilityEmbeddedConnection connection, InputTransferToken inputTransferToken, @NonNull ISurfaceControlViewHost ri)161 SurfacePackage(SurfaceControl sc, IAccessibilityEmbeddedConnection connection, 162 InputTransferToken inputTransferToken, 163 @NonNull ISurfaceControlViewHost ri) { 164 mSurfaceControl = sc; 165 mAccessibilityEmbeddedConnection = connection; 166 mInputTransferToken = inputTransferToken; 167 mRemoteInterface = ri; 168 } 169 170 /** 171 * Constructs a copy of {@code SurfacePackage} with an independent lifetime. 172 * 173 * The caller can use this to create an independent copy in situations where ownership of 174 * the {@code SurfacePackage} would be transferred elsewhere, such as attaching to a 175 * {@code SurfaceView}, returning as {@code Binder} result value, etc. The caller is 176 * responsible for releasing this copy when its done. 177 * 178 * @param other {@code SurfacePackage} to create a copy of. 179 */ SurfacePackage(@onNull SurfacePackage other)180 public SurfacePackage(@NonNull SurfacePackage other) { 181 SurfaceControl otherSurfaceControl = other.mSurfaceControl; 182 if (otherSurfaceControl != null && otherSurfaceControl.isValid()) { 183 mSurfaceControl = new SurfaceControl(otherSurfaceControl, "SurfacePackage"); 184 } 185 mAccessibilityEmbeddedConnection = other.mAccessibilityEmbeddedConnection; 186 mInputTransferToken = other.mInputTransferToken; 187 mRemoteInterface = other.mRemoteInterface; 188 } 189 SurfacePackage(Parcel in)190 private SurfacePackage(Parcel in) { 191 mSurfaceControl = new SurfaceControl(); 192 mSurfaceControl.readFromParcel(in); 193 mSurfaceControl.setUnreleasedWarningCallSite("SurfacePackage(Parcel)"); 194 mAccessibilityEmbeddedConnection = IAccessibilityEmbeddedConnection.Stub.asInterface( 195 in.readStrongBinder()); 196 mInputTransferToken = InputTransferToken.CREATOR.createFromParcel(in); 197 mRemoteInterface = ISurfaceControlViewHost.Stub.asInterface(in.readStrongBinder()); 198 } 199 200 /** 201 * Returns the {@link android.view.SurfaceControl} associated with this SurfacePackage for 202 * cases where more control is required. 203 * 204 * @return the SurfaceControl associated with this SurfacePackage and its containing 205 * SurfaceControlViewHost 206 */ getSurfaceControl()207 public @NonNull SurfaceControl getSurfaceControl() { 208 return mSurfaceControl; 209 } 210 211 /** 212 * Gets an accessibility embedded connection interface for this SurfaceControlViewHost. 213 * 214 * @return {@link IAccessibilityEmbeddedConnection} interface. 215 * @hide 216 */ getAccessibilityEmbeddedConnection()217 public IAccessibilityEmbeddedConnection getAccessibilityEmbeddedConnection() { 218 return mAccessibilityEmbeddedConnection; 219 } 220 221 /** 222 * @hide 223 */ 224 @NonNull getRemoteInterface()225 public ISurfaceControlViewHost getRemoteInterface() { 226 return mRemoteInterface; 227 } 228 229 /** 230 * Forward a configuration to the remote SurfaceControlViewHost. 231 * This will cause View#onConfigurationChanged to be invoked on the remote 232 * end. This does not automatically cause the SurfaceControlViewHost 233 * to be resized. The root View of a SurfaceControlViewHost 234 * is more akin to a PopupWindow in that the size is user specified 235 * independent of configuration width and height. 236 * 237 * In order to receive the configuration change via 238 * {@link View#onConfigurationChanged}, the context used with the 239 * SurfaceControlViewHost and it's embedded view hierarchy must 240 * be a WindowContext obtained from {@link Context#createWindowContext}. 241 * 242 * If a regular service context is used, then your embedded view hierarchy 243 * will always perceive the global configuration. 244 * 245 * @param c The configuration to forward 246 */ notifyConfigurationChanged(@onNull Configuration c)247 public void notifyConfigurationChanged(@NonNull Configuration c) { 248 try { 249 getRemoteInterface().onConfigurationChanged(c); 250 } catch (RemoteException e) { 251 e.rethrowAsRuntimeException(); 252 } 253 } 254 255 /** 256 * Tear down the remote SurfaceControlViewHost and cause 257 * View#onDetachedFromWindow to be invoked on the other side. 258 */ notifyDetachedFromWindow()259 public void notifyDetachedFromWindow() { 260 try { 261 getRemoteInterface().onDispatchDetachedFromWindow(); 262 } catch (RemoteException e) { 263 e.rethrowAsRuntimeException(); 264 } 265 } 266 267 @Override describeContents()268 public int describeContents() { 269 return 0; 270 } 271 272 @Override writeToParcel(@onNull Parcel out, int flags)273 public void writeToParcel(@NonNull Parcel out, int flags) { 274 mSurfaceControl.writeToParcel(out, flags); 275 out.writeStrongBinder(mAccessibilityEmbeddedConnection.asBinder()); 276 mInputTransferToken.writeToParcel(out, flags); 277 out.writeStrongBinder(mRemoteInterface.asBinder()); 278 } 279 280 /** 281 * Release the {@link SurfaceControl} associated with this package. 282 * It's not necessary to call this if you pass the package to 283 * {@link SurfaceView#setChildSurfacePackage} as {@link SurfaceView} will 284 * take ownership in that case. 285 */ release()286 public void release() { 287 if (mSurfaceControl != null) { 288 mSurfaceControl.release(); 289 } 290 mSurfaceControl = null; 291 } 292 293 /** 294 * Gets an {@link InputTransferToken} which can be used to request focus on the embedded 295 * surface or to transfer touch gesture to the embedded surface. 296 * 297 * @return the InputTransferToken associated with {@link SurfacePackage} or {@code null} if 298 * the embedded hasn't set up its view or doesn't have input. 299 * @see WindowManager#transferTouchGesture(InputTransferToken, InputTransferToken) 300 */ 301 @Nullable 302 @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) getInputTransferToken()303 public InputTransferToken getInputTransferToken() { 304 return mInputTransferToken; 305 } 306 307 @Override toString()308 public String toString() { 309 return "{inputTransferToken=" + getInputTransferToken() + " remoteInterface=" 310 + getRemoteInterface() + "}"; 311 } 312 313 public static final @NonNull Creator<SurfacePackage> CREATOR 314 = new Creator<SurfacePackage>() { 315 public SurfacePackage createFromParcel(Parcel in) { 316 return new SurfacePackage(in); 317 } 318 public SurfacePackage[] newArray(int size) { 319 return new SurfacePackage[size]; 320 } 321 }; 322 } 323 324 /** @hide */ SurfaceControlViewHost(@onNull Context c, @NonNull Display d, @NonNull WindowlessWindowManager wwm, @NonNull String callsite)325 public SurfaceControlViewHost(@NonNull Context c, @NonNull Display d, 326 @NonNull WindowlessWindowManager wwm, @NonNull String callsite) { 327 mSurfaceControl = wwm.mRootSurface; 328 mWm = wwm; 329 mViewRoot = new ViewRootImpl(c, d, mWm, new WindowlessWindowLayout()); 330 mCloseGuard.openWithCallSite("release", callsite); 331 setConfigCallback(c, d); 332 333 WindowManagerGlobal.getInstance().addWindowlessRoot(mViewRoot); 334 335 mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection(); 336 } 337 338 /** 339 * Construct a new SurfaceControlViewHost. The root Surface will be 340 * allocated internally and is accessible via getSurfacePackage(). 341 * 342 * The {@param hostToken} parameter, primarily used for ANR reporting, 343 * must be obtained from whomever will be hosting the embedded hierarchy. 344 * It's accessible from {@link SurfaceView#getHostToken}. 345 * 346 * @param context The Context object for your activity or application. 347 * @param display The Display the hierarchy will be placed on. 348 * @param hostToken The host token, as discussed above. 349 */ SurfaceControlViewHost(@onNull Context context, @NonNull Display display, @Nullable IBinder hostToken)350 public SurfaceControlViewHost(@NonNull Context context, @NonNull Display display, 351 @Nullable IBinder hostToken) { 352 this(context, display, hostToken == null ? null : new InputTransferToken(hostToken), 353 "untracked"); 354 355 } 356 357 /** 358 * Construct a new SurfaceControlViewHost. The root Surface will be 359 * allocated internally and is accessible via getSurfacePackage(). 360 * <p> 361 * The hostInputTransferToken parameter allows the host and embedded to be associated with 362 * each other to allow transferring touch gesture and focus. This is also used for ANR 363 * reporting. It's accessible from {@link AttachedSurfaceControl#getInputTransferToken()}. 364 * 365 * @param context The Context object for your activity or application. 366 * @param display The Display the hierarchy will be placed on. 367 * @param hostInputTransferToken The host input transfer token, as discussed above. 368 */ 369 @FlaggedApi(Flags.FLAG_SURFACE_CONTROL_INPUT_RECEIVER) SurfaceControlViewHost(@onNull Context context, @NonNull Display display, @Nullable InputTransferToken hostInputTransferToken)370 public SurfaceControlViewHost(@NonNull Context context, @NonNull Display display, 371 @Nullable InputTransferToken hostInputTransferToken) { 372 this(context, display, hostInputTransferToken, "untracked"); 373 } 374 375 /** 376 * Construct a new SurfaceControlViewHost. The root Surface will be 377 * allocated internally and is accessible via getSurfacePackage(). 378 * 379 * The {@param hostToken} parameter, primarily used for ANR reporting, 380 * must be obtained from whomever will be hosting the embedded hierarchy. 381 * It's accessible from {@link SurfaceView#getHostToken}. 382 * 383 * @param context The Context object for your activity or application. 384 * @param display The Display the hierarchy will be placed on. 385 * @param hostToken The host token, as discussed above. 386 * @param callsite The call site, used for tracking leakage of the host 387 * @hide 388 */ SurfaceControlViewHost(@onNull Context context, @NonNull Display display, @Nullable InputTransferToken hostToken, @NonNull String callsite)389 public SurfaceControlViewHost(@NonNull Context context, @NonNull Display display, 390 @Nullable InputTransferToken hostToken, @NonNull String callsite) { 391 mSurfaceControl = new SurfaceControl.Builder() 392 .setContainerLayer() 393 .setName("SurfaceControlViewHost") 394 .setCallsite("SurfaceControlViewHost[" + callsite + "]") 395 .build(); 396 mWm = new WindowlessWindowManager(context.getResources().getConfiguration(), 397 mSurfaceControl, hostToken); 398 399 mViewRoot = new ViewRootImpl(context, display, mWm, new WindowlessWindowLayout()); 400 mCloseGuard.openWithCallSite("release", callsite); 401 setConfigCallback(context, display); 402 403 WindowManagerGlobal.getInstance().addWindowlessRoot(mViewRoot); 404 405 mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection(); 406 } 407 setConfigCallback(Context c, Display d)408 private void setConfigCallback(Context c, Display d) { 409 final IBinder token = c.getWindowContextToken(); 410 mConfigChangedCallback = conf -> { 411 if (token instanceof WindowTokenClient) { 412 final WindowTokenClient w = (WindowTokenClient) token; 413 w.onConfigurationChanged(conf, d.getDisplayId(), true); 414 } 415 }; 416 417 ViewRootImpl.addConfigCallback(mConfigChangedCallback); 418 } 419 420 /** 421 * @hide 422 */ 423 @Override finalize()424 protected void finalize() throws Throwable { 425 if (mReleased) { 426 return; 427 } 428 if (mCloseGuard != null) { 429 mCloseGuard.warnIfOpen(); 430 } 431 // We aren't on the UI thread here so we need to pass false to doDie 432 doRelease(false /* immediate */); 433 } 434 435 /** 436 * Return a SurfacePackage for the root SurfaceControl of the embedded hierarchy. 437 * Rather than be directly reparented using {@link SurfaceControl.Transaction} this 438 * SurfacePackage should be passed to {@link SurfaceView#setChildSurfacePackage} 439 * which will not only reparent the Surface, but ensure the accessibility hierarchies 440 * are linked. 441 */ getSurfacePackage()442 public @Nullable SurfacePackage getSurfacePackage() { 443 if (mSurfaceControl != null && mAccessibilityEmbeddedConnection != null) { 444 return new SurfacePackage(new SurfaceControl(mSurfaceControl, "getSurfacePackage"), 445 mAccessibilityEmbeddedConnection, getInputTransferToken(), mRemoteInterface); 446 } else { 447 return null; 448 } 449 } 450 451 /** 452 * @hide 453 */ getRootSurfaceControl()454 public @NonNull AttachedSurfaceControl getRootSurfaceControl() { 455 return mViewRoot; 456 } 457 458 /** 459 * Set the root view of the SurfaceControlViewHost. This view will render in to 460 * the SurfaceControl, and receive input based on the SurfaceControls positioning on 461 * screen. It will be laid as if it were in a window of the passed in width and height. 462 * 463 * @param view The View to add 464 * @param width The width to layout the View within, in pixels. 465 * @param height The height to layout the View within, in pixels. 466 */ setView(@onNull View view, int width, int height)467 public void setView(@NonNull View view, int width, int height) { 468 final WindowManager.LayoutParams lp = 469 new WindowManager.LayoutParams(width, height, 470 WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT); 471 setView(view, lp); 472 } 473 474 /** 475 * @hide 476 */ 477 @TestApi setView(@onNull View view, @NonNull WindowManager.LayoutParams attrs)478 public void setView(@NonNull View view, @NonNull WindowManager.LayoutParams attrs) { 479 Objects.requireNonNull(view); 480 attrs.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 481 addWindowToken(attrs); 482 view.setLayoutParams(attrs); 483 mViewRoot.setView(view, attrs, null); 484 mViewRoot.setBackKeyCallbackForWindowlessWindow(mWm::forwardBackKeyToParent); 485 } 486 487 /** 488 * @return The view passed to setView, or null if none has been passed. 489 */ getView()490 public @Nullable View getView() { 491 return mViewRoot.getView(); 492 } 493 494 /** 495 * @return the ViewRootImpl wrapped by this host. 496 * @hide 497 */ getWindowToken()498 public IWindow getWindowToken() { 499 return mViewRoot.mWindow; 500 } 501 502 /** 503 * @return the WindowlessWindowManager instance that this host is attached to. 504 * @hide 505 */ getWindowlessWM()506 public @NonNull WindowlessWindowManager getWindowlessWM() { 507 return mWm; 508 } 509 510 /** 511 * Forces relayout and draw and allows to set a custom callback when it is finished 512 * @hide 513 */ relayout(WindowManager.LayoutParams attrs, WindowlessWindowManager.ResizeCompleteCallback callback)514 public void relayout(WindowManager.LayoutParams attrs, 515 WindowlessWindowManager.ResizeCompleteCallback callback) { 516 mViewRoot.setLayoutParams(attrs, false); 517 mViewRoot.setReportNextDraw(true /* syncBuffer */, "scvh_relayout"); 518 mWm.setCompletionCallback(mViewRoot.mWindow.asBinder(), callback); 519 } 520 521 /** 522 * @hide 523 */ 524 @TestApi relayout(WindowManager.LayoutParams attrs)525 public void relayout(WindowManager.LayoutParams attrs) { 526 mViewRoot.setLayoutParams(attrs, false); 527 } 528 529 /** 530 * Modify the size of the root view. 531 * 532 * @param width Width in pixels 533 * @param height Height in pixels 534 */ relayout(int width, int height)535 public void relayout(int width, int height) { 536 final WindowManager.LayoutParams lp = 537 new WindowManager.LayoutParams(width, height, 538 WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT); 539 relayout(lp); 540 } 541 542 /** 543 * Trigger the tear down of the embedded view hierarchy and release the SurfaceControl. 544 * This will result in onDispatchedFromWindow being dispatched to the embedded view hierarchy 545 * and render the object unusable. 546 */ release()547 public void release() { 548 // ViewRoot will release mSurfaceControl for us. 549 doRelease(true /* immediate */); 550 } 551 doRelease(boolean immediate)552 private void doRelease(boolean immediate) { 553 if (mConfigChangedCallback != null) { 554 ViewRootImpl.removeConfigCallback(mConfigChangedCallback); 555 mConfigChangedCallback = null; 556 } 557 558 mViewRoot.die(immediate); 559 WindowManagerGlobal.getInstance().removeWindowlessRoot(mViewRoot); 560 mReleased = true; 561 mCloseGuard.close(); 562 } 563 564 /** 565 * Returns an input token used which can be used to request focus on the embedded surface 566 * or to transfer touch gesture to the embedded surface. 567 * 568 * @hide 569 */ getInputTransferToken()570 public InputTransferToken getInputTransferToken() { 571 return mWm.getInputTransferToken(getWindowToken().asBinder()); 572 } 573 addWindowToken(WindowManager.LayoutParams attrs)574 private void addWindowToken(WindowManager.LayoutParams attrs) { 575 final WindowManager wm = 576 (WindowManager) mViewRoot.mContext.getSystemService(Context.WINDOW_SERVICE); 577 attrs.token = wm.getDefaultToken(); 578 } 579 580 /** 581 * Transfer the currently in progress touch gesture to the parent (if any) of this 582 * SurfaceControlViewHost. This requires that the SurfaceControlViewHost was created with an 583 * associated host {@link InputTransferToken}. 584 * 585 * @return Whether the touch stream was transferred. 586 * @deprecated Use {@link WindowManager#transferTouchGesture(InputTransferToken, 587 * InputTransferToken)} instead. 588 */ 589 @Deprecated transferTouchGestureToHost()590 public boolean transferTouchGestureToHost() { 591 if (mViewRoot == null) { 592 return false; 593 } 594 final WindowManager wm = (WindowManager) mViewRoot.mContext.getSystemService( 595 Context.WINDOW_SERVICE); 596 InputTransferToken embeddedToken = getInputTransferToken(); 597 InputTransferToken hostToken = mWm.mHostInputTransferToken; 598 if (embeddedToken == null || hostToken == null) { 599 Log.w(TAG, "Failed to transferTouchGestureToHost. Host or embedded token is null"); 600 return false; 601 } 602 return wm.transferTouchGesture(getInputTransferToken(), mWm.mHostInputTransferToken); 603 } 604 } 605