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