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