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