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