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