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.graphics.PixelFormat; 24 import android.os.IBinder; 25 import android.os.Parcel; 26 import android.os.Parcelable; 27 import android.view.accessibility.IAccessibilityEmbeddedConnection; 28 29 import java.util.Objects; 30 31 /** 32 * Utility class for adding a View hierarchy to a {@link SurfaceControl}. The View hierarchy 33 * will render in to a root SurfaceControl, and receive input based on the SurfaceControl's 34 * placement on-screen. The primary usage of this class is to embed a View hierarchy from 35 * one process in to another. After the SurfaceControlViewHost has been set up in the embedded 36 * content provider, we can send the {@link SurfaceControlViewHost.SurfacePackage} 37 * to the host process. The host process can then attach the hierarchy to a SurfaceView within 38 * its own by calling 39 * {@link SurfaceView#setChildSurfacePackage}. 40 */ 41 public class SurfaceControlViewHost { 42 private final ViewRootImpl mViewRoot; 43 private WindowlessWindowManager mWm; 44 45 private SurfaceControl mSurfaceControl; 46 private IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection; 47 48 /** 49 * Package encapsulating a Surface hierarchy which contains interactive view 50 * elements. It's expected to get this object from 51 * {@link SurfaceControlViewHost#getSurfacePackage} afterwards it can be embedded within 52 * a SurfaceView by calling {@link SurfaceView#setChildSurfacePackage}. 53 * 54 * Note that each {@link SurfacePackage} must be released by calling 55 * {@link SurfacePackage#release}. However, if you use the recommended flow, 56 * the framework will automatically handle the lifetime for you. 57 * 58 * 1. When sending the package to the remote process, return it from an AIDL method 59 * or manually use FLAG_WRITE_RETURN_VALUE in writeToParcel. This will automatically 60 * release the package in the local process. 61 * 2. In the remote process, consume the package using SurfaceView. This way the 62 * SurfaceView will take over the lifetime and call {@link SurfacePackage#release} 63 * for the user. 64 * 65 * One final note: The {@link SurfacePackage} lifetime is totally de-coupled 66 * from the lifetime of the underlying {@link SurfaceControlViewHost}. Regardless 67 * of the lifetime of the package the user should still call 68 * {@link SurfaceControlViewHost#release} when finished. 69 */ 70 public static final class SurfacePackage implements Parcelable { 71 private SurfaceControl mSurfaceControl; 72 private final IAccessibilityEmbeddedConnection mAccessibilityEmbeddedConnection; 73 private final IBinder mInputToken; 74 SurfacePackage(SurfaceControl sc, IAccessibilityEmbeddedConnection connection, IBinder inputToken)75 SurfacePackage(SurfaceControl sc, IAccessibilityEmbeddedConnection connection, 76 IBinder inputToken) { 77 mSurfaceControl = sc; 78 mAccessibilityEmbeddedConnection = connection; 79 mInputToken = inputToken; 80 } 81 82 /** 83 * Constructs a copy of {@code SurfacePackage} with an independent lifetime. 84 * 85 * The caller can use this to create an independent copy in situations where ownership of 86 * the {@code SurfacePackage} would be transferred elsewhere, such as attaching to a 87 * {@code SurfaceView}, returning as {@code Binder} result value, etc. The caller is 88 * responsible for releasing this copy when its done. 89 * 90 * @param other {@code SurfacePackage} to create a copy of. 91 */ SurfacePackage(@onNull SurfacePackage other)92 public SurfacePackage(@NonNull SurfacePackage other) { 93 SurfaceControl otherSurfaceControl = other.mSurfaceControl; 94 if (otherSurfaceControl != null && otherSurfaceControl.isValid()) { 95 mSurfaceControl = new SurfaceControl(); 96 mSurfaceControl.copyFrom(otherSurfaceControl, "SurfacePackage"); 97 } 98 mAccessibilityEmbeddedConnection = other.mAccessibilityEmbeddedConnection; 99 mInputToken = other.mInputToken; 100 } 101 SurfacePackage(Parcel in)102 private SurfacePackage(Parcel in) { 103 mSurfaceControl = new SurfaceControl(); 104 mSurfaceControl.readFromParcel(in); 105 mAccessibilityEmbeddedConnection = IAccessibilityEmbeddedConnection.Stub.asInterface( 106 in.readStrongBinder()); 107 mInputToken = in.readStrongBinder(); 108 } 109 110 /** 111 * Use {@link SurfaceView#setChildSurfacePackage} or manually fix 112 * accessibility (see SurfaceView implementation). 113 * @hide 114 */ getSurfaceControl()115 public @NonNull SurfaceControl getSurfaceControl() { 116 return mSurfaceControl; 117 } 118 119 /** 120 * Gets an accessibility embedded connection interface for this SurfaceControlViewHost. 121 * 122 * @return {@link IAccessibilityEmbeddedConnection} interface. 123 * @hide 124 */ getAccessibilityEmbeddedConnection()125 public IAccessibilityEmbeddedConnection getAccessibilityEmbeddedConnection() { 126 return mAccessibilityEmbeddedConnection; 127 } 128 129 @Override describeContents()130 public int describeContents() { 131 return 0; 132 } 133 134 @Override writeToParcel(@onNull Parcel out, int flags)135 public void writeToParcel(@NonNull Parcel out, int flags) { 136 mSurfaceControl.writeToParcel(out, flags); 137 out.writeStrongBinder(mAccessibilityEmbeddedConnection.asBinder()); 138 out.writeStrongBinder(mInputToken); 139 } 140 141 /** 142 * Release the {@link SurfaceControl} associated with this package. 143 * It's not necessary to call this if you pass the package to 144 * {@link SurfaceView#setChildSurfacePackage} as {@link SurfaceView} will 145 * take ownership in that case. 146 */ release()147 public void release() { 148 if (mSurfaceControl != null) { 149 mSurfaceControl.release(); 150 } 151 mSurfaceControl = null; 152 } 153 154 /** 155 * Returns an input token used which can be used to request focus on the embedded surface. 156 * 157 * @hide 158 */ getInputToken()159 public IBinder getInputToken() { 160 return mInputToken; 161 } 162 163 public static final @NonNull Creator<SurfacePackage> CREATOR 164 = new Creator<SurfacePackage>() { 165 public SurfacePackage createFromParcel(Parcel in) { 166 return new SurfacePackage(in); 167 } 168 public SurfacePackage[] newArray(int size) { 169 return new SurfacePackage[size]; 170 } 171 }; 172 } 173 174 /** @hide */ SurfaceControlViewHost(@onNull Context c, @NonNull Display d, @NonNull WindowlessWindowManager wwm)175 public SurfaceControlViewHost(@NonNull Context c, @NonNull Display d, 176 @NonNull WindowlessWindowManager wwm) { 177 this(c, d, wwm, false /* useSfChoreographer */); 178 } 179 180 /** @hide */ SurfaceControlViewHost(@onNull Context c, @NonNull Display d, @NonNull WindowlessWindowManager wwm, boolean useSfChoreographer)181 public SurfaceControlViewHost(@NonNull Context c, @NonNull Display d, 182 @NonNull WindowlessWindowManager wwm, boolean useSfChoreographer) { 183 mWm = wwm; 184 mViewRoot = new ViewRootImpl(c, d, mWm, useSfChoreographer); 185 mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection(); 186 } 187 188 /** 189 * Construct a new SurfaceControlViewHost. The root Surface will be 190 * allocated internally and is accessible via getSurfacePackage(). 191 * 192 * The {@param hostToken} parameter, primarily used for ANR reporting, 193 * must be obtained from whomever will be hosting the embedded hierarchy. 194 * It's accessible from {@link SurfaceView#getHostToken}. 195 * 196 * @param context The Context object for your activity or application. 197 * @param display The Display the hierarchy will be placed on. 198 * @param hostToken The host token, as discussed above. 199 */ SurfaceControlViewHost(@onNull Context context, @NonNull Display display, @Nullable IBinder hostToken)200 public SurfaceControlViewHost(@NonNull Context context, @NonNull Display display, 201 @Nullable IBinder hostToken) { 202 mSurfaceControl = new SurfaceControl.Builder() 203 .setContainerLayer() 204 .setName("SurfaceControlViewHost") 205 .setCallsite("SurfaceControlViewHost") 206 .build(); 207 mWm = new WindowlessWindowManager(context.getResources().getConfiguration(), 208 mSurfaceControl, hostToken); 209 mViewRoot = new ViewRootImpl(context, display, mWm); 210 mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection(); 211 } 212 213 /** 214 * @hide 215 */ 216 @Override finalize()217 protected void finalize() throws Throwable { 218 // We aren't on the UI thread here so we need to pass false to 219 // doDie 220 mViewRoot.die(false /* immediate */); 221 } 222 223 224 /** 225 * Return a SurfacePackage for the root SurfaceControl of the embedded hierarchy. 226 * Rather than be directly reparented using {@link SurfaceControl.Transaction} this 227 * SurfacePackage should be passed to {@link SurfaceView#setChildSurfacePackage} 228 * which will not only reparent the Surface, but ensure the accessibility hierarchies 229 * are linked. 230 */ getSurfacePackage()231 public @Nullable SurfacePackage getSurfacePackage() { 232 if (mSurfaceControl != null && mAccessibilityEmbeddedConnection != null) { 233 return new SurfacePackage(mSurfaceControl, mAccessibilityEmbeddedConnection, 234 mViewRoot.getInputToken()); 235 } else { 236 return null; 237 } 238 } 239 240 /** 241 * Set the root view of the SurfaceControlViewHost. This view will render in to 242 * the SurfaceControl, and receive input based on the SurfaceControls positioning on 243 * screen. It will be laid as if it were in a window of the passed in width and height. 244 * 245 * @param view The View to add 246 * @param width The width to layout the View within, in pixels. 247 * @param height The height to layout the View within, in pixels. 248 */ setView(@onNull View view, int width, int height)249 public void setView(@NonNull View view, int width, int height) { 250 final WindowManager.LayoutParams lp = 251 new WindowManager.LayoutParams(width, height, 252 WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT); 253 setView(view, lp); 254 } 255 256 /** 257 * @hide 258 */ 259 @TestApi setView(@onNull View view, @NonNull WindowManager.LayoutParams attrs)260 public void setView(@NonNull View view, @NonNull WindowManager.LayoutParams attrs) { 261 Objects.requireNonNull(view); 262 attrs.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 263 view.setLayoutParams(attrs); 264 mViewRoot.setView(view, attrs, null); 265 } 266 267 /** 268 * @return The view passed to setView, or null if none has been passed. 269 */ getView()270 public @Nullable View getView() { 271 return mViewRoot.getView(); 272 } 273 274 /** 275 * @return the ViewRootImpl wrapped by this host. 276 * @hide 277 */ getWindowToken()278 public IWindow getWindowToken() { 279 return mViewRoot.mWindow; 280 } 281 282 /** 283 * @return the WindowlessWindowManager instance that this host is attached to. 284 * @hide 285 */ getWindowlessWM()286 public @NonNull WindowlessWindowManager getWindowlessWM() { 287 return mWm; 288 } 289 290 /** 291 * @hide 292 */ 293 @TestApi relayout(WindowManager.LayoutParams attrs)294 public void relayout(WindowManager.LayoutParams attrs) { 295 relayout(attrs, SurfaceControl.Transaction::apply); 296 } 297 298 /** 299 * Forces relayout and draw and allows to set a custom callback when it is finished 300 * @hide 301 */ relayout(WindowManager.LayoutParams attrs, WindowlessWindowManager.ResizeCompleteCallback callback)302 public void relayout(WindowManager.LayoutParams attrs, 303 WindowlessWindowManager.ResizeCompleteCallback callback) { 304 mViewRoot.setLayoutParams(attrs, false); 305 mViewRoot.setReportNextDraw(); 306 mWm.setCompletionCallback(mViewRoot.mWindow.asBinder(), callback); 307 } 308 309 /** 310 * Modify the size of the root view. 311 * 312 * @param width Width in pixels 313 * @param height Height in pixels 314 */ relayout(int width, int height)315 public void relayout(int width, int height) { 316 final WindowManager.LayoutParams lp = 317 new WindowManager.LayoutParams(width, height, 318 WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT); 319 relayout(lp); 320 } 321 322 /** 323 * Trigger the tear down of the embedded view hierarchy and release the SurfaceControl. 324 * This will result in onDispatchedFromWindow being dispatched to the embedded view hierarchy 325 * and render the object unusable. 326 */ release()327 public void release() { 328 // ViewRoot will release mSurfaceControl for us. 329 mViewRoot.die(true /* immediate */); 330 } 331 } 332