• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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