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