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 com.android.wm.shell.common; 18 19 import static android.view.WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED; 20 21 import android.annotation.NonNull; 22 import android.content.Context; 23 import android.content.res.Configuration; 24 import android.graphics.Point; 25 import android.graphics.Region; 26 import android.os.Bundle; 27 import android.os.IBinder; 28 import android.os.ParcelFileDescriptor; 29 import android.os.RemoteException; 30 import android.util.MergedConfiguration; 31 import android.util.Slog; 32 import android.util.SparseArray; 33 import android.view.Display; 34 import android.view.DragEvent; 35 import android.view.IScrollCaptureResponseListener; 36 import android.view.IWindow; 37 import android.view.IWindowManager; 38 import android.view.IWindowSession; 39 import android.view.IWindowSessionCallback; 40 import android.view.InsetsSourceControl; 41 import android.view.InsetsState; 42 import android.view.ScrollCaptureResponse; 43 import android.view.SurfaceControl; 44 import android.view.SurfaceControlViewHost; 45 import android.view.SurfaceSession; 46 import android.view.View; 47 import android.view.ViewGroup; 48 import android.view.WindowManager; 49 import android.view.WindowlessWindowManager; 50 import android.window.ClientWindowFrames; 51 52 import com.android.internal.os.IResultReceiver; 53 54 import java.util.HashMap; 55 56 /** 57 * Represents the "windowing" layer of the WM Shell. This layer allows shell components to place and 58 * manipulate windows without talking to WindowManager. 59 */ 60 public class SystemWindows { 61 private static final String TAG = "SystemWindows"; 62 63 private final SparseArray<PerDisplay> mPerDisplay = new SparseArray<>(); 64 private final HashMap<View, SurfaceControlViewHost> mViewRoots = new HashMap<>(); 65 private final DisplayController mDisplayController; 66 private final IWindowManager mWmService; 67 private IWindowSession mSession; 68 69 private final DisplayController.OnDisplaysChangedListener mDisplayListener = 70 new DisplayController.OnDisplaysChangedListener() { 71 @Override 72 public void onDisplayAdded(int displayId) { } 73 74 @Override 75 public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { 76 PerDisplay pd = mPerDisplay.get(displayId); 77 if (pd == null) { 78 return; 79 } 80 pd.updateConfiguration(newConfig); 81 } 82 83 @Override 84 public void onDisplayRemoved(int displayId) { } 85 }; 86 SystemWindows(DisplayController displayController, IWindowManager wmService)87 public SystemWindows(DisplayController displayController, IWindowManager wmService) { 88 mWmService = wmService; 89 mDisplayController = displayController; 90 mDisplayController.addDisplayWindowListener(mDisplayListener); 91 try { 92 mSession = wmService.openSession( 93 new IWindowSessionCallback.Stub() { 94 @Override 95 public void onAnimatorScaleChanged(float scale) {} 96 }); 97 } catch (RemoteException e) { 98 Slog.e(TAG, "Unable to create layer", e); 99 } 100 } 101 102 /** 103 * Adds a view to system-ui window management. 104 */ addView(View view, WindowManager.LayoutParams attrs, int displayId, @WindowManager.ShellRootLayer int shellRootLayer)105 public void addView(View view, WindowManager.LayoutParams attrs, int displayId, 106 @WindowManager.ShellRootLayer int shellRootLayer) { 107 PerDisplay pd = mPerDisplay.get(displayId); 108 if (pd == null) { 109 pd = new PerDisplay(displayId); 110 mPerDisplay.put(displayId, pd); 111 } 112 pd.addView(view, attrs, shellRootLayer); 113 } 114 115 /** 116 * Removes a view from system-ui window management. 117 * @param view 118 */ removeView(View view)119 public void removeView(View view) { 120 SurfaceControlViewHost root = mViewRoots.remove(view); 121 root.release(); 122 } 123 124 /** 125 * Updates the layout params of a view. 126 */ updateViewLayout(@onNull View view, ViewGroup.LayoutParams params)127 public void updateViewLayout(@NonNull View view, ViewGroup.LayoutParams params) { 128 SurfaceControlViewHost root = mViewRoots.get(view); 129 if (root == null || !(params instanceof WindowManager.LayoutParams)) { 130 return; 131 } 132 view.setLayoutParams(params); 133 root.relayout((WindowManager.LayoutParams) params); 134 } 135 136 /** 137 * Sets the accessibility window for the given {@param shellRootLayer}. 138 */ setShellRootAccessibilityWindow(int displayId, @WindowManager.ShellRootLayer int shellRootLayer, View view)139 public void setShellRootAccessibilityWindow(int displayId, 140 @WindowManager.ShellRootLayer int shellRootLayer, View view) { 141 PerDisplay pd = mPerDisplay.get(displayId); 142 if (pd == null) { 143 return; 144 } 145 pd.setShellRootAccessibilityWindow(shellRootLayer, view); 146 } 147 148 /** 149 * Sets the touchable region of a view's window. This will be cropped to the window size. 150 * @param view 151 * @param region 152 */ setTouchableRegion(@onNull View view, Region region)153 public void setTouchableRegion(@NonNull View view, Region region) { 154 SurfaceControlViewHost root = mViewRoots.get(view); 155 if (root == null) { 156 return; 157 } 158 WindowlessWindowManager wwm = root.getWindowlessWM(); 159 if (!(wwm instanceof SysUiWindowManager)) { 160 return; 161 } 162 ((SysUiWindowManager) wwm).setTouchableRegionForWindow(view, region); 163 } 164 165 /** 166 * Get the IWindow token for a specific root. 167 * 168 * @param windowType A window type from {@link WindowManager}. 169 */ getWindow(int displayId, int windowType)170 IWindow getWindow(int displayId, int windowType) { 171 PerDisplay pd = mPerDisplay.get(displayId); 172 if (pd == null) { 173 return null; 174 } 175 return pd.getWindow(windowType); 176 } 177 178 /** 179 * Gets the SurfaceControl associated with a root view. This is the same surface that backs the 180 * ViewRootImpl. 181 */ getViewSurface(View rootView)182 public SurfaceControl getViewSurface(View rootView) { 183 for (int i = 0; i < mPerDisplay.size(); ++i) { 184 for (int iWm = 0; iWm < mPerDisplay.valueAt(i).mWwms.size(); ++iWm) { 185 SurfaceControl out = mPerDisplay.valueAt(i).mWwms.valueAt(iWm) 186 .getSurfaceControlForWindow(rootView); 187 if (out != null) { 188 return out; 189 } 190 } 191 } 192 return null; 193 } 194 195 private class PerDisplay { 196 final int mDisplayId; 197 private final SparseArray<SysUiWindowManager> mWwms = new SparseArray<>(); 198 PerDisplay(int displayId)199 PerDisplay(int displayId) { 200 mDisplayId = displayId; 201 } 202 addView(View view, WindowManager.LayoutParams attrs, @WindowManager.ShellRootLayer int shellRootLayer)203 public void addView(View view, WindowManager.LayoutParams attrs, 204 @WindowManager.ShellRootLayer int shellRootLayer) { 205 SysUiWindowManager wwm = addRoot(shellRootLayer); 206 if (wwm == null) { 207 Slog.e(TAG, "Unable to create systemui root"); 208 return; 209 } 210 final Display display = mDisplayController.getDisplay(mDisplayId); 211 SurfaceControlViewHost viewRoot = 212 new SurfaceControlViewHost( 213 view.getContext(), display, wwm, true /* useSfChoreographer */); 214 attrs.flags |= FLAG_HARDWARE_ACCELERATED; 215 viewRoot.setView(view, attrs); 216 mViewRoots.put(view, viewRoot); 217 setShellRootAccessibilityWindow(shellRootLayer, view); 218 } 219 addRoot(@indowManager.ShellRootLayer int shellRootLayer)220 SysUiWindowManager addRoot(@WindowManager.ShellRootLayer int shellRootLayer) { 221 SysUiWindowManager wwm = mWwms.get(shellRootLayer); 222 if (wwm != null) { 223 return wwm; 224 } 225 SurfaceControl rootSurface = null; 226 ContainerWindow win = new ContainerWindow(); 227 try { 228 rootSurface = mWmService.addShellRoot(mDisplayId, win, shellRootLayer); 229 } catch (RemoteException e) { 230 } 231 if (rootSurface == null) { 232 Slog.e(TAG, "Unable to get root surfacecontrol for systemui"); 233 return null; 234 } 235 Context displayContext = mDisplayController.getDisplayContext(mDisplayId); 236 wwm = new SysUiWindowManager(mDisplayId, displayContext, rootSurface, win); 237 mWwms.put(shellRootLayer, wwm); 238 return wwm; 239 } 240 getWindow(int windowType)241 IWindow getWindow(int windowType) { 242 SysUiWindowManager wwm = mWwms.get(windowType); 243 if (wwm == null) { 244 return null; 245 } 246 return wwm.mContainerWindow; 247 } 248 setShellRootAccessibilityWindow(@indowManager.ShellRootLayer int shellRootLayer, View view)249 void setShellRootAccessibilityWindow(@WindowManager.ShellRootLayer int shellRootLayer, 250 View view) { 251 SysUiWindowManager wwm = mWwms.get(shellRootLayer); 252 if (wwm == null) { 253 return; 254 } 255 try { 256 mWmService.setShellRootAccessibilityWindow(mDisplayId, shellRootLayer, 257 view != null ? mViewRoots.get(view).getWindowToken() : null); 258 } catch (RemoteException e) { 259 Slog.e(TAG, "Error setting accessibility window for " + mDisplayId + ":" 260 + shellRootLayer, e); 261 } 262 } 263 updateConfiguration(Configuration configuration)264 void updateConfiguration(Configuration configuration) { 265 for (int i = 0; i < mWwms.size(); ++i) { 266 mWwms.valueAt(i).updateConfiguration(configuration); 267 } 268 } 269 } 270 271 /** 272 * A subclass of WindowlessWindowManager that provides insets to its viewroots. 273 */ 274 public class SysUiWindowManager extends WindowlessWindowManager { 275 final int mDisplayId; 276 ContainerWindow mContainerWindow; 277 final HashMap<IBinder, SurfaceControl> mLeashForWindow = 278 new HashMap<IBinder, SurfaceControl>(); SysUiWindowManager(int displayId, Context ctx, SurfaceControl rootSurface, ContainerWindow container)279 public SysUiWindowManager(int displayId, Context ctx, SurfaceControl rootSurface, 280 ContainerWindow container) { 281 super(ctx.getResources().getConfiguration(), rootSurface, null /* hostInputToken */); 282 mContainerWindow = container; 283 mDisplayId = displayId; 284 } 285 updateConfiguration(Configuration configuration)286 void updateConfiguration(Configuration configuration) { 287 setConfiguration(configuration); 288 } 289 getSurfaceControlForWindow(View rootView)290 SurfaceControl getSurfaceControlForWindow(View rootView) { 291 synchronized (this) { 292 return mLeashForWindow.get(getWindowBinder(rootView)); 293 } 294 } 295 attachToParentSurface(IWindow window, SurfaceControl.Builder b)296 protected void attachToParentSurface(IWindow window, SurfaceControl.Builder b) { 297 SurfaceControl leash = new SurfaceControl.Builder(new SurfaceSession()) 298 .setContainerLayer() 299 .setName("SystemWindowLeash") 300 .setHidden(false) 301 .setParent(mRootSurface) 302 .setCallsite("SysUiWIndowManager#attachToParentSurface").build(); 303 synchronized (this) { 304 mLeashForWindow.put(window.asBinder(), leash); 305 } 306 b.setParent(leash); 307 } 308 309 @Override remove(android.view.IWindow window)310 public void remove(android.view.IWindow window) throws RemoteException { 311 super.remove(window); 312 synchronized(this) { 313 IBinder token = window.asBinder(); 314 new SurfaceControl.Transaction().remove(mLeashForWindow.get(token)) 315 .apply(); 316 mLeashForWindow.remove(token); 317 } 318 } 319 setTouchableRegionForWindow(View rootView, Region region)320 void setTouchableRegionForWindow(View rootView, Region region) { 321 IBinder token = rootView.getWindowToken(); 322 if (token == null) { 323 return; 324 } 325 setTouchRegion(token, region); 326 } 327 } 328 329 static class ContainerWindow extends IWindow.Stub { ContainerWindow()330 ContainerWindow() {} 331 332 @Override resized(ClientWindowFrames frames, boolean reportDraw, MergedConfiguration newMergedConfiguration, boolean forceLayout, boolean alwaysConsumeSystemBars, int displayId)333 public void resized(ClientWindowFrames frames, boolean reportDraw, 334 MergedConfiguration newMergedConfiguration, boolean forceLayout, 335 boolean alwaysConsumeSystemBars, int displayId) {} 336 337 @Override locationInParentDisplayChanged(Point offset)338 public void locationInParentDisplayChanged(Point offset) {} 339 340 @Override insetsChanged(InsetsState insetsState, boolean willMove, boolean willResize)341 public void insetsChanged(InsetsState insetsState, boolean willMove, boolean willResize) {} 342 343 @Override insetsControlChanged(InsetsState insetsState, InsetsSourceControl[] activeControls, boolean willMove, boolean willResize)344 public void insetsControlChanged(InsetsState insetsState, 345 InsetsSourceControl[] activeControls, boolean willMove, boolean willResize) {} 346 347 @Override showInsets(int types, boolean fromIme)348 public void showInsets(int types, boolean fromIme) {} 349 350 @Override hideInsets(int types, boolean fromIme)351 public void hideInsets(int types, boolean fromIme) {} 352 353 @Override moved(int newX, int newY)354 public void moved(int newX, int newY) {} 355 356 @Override dispatchAppVisibility(boolean visible)357 public void dispatchAppVisibility(boolean visible) {} 358 359 @Override dispatchGetNewSurface()360 public void dispatchGetNewSurface() {} 361 362 @Override windowFocusChanged(boolean hasFocus, boolean inTouchMode)363 public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {} 364 365 @Override executeCommand(String command, String parameters, ParcelFileDescriptor out)366 public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {} 367 368 @Override closeSystemDialogs(String reason)369 public void closeSystemDialogs(String reason) {} 370 371 @Override dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, float zoom, boolean sync)372 public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, 373 float zoom, boolean sync) {} 374 375 @Override dispatchWallpaperCommand(String action, int x, int y, int z, Bundle extras, boolean sync)376 public void dispatchWallpaperCommand(String action, int x, int y, 377 int z, Bundle extras, boolean sync) {} 378 379 /* Drag/drop */ 380 @Override dispatchDragEvent(DragEvent event)381 public void dispatchDragEvent(DragEvent event) {} 382 383 @Override updatePointerIcon(float x, float y)384 public void updatePointerIcon(float x, float y) {} 385 386 @Override dispatchWindowShown()387 public void dispatchWindowShown() {} 388 389 @Override requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId)390 public void requestAppKeyboardShortcuts(IResultReceiver receiver, int deviceId) {} 391 392 @Override requestScrollCapture(IScrollCaptureResponseListener listener)393 public void requestScrollCapture(IScrollCaptureResponseListener listener) { 394 try { 395 listener.onScrollCaptureResponse( 396 new ScrollCaptureResponse.Builder() 397 .setDescription("Not Implemented") 398 .build()); 399 400 } catch (RemoteException ex) { 401 // ignore 402 } 403 } 404 } 405 } 406