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.Nullable;
20 import android.content.res.Configuration;
21 import android.graphics.PixelFormat;
22 import android.graphics.Point;
23 import android.graphics.Rect;
24 import android.graphics.Region;
25 import android.os.IBinder;
26 import android.os.RemoteCallback;
27 import android.os.RemoteException;
28 import android.util.Log;
29 import android.util.MergedConfiguration;
30 import android.window.ClientWindowFrames;
31 
32 import java.util.HashMap;
33 import java.util.Objects;
34 
35 /**
36 * A simplistic implementation of IWindowSession. Rather than managing Surfaces
37 * as children of the display, it manages Surfaces as children of a given root.
38 *
39 * By parcelling the root surface, the app can offer another app content for embedding.
40 * @hide
41 */
42 public class WindowlessWindowManager implements IWindowSession {
43     private final static String TAG = "WindowlessWindowManager";
44 
45     private class State {
46         SurfaceControl mSurfaceControl;
47         WindowManager.LayoutParams mParams = new WindowManager.LayoutParams();
48         int mDisplayId;
49         IBinder mInputChannelToken;
50         Region mInputRegion;
State(SurfaceControl sc, WindowManager.LayoutParams p, int displayId, IBinder inputChannelToken)51         State(SurfaceControl sc, WindowManager.LayoutParams p, int displayId,
52                 IBinder inputChannelToken) {
53             mSurfaceControl = sc;
54             mParams.copyFrom(p);
55             mDisplayId = displayId;
56             mInputChannelToken = inputChannelToken;
57         }
58     };
59 
60     /**
61      * Used to store SurfaceControl we've built for clients to
62      * reconfigure them if relayout is called.
63      */
64     final HashMap<IBinder, State> mStateForWindow = new HashMap<IBinder, State>();
65 
66     public interface ResizeCompleteCallback {
finished(SurfaceControl.Transaction completion)67         public void finished(SurfaceControl.Transaction completion);
68     }
69 
70     final HashMap<IBinder, ResizeCompleteCallback> mResizeCompletionForWindow =
71         new HashMap<IBinder, ResizeCompleteCallback>();
72 
73     private final SurfaceSession mSurfaceSession = new SurfaceSession();
74     protected final SurfaceControl mRootSurface;
75     private final Configuration mConfiguration;
76     private final IWindowSession mRealWm;
77     private final IBinder mHostInputToken;
78 
79     private int mForceHeight = -1;
80     private int mForceWidth = -1;
81 
WindowlessWindowManager(Configuration c, SurfaceControl rootSurface, IBinder hostInputToken)82     public WindowlessWindowManager(Configuration c, SurfaceControl rootSurface,
83             IBinder hostInputToken) {
84         mRootSurface = rootSurface;
85         mConfiguration = new Configuration(c);
86         mRealWm = WindowManagerGlobal.getWindowSession();
87         mHostInputToken = hostInputToken;
88     }
89 
setConfiguration(Configuration configuration)90     protected void setConfiguration(Configuration configuration) {
91         mConfiguration.setTo(configuration);
92     }
93 
94     /**
95      * Utility API.
96      */
setCompletionCallback(IBinder window, ResizeCompleteCallback callback)97     void setCompletionCallback(IBinder window, ResizeCompleteCallback callback) {
98         if (mResizeCompletionForWindow.get(window) != null) {
99             Log.w(TAG, "Unsupported overlapping resizes");
100         }
101         mResizeCompletionForWindow.put(window, callback);
102     }
103 
setTouchRegion(IBinder window, @Nullable Region region)104     protected void setTouchRegion(IBinder window, @Nullable Region region) {
105         State state;
106         synchronized (this) {
107             // Do everything while locked so that we synchronize with relayout. This should be a
108             // very infrequent operation.
109             state = mStateForWindow.get(window);
110             if (state == null) {
111                 return;
112             }
113             if (Objects.equals(region, state.mInputRegion)) {
114                 return;
115             }
116             state.mInputRegion = region != null ? new Region(region) : null;
117             if (state.mInputChannelToken != null) {
118                 try {
119                     mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId,
120                             state.mSurfaceControl, state.mParams.flags, state.mParams.privateFlags,
121                             state.mInputRegion);
122                 } catch (RemoteException e) {
123                     Log.e(TAG, "Failed to update surface input channel: ", e);
124                 }
125             }
126         }
127     }
128 
attachToParentSurface(IWindow window, SurfaceControl.Builder b)129     protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) {
130         b.setParent(mRootSurface);
131     }
132 
133     /**
134      * IWindowSession implementation.
135      */
136     @Override
addToDisplay(IWindow window, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, InsetsVisibilities requestedVisibilities, InputChannel outInputChannel, InsetsState outInsetsState, InsetsSourceControl[] outActiveControls)137     public int addToDisplay(IWindow window, WindowManager.LayoutParams attrs,
138             int viewVisibility, int displayId, InsetsVisibilities requestedVisibilities,
139             InputChannel outInputChannel, InsetsState outInsetsState,
140             InsetsSourceControl[] outActiveControls) {
141         final SurfaceControl.Builder b = new SurfaceControl.Builder(mSurfaceSession)
142                 .setFormat(attrs.format)
143                 .setBLASTLayer()
144                 .setName(attrs.getTitle().toString())
145                 .setCallsite("WindowlessWindowManager.addToDisplay");
146         attachToParentSurface(window, b);
147         final SurfaceControl sc = b.build();
148 
149         if (((attrs.inputFeatures &
150                 WindowManager.LayoutParams.INPUT_FEATURE_NO_INPUT_CHANNEL) == 0)) {
151             try {
152                 if(mRealWm instanceof IWindowSession.Stub) {
153                     mRealWm.grantInputChannel(displayId,
154                         new SurfaceControl(sc, "WindowlessWindowManager.addToDisplay"),
155                         window, mHostInputToken, attrs.flags, attrs.privateFlags, attrs.type,
156                         outInputChannel);
157                 } else {
158                     mRealWm.grantInputChannel(displayId, sc, window, mHostInputToken, attrs.flags,
159                         attrs.privateFlags, attrs.type, outInputChannel);
160                 }
161             } catch (RemoteException e) {
162                 Log.e(TAG, "Failed to grant input to surface: ", e);
163             }
164         }
165 
166         final State state = new State(sc, attrs, displayId,
167                 outInputChannel != null ? outInputChannel.getToken() : null);
168         synchronized (this) {
169             mStateForWindow.put(window.asBinder(), state);
170         }
171 
172         final int res = WindowManagerGlobal.ADD_OKAY | WindowManagerGlobal.ADD_FLAG_APP_VISIBLE |
173                         WindowManagerGlobal.ADD_FLAG_USE_BLAST;
174 
175         // Include whether the window is in touch mode.
176         return isInTouchMode() ? res | WindowManagerGlobal.ADD_FLAG_IN_TOUCH_MODE : res;
177     }
178 
179     /**
180      * IWindowSession implementation. Currently this class doesn't need to support for multi-user.
181      */
182     @Override
addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs, int viewVisibility, int displayId, int userId, InsetsVisibilities requestedVisibilities, InputChannel outInputChannel, InsetsState outInsetsState, InsetsSourceControl[] outActiveControls)183     public int addToDisplayAsUser(IWindow window, WindowManager.LayoutParams attrs,
184             int viewVisibility, int displayId, int userId, InsetsVisibilities requestedVisibilities,
185             InputChannel outInputChannel, InsetsState outInsetsState,
186             InsetsSourceControl[] outActiveControls) {
187         return addToDisplay(window, attrs, viewVisibility, displayId, requestedVisibilities,
188                 outInputChannel, outInsetsState, outActiveControls);
189     }
190 
191     @Override
addToDisplayWithoutInputChannel(android.view.IWindow window, android.view.WindowManager.LayoutParams attrs, int viewVisibility, int layerStackId, android.view.InsetsState insetsState)192     public int addToDisplayWithoutInputChannel(android.view.IWindow window,
193             android.view.WindowManager.LayoutParams attrs, int viewVisibility, int layerStackId,
194             android.view.InsetsState insetsState) {
195         return 0;
196     }
197 
198     @Override
remove(android.view.IWindow window)199     public void remove(android.view.IWindow window) throws RemoteException {
200         mRealWm.remove(window);
201         State state;
202         synchronized (this) {
203             state = mStateForWindow.remove(window.asBinder());
204         }
205         if (state == null) {
206             throw new IllegalArgumentException(
207                     "Invalid window token (never added or removed already)");
208         }
209 
210         try (SurfaceControl.Transaction t = new SurfaceControl.Transaction()) {
211             t.remove(state.mSurfaceControl).apply();
212         }
213     }
214 
isOpaque(WindowManager.LayoutParams attrs)215     private boolean isOpaque(WindowManager.LayoutParams attrs) {
216         if (attrs.surfaceInsets != null && attrs.surfaceInsets.left != 0 ||
217                 attrs.surfaceInsets.top != 0 || attrs.surfaceInsets.right != 0 ||
218                 attrs.surfaceInsets.bottom != 0) {
219             return false;
220         }
221         return !PixelFormat.formatHasAlpha(attrs.format);
222     }
223 
isInTouchMode()224     private boolean isInTouchMode() {
225         try {
226             return WindowManagerGlobal.getWindowSession().getInTouchMode();
227         } catch (RemoteException e) {
228             Log.e(TAG, "Unable to check if the window is in touch mode", e);
229         }
230         return false;
231     }
232 
233     /** Access to package members for SystemWindow leashing
234      * @hide
235      */
getWindowBinder(View rootView)236     protected IBinder getWindowBinder(View rootView) {
237         final ViewRootImpl root = rootView.getViewRootImpl();
238         if (root == null) {
239             return null;
240         }
241         return root.mWindow.asBinder();
242     }
243 
244     /** @hide */
245     @Nullable
getSurfaceControl(View rootView)246     protected SurfaceControl getSurfaceControl(View rootView) {
247         final ViewRootImpl root = rootView.getViewRootImpl();
248         if (root == null) {
249             return null;
250         }
251         return getSurfaceControl(root.mWindow);
252     }
253 
254     /** @hide */
255     @Nullable
getSurfaceControl(IWindow window)256     protected SurfaceControl getSurfaceControl(IWindow window) {
257         final State s = mStateForWindow.get(window.asBinder());
258         if (s == null) {
259             return null;
260         }
261         return s.mSurfaceControl;
262     }
263 
264     @Override
relayout(IWindow window, WindowManager.LayoutParams inAttrs, int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber, ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration, SurfaceControl outSurfaceControl, InsetsState outInsetsState, InsetsSourceControl[] outActiveControls, Point outSurfaceSize)265     public int relayout(IWindow window, WindowManager.LayoutParams inAttrs,
266             int requestedWidth, int requestedHeight, int viewFlags, int flags, long frameNumber,
267             ClientWindowFrames outFrames, MergedConfiguration mergedConfiguration,
268             SurfaceControl outSurfaceControl, InsetsState outInsetsState,
269             InsetsSourceControl[] outActiveControls, Point outSurfaceSize) {
270         final State state;
271         synchronized (this) {
272             state = mStateForWindow.get(window.asBinder());
273         }
274         if (state == null) {
275             throw new IllegalArgumentException(
276                     "Invalid window token (never added or removed already)");
277         }
278         SurfaceControl sc = state.mSurfaceControl;
279         SurfaceControl.Transaction t = new SurfaceControl.Transaction();
280 
281         int attrChanges = 0;
282         if (inAttrs != null) {
283             attrChanges = state.mParams.copyFrom(inAttrs);
284         }
285         WindowManager.LayoutParams attrs = state.mParams;
286 
287         if (viewFlags == View.VISIBLE) {
288             outSurfaceSize.set(getSurfaceWidth(attrs), getSurfaceHeight(attrs));
289             t.setOpaque(sc, isOpaque(attrs)).show(sc).apply();
290             outSurfaceControl.copyFrom(sc, "WindowlessWindowManager.relayout");
291         } else {
292             t.hide(sc).apply();
293             outSurfaceControl.release();
294         }
295         outFrames.frame.set(0, 0, attrs.width, attrs.height);
296         outFrames.displayFrame.set(outFrames.frame);
297 
298         mergedConfiguration.setConfiguration(mConfiguration, mConfiguration);
299 
300         if ((attrChanges & WindowManager.LayoutParams.FLAGS_CHANGED) != 0
301                 && state.mInputChannelToken != null) {
302             try {
303                 if(mRealWm instanceof IWindowSession.Stub) {
304                     mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId,
305                             new SurfaceControl(sc, "WindowlessWindowManager.relayout"),
306                             attrs.flags, attrs.privateFlags, state.mInputRegion);
307                 } else {
308                     mRealWm.updateInputChannel(state.mInputChannelToken, state.mDisplayId, sc,
309                             attrs.flags, attrs.privateFlags, state.mInputRegion);
310                 }
311             } catch (RemoteException e) {
312                 Log.e(TAG, "Failed to update surface input channel: ", e);
313             }
314         }
315 
316         // Include whether the window is in touch mode.
317         return isInTouchMode() ? WindowManagerGlobal.RELAYOUT_RES_IN_TOUCH_MODE : 0;
318     }
319 
320     @Override
prepareToReplaceWindows(android.os.IBinder appToken, boolean childrenOnly)321     public void prepareToReplaceWindows(android.os.IBinder appToken, boolean childrenOnly) {
322     }
323 
324     @Override
outOfMemory(android.view.IWindow window)325     public boolean outOfMemory(android.view.IWindow window) {
326         return false;
327     }
328 
329     @Override
setInsets(android.view.IWindow window, int touchableInsets, android.graphics.Rect contentInsets, android.graphics.Rect visibleInsets, android.graphics.Region touchableRegion)330     public void setInsets(android.view.IWindow window, int touchableInsets,
331             android.graphics.Rect contentInsets, android.graphics.Rect visibleInsets,
332             android.graphics.Region touchableRegion) {
333     }
334 
335     @Override
finishDrawing(android.view.IWindow window, android.view.SurfaceControl.Transaction postDrawTransaction)336     public void finishDrawing(android.view.IWindow window,
337             android.view.SurfaceControl.Transaction postDrawTransaction) {
338         synchronized (this) {
339             final ResizeCompleteCallback c =
340                 mResizeCompletionForWindow.get(window.asBinder());
341             if (c == null) {
342                 // No one wanted the callback, but it wasn't necessarily unexpected.
343                 postDrawTransaction.apply();
344                 return;
345             }
346             c.finished(postDrawTransaction);
347             mResizeCompletionForWindow.remove(window.asBinder());
348         }
349     }
350 
351     @Override
setInTouchMode(boolean showFocus)352     public void setInTouchMode(boolean showFocus) {
353     }
354 
355     @Override
getInTouchMode()356     public boolean getInTouchMode() {
357         return false;
358     }
359 
360     @Override
performHapticFeedback(int effectId, boolean always)361     public boolean performHapticFeedback(int effectId, boolean always) {
362         return false;
363     }
364 
365     @Override
performDrag(android.view.IWindow window, int flags, android.view.SurfaceControl surface, int touchSource, float touchX, float touchY, float thumbCenterX, float thumbCenterY, android.content.ClipData data)366     public android.os.IBinder performDrag(android.view.IWindow window, int flags,
367             android.view.SurfaceControl surface, int touchSource, float touchX, float touchY,
368             float thumbCenterX, float thumbCenterY, android.content.ClipData data) {
369         return null;
370     }
371 
372     @Override
reportDropResult(android.view.IWindow window, boolean consumed)373     public void reportDropResult(android.view.IWindow window, boolean consumed) {
374     }
375 
376     @Override
cancelDragAndDrop(android.os.IBinder dragToken, boolean skipAnimation)377     public void cancelDragAndDrop(android.os.IBinder dragToken, boolean skipAnimation) {
378     }
379 
380     @Override
dragRecipientEntered(android.view.IWindow window)381     public void dragRecipientEntered(android.view.IWindow window) {
382     }
383 
384     @Override
dragRecipientExited(android.view.IWindow window)385     public void dragRecipientExited(android.view.IWindow window) {
386     }
387 
388     @Override
setWallpaperPosition(android.os.IBinder windowToken, float x, float y, float xstep, float ystep)389     public void setWallpaperPosition(android.os.IBinder windowToken, float x, float y,
390             float xstep, float ystep) {
391     }
392 
393     @Override
setWallpaperZoomOut(android.os.IBinder windowToken, float zoom)394     public void setWallpaperZoomOut(android.os.IBinder windowToken, float zoom) {
395     }
396 
397     @Override
setShouldZoomOutWallpaper(android.os.IBinder windowToken, boolean shouldZoom)398     public void setShouldZoomOutWallpaper(android.os.IBinder windowToken, boolean shouldZoom) {
399     }
400 
401     @Override
wallpaperOffsetsComplete(android.os.IBinder window)402     public void wallpaperOffsetsComplete(android.os.IBinder window) {
403     }
404 
405     @Override
setWallpaperDisplayOffset(android.os.IBinder windowToken, int x, int y)406     public void setWallpaperDisplayOffset(android.os.IBinder windowToken, int x, int y) {
407     }
408 
409     @Override
sendWallpaperCommand(android.os.IBinder window, java.lang.String action, int x, int y, int z, android.os.Bundle extras, boolean sync)410     public android.os.Bundle sendWallpaperCommand(android.os.IBinder window,
411             java.lang.String action, int x, int y, int z, android.os.Bundle extras, boolean sync) {
412         return null;
413     }
414 
415     @Override
wallpaperCommandComplete(android.os.IBinder window, android.os.Bundle result)416     public void wallpaperCommandComplete(android.os.IBinder window, android.os.Bundle result) {
417     }
418 
419     @Override
onRectangleOnScreenRequested(android.os.IBinder token, android.graphics.Rect rectangle)420     public void onRectangleOnScreenRequested(android.os.IBinder token,
421             android.graphics.Rect rectangle) {
422     }
423 
424     @Override
getWindowId(android.os.IBinder window)425     public android.view.IWindowId getWindowId(android.os.IBinder window) {
426         return null;
427     }
428 
429     @Override
pokeDrawLock(android.os.IBinder window)430     public void pokeDrawLock(android.os.IBinder window) {
431     }
432 
433     @Override
startMovingTask(android.view.IWindow window, float startX, float startY)434     public boolean startMovingTask(android.view.IWindow window, float startX, float startY) {
435         return false;
436     }
437 
438     @Override
finishMovingTask(android.view.IWindow window)439     public void finishMovingTask(android.view.IWindow window) {
440     }
441 
442     @Override
updatePointerIcon(android.view.IWindow window)443     public void updatePointerIcon(android.view.IWindow window) {
444     }
445 
446     @Override
updateDisplayContentLocation(android.view.IWindow window, int x, int y, int displayId)447     public void updateDisplayContentLocation(android.view.IWindow window, int x, int y,
448             int displayId) {
449     }
450 
451     @Override
updateTapExcludeRegion(android.view.IWindow window, android.graphics.Region region)452     public void updateTapExcludeRegion(android.view.IWindow window,
453             android.graphics.Region region) {
454     }
455 
456     @Override
updateRequestedVisibilities(IWindow window, InsetsVisibilities visibilities)457     public void updateRequestedVisibilities(IWindow window, InsetsVisibilities visibilities)  {
458     }
459 
460     @Override
reportSystemGestureExclusionChanged(android.view.IWindow window, java.util.List<android.graphics.Rect> exclusionRects)461     public void reportSystemGestureExclusionChanged(android.view.IWindow window,
462             java.util.List<android.graphics.Rect> exclusionRects) {
463     }
464 
465     @Override
grantInputChannel(int displayId, SurfaceControl surface, IWindow window, IBinder hostInputToken, int flags, int privateFlags, int type, InputChannel outInputChannel)466     public void grantInputChannel(int displayId, SurfaceControl surface, IWindow window,
467             IBinder hostInputToken, int flags, int privateFlags, int type,
468             InputChannel outInputChannel) {
469     }
470 
471     @Override
updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface, int flags, int privateFlags, Region region)472     public void updateInputChannel(IBinder channelToken, int displayId, SurfaceControl surface,
473             int flags, int privateFlags, Region region) {
474     }
475 
476     @Override
asBinder()477     public android.os.IBinder asBinder() {
478         return null;
479     }
480 
getSurfaceWidth(WindowManager.LayoutParams attrs)481     private int getSurfaceWidth(WindowManager.LayoutParams attrs) {
482       final Rect surfaceInsets = attrs.surfaceInsets;
483       return surfaceInsets != null
484           ? attrs.width + surfaceInsets.left + surfaceInsets.right : attrs.width;
485     }
getSurfaceHeight(WindowManager.LayoutParams attrs)486     private int getSurfaceHeight(WindowManager.LayoutParams attrs) {
487       final Rect surfaceInsets = attrs.surfaceInsets;
488       return surfaceInsets != null
489           ? attrs.height + surfaceInsets.top + surfaceInsets.bottom : attrs.height;
490     }
491 
492     @Override
grantEmbeddedWindowFocus(IWindow callingWindow, IBinder targetInputToken, boolean grantFocus)493     public void grantEmbeddedWindowFocus(IWindow callingWindow, IBinder targetInputToken,
494                                          boolean grantFocus) {
495     }
496 
497     @Override
generateDisplayHash(IWindow window, Rect boundsInWindow, String hashAlgorithm, RemoteCallback callback)498     public void generateDisplayHash(IWindow window, Rect boundsInWindow, String hashAlgorithm,
499             RemoteCallback callback) {
500     }
501 
502     @Override
dropForAccessibility(IWindow window, int x, int y)503     public boolean dropForAccessibility(IWindow window, int x, int y) {
504         return false;
505     }
506 }
507