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 SurfacePackage(SurfaceControl sc, IAccessibilityEmbeddedConnection connection)74 SurfacePackage(SurfaceControl sc, IAccessibilityEmbeddedConnection connection) { 75 mSurfaceControl = sc; 76 mAccessibilityEmbeddedConnection = connection; 77 } 78 SurfacePackage(Parcel in)79 private SurfacePackage(Parcel in) { 80 mSurfaceControl = new SurfaceControl(); 81 mSurfaceControl.readFromParcel(in); 82 mAccessibilityEmbeddedConnection = IAccessibilityEmbeddedConnection.Stub.asInterface( 83 in.readStrongBinder()); 84 } 85 86 /** 87 * Use {@link SurfaceView#setChildSurfacePackage} or manually fix 88 * accessibility (see SurfaceView implementation). 89 * @hide 90 */ getSurfaceControl()91 public @NonNull SurfaceControl getSurfaceControl() { 92 return mSurfaceControl; 93 } 94 95 /** 96 * Gets an accessibility embedded connection interface for this SurfaceControlViewHost. 97 * 98 * @return {@link IAccessibilityEmbeddedConnection} interface. 99 * @hide 100 */ getAccessibilityEmbeddedConnection()101 public IAccessibilityEmbeddedConnection getAccessibilityEmbeddedConnection() { 102 return mAccessibilityEmbeddedConnection; 103 } 104 105 @Override describeContents()106 public int describeContents() { 107 return 0; 108 } 109 110 @Override writeToParcel(@onNull Parcel out, int flags)111 public void writeToParcel(@NonNull Parcel out, int flags) { 112 mSurfaceControl.writeToParcel(out, flags); 113 out.writeStrongBinder(mAccessibilityEmbeddedConnection.asBinder()); 114 } 115 116 /** 117 * Release the {@link SurfaceControl} associated with this package. 118 * It's not necessary to call this if you pass the package to 119 * {@link SurfaceView#setChildSurfacePackage} as {@link SurfaceView} will 120 * take ownership in that case. 121 */ release()122 public void release() { 123 if (mSurfaceControl != null) { 124 mSurfaceControl.release(); 125 } 126 mSurfaceControl = null; 127 } 128 129 public static final @NonNull Creator<SurfacePackage> CREATOR 130 = new Creator<SurfacePackage>() { 131 public SurfacePackage createFromParcel(Parcel in) { 132 return new SurfacePackage(in); 133 } 134 public SurfacePackage[] newArray(int size) { 135 return new SurfacePackage[size]; 136 } 137 }; 138 } 139 140 /** @hide */ SurfaceControlViewHost(@onNull Context c, @NonNull Display d, @NonNull WindowlessWindowManager wwm)141 public SurfaceControlViewHost(@NonNull Context c, @NonNull Display d, 142 @NonNull WindowlessWindowManager wwm) { 143 this(c, d, wwm, false /* useSfChoreographer */); 144 } 145 146 /** @hide */ SurfaceControlViewHost(@onNull Context c, @NonNull Display d, @NonNull WindowlessWindowManager wwm, boolean useSfChoreographer)147 public SurfaceControlViewHost(@NonNull Context c, @NonNull Display d, 148 @NonNull WindowlessWindowManager wwm, boolean useSfChoreographer) { 149 mWm = wwm; 150 mViewRoot = new ViewRootImpl(c, d, mWm, useSfChoreographer); 151 mViewRoot.forceDisableBLAST(); 152 mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection(); 153 } 154 155 /** 156 * Construct a new SurfaceControlViewHost. The root Surface will be 157 * allocated internally and is accessible via getSurfacePackage(). 158 * 159 * The {@param hostToken} parameter, primarily used for ANR reporting, 160 * must be obtained from whomever will be hosting the embedded hierarchy. 161 * It's accessible from {@link SurfaceView#getHostToken}. 162 * 163 * @param context The Context object for your activity or application. 164 * @param display The Display the hierarchy will be placed on. 165 * @param hostToken The host token, as discussed above. 166 */ SurfaceControlViewHost(@onNull Context context, @NonNull Display display, @Nullable IBinder hostToken)167 public SurfaceControlViewHost(@NonNull Context context, @NonNull Display display, 168 @Nullable IBinder hostToken) { 169 mSurfaceControl = new SurfaceControl.Builder() 170 .setContainerLayer() 171 .setName("SurfaceControlViewHost") 172 .setCallsite("SurfaceControlViewHost") 173 .build(); 174 mWm = new WindowlessWindowManager(context.getResources().getConfiguration(), 175 mSurfaceControl, hostToken); 176 mViewRoot = new ViewRootImpl(context, display, mWm); 177 mViewRoot.forceDisableBLAST(); 178 mAccessibilityEmbeddedConnection = mViewRoot.getAccessibilityEmbeddedConnection(); 179 } 180 181 /** 182 * @hide 183 */ 184 @Override finalize()185 protected void finalize() throws Throwable { 186 // We aren't on the UI thread here so we need to pass false to 187 // doDie 188 mViewRoot.die(false /* immediate */); 189 } 190 191 192 /** 193 * Return a SurfacePackage for the root SurfaceControl of the embedded hierarchy. 194 * Rather than be directly reparented using {@link SurfaceControl.Transaction} this 195 * SurfacePackage should be passed to {@link SurfaceView#setChildSurfacePackage} 196 * which will not only reparent the Surface, but ensure the accessibility hierarchies 197 * are linked. 198 */ getSurfacePackage()199 public @Nullable SurfacePackage getSurfacePackage() { 200 if (mSurfaceControl != null && mAccessibilityEmbeddedConnection != null) { 201 return new SurfacePackage(mSurfaceControl, mAccessibilityEmbeddedConnection); 202 } else { 203 return null; 204 } 205 } 206 207 /** 208 * @hide 209 */ 210 @TestApi setView(@onNull View view, @NonNull WindowManager.LayoutParams attrs)211 public void setView(@NonNull View view, @NonNull WindowManager.LayoutParams attrs) { 212 Objects.requireNonNull(view); 213 mViewRoot.setView(view, attrs, null); 214 } 215 216 /** 217 * Set the root view of the SurfaceControlViewHost. This view will render in to 218 * the SurfaceControl, and receive input based on the SurfaceControls positioning on 219 * screen. It will be laid as if it were in a window of the passed in width and height. 220 * 221 * @param view The View to add 222 * @param width The width to layout the View within, in pixels. 223 * @param height The height to layout the View within, in pixels. 224 */ setView(@onNull View view, int width, int height)225 public void setView(@NonNull View view, int width, int height) { 226 final WindowManager.LayoutParams lp = 227 new WindowManager.LayoutParams(width, height, 228 WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT); 229 lp.flags |= WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 230 setView(view, lp); 231 } 232 233 /** 234 * @return The view passed to setView, or null if none has been passed. 235 */ getView()236 public @Nullable View getView() { 237 return mViewRoot.getView(); 238 } 239 240 /** 241 * @return the ViewRootImpl wrapped by this host. 242 * @hide 243 */ getWindowToken()244 public IWindow getWindowToken() { 245 return mViewRoot.mWindow; 246 } 247 248 /** 249 * @return the WindowlessWindowManager instance that this host is attached to. 250 * @hide 251 */ getWindowlessWM()252 public @NonNull WindowlessWindowManager getWindowlessWM() { 253 return mWm; 254 } 255 256 /** 257 * @hide 258 */ 259 @TestApi relayout(WindowManager.LayoutParams attrs)260 public void relayout(WindowManager.LayoutParams attrs) { 261 mViewRoot.setLayoutParams(attrs, false); 262 mViewRoot.setReportNextDraw(); 263 mWm.setCompletionCallback(mViewRoot.mWindow.asBinder(), (SurfaceControl.Transaction t) -> { 264 t.apply(); 265 }); 266 } 267 268 /** 269 * Modify the size of the root view. 270 * 271 * @param width Width in pixels 272 * @param height Height in pixels 273 */ relayout(int width, int height)274 public void relayout(int width, int height) { 275 final WindowManager.LayoutParams lp = 276 new WindowManager.LayoutParams(width, height, 277 WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.TRANSPARENT); 278 relayout(lp); 279 } 280 281 /** 282 * Trigger the tear down of the embedded view hierarchy and release the SurfaceControl. 283 * This will result in onDispatchedFromWindow being dispatched to the embedded view hierarchy 284 * and render the object unusable. 285 */ release()286 public void release() { 287 // ViewRoot will release mSurfaceControl for us. 288 mViewRoot.die(true /* immediate */); 289 } 290 } 291