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