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.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.TestApi; 22 import android.content.Context; 23 import android.content.res.Configuration; 24 import android.graphics.PixelFormat; 25 import android.graphics.Rect; 26 import android.os.IBinder; 27 import android.os.Parcel; 28 import android.os.Parcelable; 29 import android.os.RemoteException; 30 import android.util.Log; 31 import android.view.accessibility.IAccessibilityEmbeddedConnection; 32 import android.window.WindowTokenClient; 33 34 import java.util.Objects; 35 36 /** 37 * Utility class for adding a View hierarchy to a {@link SurfaceControl}. The View hierarchy 38 * will render in to a root SurfaceControl, and receive input based on the SurfaceControl's 39 * placement on-screen. The primary usage of this class is to embed a View hierarchy from 40 * one process in to another. After the SurfaceControlViewHost has been set up in the embedded 41 * content provider, we can send the {@link SurfaceControlViewHost.SurfacePackage} 42 * to the host process. The host process can then attach the hierarchy to a SurfaceView within 43 * its own by calling 44 * {@link SurfaceView#setChildSurfacePackage}. 45 */ 46 public class SurfaceControlViewHost { 47 private final static String TAG = "SurfaceControlViewHost"; 48 private final ViewRootImpl mViewRoot; 49 private WindowlessWindowManager mWm; 50 51 private SurfaceControl mSurfaceControl; 52 private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection; 53 private boolean mReleased = false; 54 55 private final class ISurfaceControlViewHostImpl extends ISurfaceControlViewHost.Stub { 56 @Override onConfigurationChanged(Configuration configuration)57 public void onConfigurationChanged(Configuration configuration) { 58 if (mViewRoot == null) { 59 return; 60 } 61 mViewRoot.mHandler.post(() -> { 62 if (mWm != null) { 63 mWm.setConfiguration(configuration); 64 } 65 if (mViewRoot != null) { 66 mViewRoot.forceWmRelayout(); 67 } 68 }); 69 } 70 71 @Override onDispatchDetachedFromWindow()72 public void onDispatchDetachedFromWindow() { 73 if (mViewRoot == null) { 74 return; 75 } 76 mViewRoot.mHandler.post(() -> { 77 release(); 78 }); 79 } 80 81 @Override onInsetsChanged(InsetsState state, Rect frame)82 public void onInsetsChanged(InsetsState state, Rect frame) { 83 if (mViewRoot != null) { 84 mViewRoot.mHandler.post(() -> { 85 mViewRoot.setOverrideInsetsFrame(frame); 86 }); 87 } 88 mWm.setInsetsState(state); 89 } 90 } 91 92 private ISurfaceControlViewHost mRemoteInterface = new ISurfaceControlViewHostImpl(); 93 94 /** 95 * Package encapsulating a Surface hierarchy which contains interactive view 96 * elements. It's expected to get this object from 97 * {@link SurfaceControlViewHost#getSurfacePackage} afterwards it can be embedded within 98 * a SurfaceView by calling {@link SurfaceView#setChildSurfacePackage}. 99 * 100 * Note that each {@link SurfacePackage} must be released by calling 101 * {@link SurfacePackage#release}. However, if you use the recommended flow, 102 * the framework will automatically handle the lifetime for you. 103 * 104 * 1. When sending the package to the remote process, return it from an AIDL method 105 * or manually use FLAG_WRITE_RETURN_VALUE in writeToParcel. This will automatically 106 * release the package in the local process. 107 * 2. In the remote process, consume the package using SurfaceView. This way the 108 * SurfaceView will take over the lifetime and call {@link SurfacePackage#release} 109 * for the user. 110 * 111 * One final note: The {@link SurfacePackage} lifetime is totally de-coupled 112 * from the lifetime of the underlying {@link SurfaceControlViewHost}. Regardless 113 * of the lifetime of the package the user should still call 114 * {@link SurfaceControlViewHost#release} when finished. 115 */ 116 public static final class SurfacePackage implements Parcelable { 117 private SurfaceControl mSurfaceControl; 118 private final IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection; 119 private final IBinder mInputToken; 120 private final ISurfaceControlViewHost mRemoteInterface; 121 SurfacePackage(SurfaceControl sc, IAccessibilityEmbeddedConnection connection, IBinder inputToken, ISurfaceControlViewHost ri)122 SurfacePackage(SurfaceControl sc, IAccessibilityEmbeddedConnection connection, 123 IBinder inputToken, ISurfaceControlViewHost ri) { 124 mSurfaceControl = sc; 125 mAccessibilityEmbeddedConnection = connection; 126 mInputToken = inputToken; 127 mRemoteInterface = ri; 128 } 129 130 /** 131 * Constructs a copy of {@code SurfacePackage} with an independent lifetime. 132 * 133 * The caller can use this to create an independent copy in situations where ownership of 134 * the {@code SurfacePackage} would be transferred elsewhere, such as attaching to a 135 * {@code SurfaceView}, returning as {@code Binder} result value, etc. The caller is 136 * responsible for releasing this copy when its done. 137 * 138 * @param other {@code SurfacePackage} to create a copy of. 139 */ SurfacePackage(@onNull SurfacePackage other)140 public SurfacePackage(@NonNull SurfacePackage other) { 141 SurfaceControl otherSurfaceControl = other.mSurfaceControl; 142 if (otherSurfaceControl != null && otherSurfaceControl.isValid()) { 143 mSurfaceControl = new SurfaceControl(); 144 mSurfaceControl.copyFrom(otherSurfaceControl, "SurfacePackage"); 145 } 146 mAccessibilityEmbeddedConnection = other.mAccessibilityEmbeddedConnection; 147 mInputToken = other.mInputToken; 148 mRemoteInterface = other.mRemoteInterface; 149 } 150 SurfacePackage(Parcel in)151 private SurfacePackage(Parcel in) { 152 mSurfaceControl = new SurfaceControl(); 153 mSurfaceControl.readFromParcel(in); 154 mAccessibilityEmbeddedConnection = IAccessibilityEmbeddedConnection.Stub.asInterface( 155 in.readStrongBinder()); 156 mInputToken = in.readStrongBinder(); 157 mRemoteInterface = ISurfaceControlViewHost.Stub.asInterface( 158 in.readStrongBinder()); 159 } 160 161 /** 162 * Use {@link SurfaceView#setChildSurfacePackage} or manually fix 163 * accessibility (see SurfaceView implementation). 164 * @hide 165 */ getSurfaceControl()166 public @NonNull SurfaceControl getSurfaceControl() { 167 return mSurfaceControl; 168 } 169 170 /** 171 * Gets an accessibility embedded connection interface for this SurfaceControlViewHost. 172 * 173 * @return {@link IAccessibilityEmbeddedConnection} interface. 174 * @hide 175 */ getAccessibilityEmbeddedConnection()176 public IAccessibilityEmbeddedConnection getAccessibilityEmbeddedConnection() { 177 return mAccessibilityEmbeddedConnection; 178 } 179 180 /** 181 * @hide 182 */ getRemoteInterface()183 public ISurfaceControlViewHost getRemoteInterface() { 184 return mRemoteInterface; 185 } 186 187 /** 188 * Forward a configuration to the remote SurfaceControlViewHost. 189 * This will cause View#onConfigurationChanged to be invoked on the remote 190 * end. This does not automatically cause the SurfaceControlViewHost 191 * to be resized. The root View of a SurfaceControlViewHost 192 * is more akin to a PopupWindow in that the size is user specified 193 * independent of configuration width and height. 194 * 195 * In order to receive the configuration change via 196 * {@link View#onConfigurationChanged}, the context used with the 197 * SurfaceControlViewHost and it's embedded view hierarchy must 198 * be a WindowContext obtained from {@link Context#createWindowContext}. 199 * 200 * If a regular service context is used, then your embedded view hierarchy 201 * will always perceive the global configuration. 202 * 203 * @param c The configuration to forward 204 */ notifyConfigurationChanged(@onNull Configuration c)205 public void notifyConfigurationChanged(@NonNull Configuration c) { 206 try { 207 getRemoteInterface().onConfigurationChanged(c); 208 } catch (RemoteException e) { 209 e.rethrowAsRuntimeException(); 210 } 211 } 212 213 /** 214 * Tear down the remote SurfaceControlViewHost and cause 215 * View#onDetachedFromWindow to be invoked on the other side. 216 */ notifyDetachedFromWindow()217 public void notifyDetachedFromWindow() { 218 try { 219 getRemoteInterface().onDispatchDetachedFromWindow(); 220 } catch (RemoteException e) { 221 e.rethrowAsRuntimeException(); 222 } 223 } 224 225 @Override describeContents()226 public int describeContents() { 227 return 0; 228 } 229 230 @Override writeToParcel(@onNull Parcel out, int flags)231 public void writeToParcel(@NonNull Parcel out, int flags) { 232 mSurfaceControl.writeToParcel(out, flags); 233 out.writeStrongBinder(mAccessibilityEmbeddedConnection.asBinder()); 234 out.writeStrongBinder(mInputToken); 235 out.writeStrongBinder(mRemoteInterface.asBinder()); 236 } 237 238 /** 239 * Release the {@link SurfaceControl} associated with this package. 240 * It's not necessary to call this if you pass the package to 241 * {@link SurfaceView#setChildSurfacePackage} as {@link SurfaceView} will 242 * take ownership in that case. 243 */ release()244 public void release() { 245 if (mSurfaceControl != null) { 246 mSurfaceControl.release(); 247 } 248 mSurfaceControl = null; 249 } 250 251 /** 252 * Returns an input token used which can be used to request focus on the embedded surface. 253 * 254 * @hide 255 */ getInputToken()256 public IBinder getInputToken() { 257 return mInputToken; 258 } 259 260 public static final @NonNull Creator<SurfacePackage> CREATOR 261 = new Creator<SurfacePackage>() { 262 public SurfacePackage createFromParcel(Parcel in) { 263 return new SurfacePackage(in); 264 } 265 public SurfacePackage[] newArray(int size) { 266 return new SurfacePackage[size]; 267 } 268 }; 269 } 270 271 /** @hide */ SurfaceControlViewHost(@onNull Context c, @NonNull Display d, @NonNull WindowlessWindowManager wwm)272 public SurfaceControlViewHost(@NonNull Context c, @NonNull Display d, 273 @NonNull WindowlessWindowManager wwm) { 274 mWm = wwm; 275 mViewRoot = new ViewRootImpl(c, d, mWm, new WindowlessWindowLayout()); 276 addConfigCallback(c, d); 277 278 WindowManagerGlobal.getInstance().addWindowlessRoot(mViewRoot); 279 280 mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection(); 281 } 282 283 /** 284 * Construct a new SurfaceControlViewHost. The root Surface will be 285 * allocated internally and is accessible via getSurfacePackage(). 286 * 287 * The {@param hostToken} parameter, primarily used for ANR reporting, 288 * must be obtained from whomever will be hosting the embedded hierarchy. 289 * It's accessible from {@link SurfaceView#getHostToken}. 290 * 291 * @param context The Context object for your activity or application. 292 * @param display The Display the hierarchy will be placed on. 293 * @param hostToken The host token, as discussed above. 294 */ SurfaceControlViewHost(@onNull Context context, @NonNull Display display, @Nullable IBinder hostToken)295 public SurfaceControlViewHost(@NonNull Context context, @NonNull Display display, 296 @Nullable IBinder hostToken) { 297 mSurfaceControl = new SurfaceControl.Builder() 298 .setContainerLayer() 299 .setName("SurfaceControlViewHost") 300 .setCallsite("SurfaceControlViewHost") 301 .build(); 302 mWm = new WindowlessWindowManager(context.getResources().getConfiguration(), 303 mSurfaceControl, hostToken); 304 305 mViewRoot = new ViewRootImpl(context, display, mWm, new WindowlessWindowLayout()); 306 addConfigCallback(context, display); 307 308 WindowManagerGlobal.getInstance().addWindowlessRoot(mViewRoot); 309 310 mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection(); 311 } 312 addConfigCallback(Context c, Display d)313 private void addConfigCallback(Context c, Display d) { 314 final IBinder token = c.getWindowContextToken(); 315 mViewRoot.addConfigCallback((conf) -> { 316 if (token instanceof WindowTokenClient) { 317 final WindowTokenClient w = (WindowTokenClient) token; 318 w.onConfigurationChanged(conf, d.getDisplayId(), true); 319 } 320 }); 321 } 322 323 /** 324 * @hide 325 */ 326 @Override finalize()327 protected void finalize() throws Throwable { 328 if (mReleased) { 329 return; 330 } 331 Log.e(TAG, "SurfaceControlViewHost finalized without being released: " + this); 332 // We aren't on the UI thread here so we need to pass false to doDie 333 mViewRoot.die(false /* immediate */); 334 WindowManagerGlobal.getInstance().removeWindowlessRoot(mViewRoot); 335 } 336 337 /** 338 * Return a SurfacePackage for the root SurfaceControl of the embedded hierarchy. 339 * Rather than be directly reparented using {@link SurfaceControl.Transaction} this 340 * SurfacePackage should be passed to {@link SurfaceView#setChildSurfacePackage} 341 * which will not only reparent the Surface, but ensure the accessibility hierarchies 342 * are linked. 343 */ getSurfacePackage()344 public @Nullable SurfacePackage getSurfacePackage() { 345 if (mSurfaceControl != null && mAccessibilityEmbeddedConnection != null) { 346 return new SurfacePackage(new SurfaceControl(mSurfaceControl, "getSurfacePackage"), 347 mAccessibilityEmbeddedConnection, 348 mWm.getFocusGrantToken(), mRemoteInterface); 349 } else { 350 return null; 351 } 352 } 353 354 /** 355 * Set the root view of the SurfaceControlViewHost. This view will render in to 356 * the SurfaceControl, and receive input based on the SurfaceControls positioning on 357 * screen. It will be laid as if it were in a window of the passed in width and height. 358 * 359 * @param view The View to add 360 * @param width The width to layout the View within, in pixels. 361 * @param height The height to layout the View within, in pixels. 362 */ setView(@onNull View view, int width, int height)363 public void setView(@NonNull View view, int width, int height) { 364 final WindowManager.LayoutParams lp = 365 new WindowManager.LayoutParams(width, height, 366 WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT); 367 setView(view, lp); 368 } 369 370 /** 371 * @hide 372 */ 373 @TestApi setView(@onNull View view, @NonNull WindowManager.LayoutParams attrs)374 public void setView(@NonNull View view, @NonNull WindowManager.LayoutParams attrs) { 375 Objects.requireNonNull(view); 376 attrs.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 377 view.setLayoutParams(attrs); 378 mViewRoot.setView(view, attrs, null); 379 } 380 381 /** 382 * @return The view passed to setView, or null if none has been passed. 383 */ getView()384 public @Nullable View getView() { 385 return mViewRoot.getView(); 386 } 387 388 /** 389 * @return the ViewRootImpl wrapped by this host. 390 * @hide 391 */ getWindowToken()392 public IWindow getWindowToken() { 393 return mViewRoot.mWindow; 394 } 395 396 /** 397 * @return the WindowlessWindowManager instance that this host is attached to. 398 * @hide 399 */ getWindowlessWM()400 public @NonNull WindowlessWindowManager getWindowlessWM() { 401 return mWm; 402 } 403 404 /** 405 * @hide 406 */ 407 @TestApi relayout(WindowManager.LayoutParams attrs)408 public void relayout(WindowManager.LayoutParams attrs) { 409 relayout(attrs, SurfaceControl.Transaction::apply); 410 } 411 412 /** 413 * Forces relayout and draw and allows to set a custom callback when it is finished 414 * @hide 415 */ relayout(WindowManager.LayoutParams attrs, WindowlessWindowManager.ResizeCompleteCallback callback)416 public void relayout(WindowManager.LayoutParams attrs, 417 WindowlessWindowManager.ResizeCompleteCallback callback) { 418 mViewRoot.setLayoutParams(attrs, false); 419 mViewRoot.setReportNextDraw(true /* syncBuffer */, "scvh_relayout"); 420 mWm.setCompletionCallback(mViewRoot.mWindow.asBinder(), callback); 421 } 422 423 /** 424 * Modify the size of the root view. 425 * 426 * @param width Width in pixels 427 * @param height Height in pixels 428 */ relayout(int width, int height)429 public void relayout(int width, int height) { 430 final WindowManager.LayoutParams lp = 431 new WindowManager.LayoutParams(width, height, 432 WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT); 433 relayout(lp); 434 } 435 436 /** 437 * Trigger the tear down of the embedded view hierarchy and release the SurfaceControl. 438 * This will result in onDispatchedFromWindow being dispatched to the embedded view hierarchy 439 * and render the object unusable. 440 */ release()441 public void release() { 442 // ViewRoot will release mSurfaceControl for us. 443 mViewRoot.die(true /* immediate */); 444 WindowManagerGlobal.getInstance().removeWindowlessRoot(mViewRoot); 445 mReleased = true; 446 } 447 448 /** 449 * @hide 450 */ getFocusGrantToken()451 public IBinder getFocusGrantToken() { 452 return mWm.getFocusGrantToken(); 453 } 454 } 455