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 android.annotation.Nullable; 20 import android.content.Context; 21 import android.content.res.Configuration; 22 import android.graphics.Rect; 23 import android.hardware.display.DisplayManager; 24 import android.os.RemoteException; 25 import android.util.ArraySet; 26 import android.util.Slog; 27 import android.util.SparseArray; 28 import android.view.Display; 29 import android.view.IDisplayWindowListener; 30 import android.view.IWindowManager; 31 import android.view.InsetsState; 32 33 import androidx.annotation.BinderThread; 34 35 import com.android.wm.shell.common.DisplayChangeController.OnDisplayChangingListener; 36 import com.android.wm.shell.common.annotations.ShellMainThread; 37 import com.android.wm.shell.sysui.ShellInit; 38 39 import java.util.ArrayList; 40 import java.util.List; 41 import java.util.Set; 42 43 /** 44 * This module deals with display rotations coming from WM. When WM starts a rotation: after it has 45 * frozen the screen, it will call into this class. This will then call all registered local 46 * controllers and give them a chance to queue up task changes to be applied synchronously with that 47 * rotation. 48 */ 49 public class DisplayController { 50 private static final String TAG = "DisplayController"; 51 52 private final ShellExecutor mMainExecutor; 53 private final Context mContext; 54 private final IWindowManager mWmService; 55 private final DisplayChangeController mChangeController; 56 private final IDisplayWindowListener mDisplayContainerListener; 57 58 private final SparseArray<DisplayRecord> mDisplays = new SparseArray<>(); 59 private final ArrayList<OnDisplaysChangedListener> mDisplayChangedListeners = new ArrayList<>(); 60 DisplayController(Context context, IWindowManager wmService, ShellInit shellInit, ShellExecutor mainExecutor)61 public DisplayController(Context context, IWindowManager wmService, ShellInit shellInit, 62 ShellExecutor mainExecutor) { 63 mMainExecutor = mainExecutor; 64 mContext = context; 65 mWmService = wmService; 66 // TODO: Inject this instead 67 mChangeController = new DisplayChangeController(mWmService, shellInit, mainExecutor); 68 mDisplayContainerListener = new DisplayWindowListenerImpl(); 69 // Note, add this after DisplaceChangeController is constructed to ensure that is 70 // initialized first 71 shellInit.addInitCallback(this::onInit, this); 72 } 73 74 /** 75 * Initializes the window listener. 76 */ onInit()77 public void onInit() { 78 try { 79 int[] displayIds = mWmService.registerDisplayWindowListener(mDisplayContainerListener); 80 for (int i = 0; i < displayIds.length; i++) { 81 onDisplayAdded(displayIds[i]); 82 } 83 } catch (RemoteException e) { 84 throw new RuntimeException("Unable to register display controller"); 85 } 86 } 87 88 /** Get the DisplayChangeController. */ getChangeController()89 public DisplayChangeController getChangeController() { 90 return mChangeController; 91 } 92 93 /** 94 * Gets a display by id from DisplayManager. 95 */ getDisplay(int displayId)96 public Display getDisplay(int displayId) { 97 final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); 98 return displayManager.getDisplay(displayId); 99 } 100 101 /** 102 * Gets the DisplayLayout associated with a display. 103 */ getDisplayLayout(int displayId)104 public @Nullable DisplayLayout getDisplayLayout(int displayId) { 105 final DisplayRecord r = mDisplays.get(displayId); 106 return r != null ? r.mDisplayLayout : null; 107 } 108 109 /** 110 * Gets a display-specific context for a display. 111 */ getDisplayContext(int displayId)112 public @Nullable Context getDisplayContext(int displayId) { 113 final DisplayRecord r = mDisplays.get(displayId); 114 return r != null ? r.mContext : null; 115 } 116 117 /** 118 * Get the InsetsState of a display. 119 */ getInsetsState(int displayId)120 public InsetsState getInsetsState(int displayId) { 121 final DisplayRecord r = mDisplays.get(displayId); 122 return r != null ? r.mInsetsState : null; 123 } 124 125 /** 126 * Updates the insets for a given display. 127 */ updateDisplayInsets(int displayId, InsetsState state)128 public void updateDisplayInsets(int displayId, InsetsState state) { 129 final DisplayRecord r = mDisplays.get(displayId); 130 if (r != null) { 131 r.setInsets(state); 132 } 133 } 134 135 /** 136 * Add a display window-container listener. It will get notified whenever a display's 137 * configuration changes or when displays are added/removed from the WM hierarchy. 138 */ addDisplayWindowListener(OnDisplaysChangedListener listener)139 public void addDisplayWindowListener(OnDisplaysChangedListener listener) { 140 synchronized (mDisplays) { 141 if (mDisplayChangedListeners.contains(listener)) { 142 return; 143 } 144 mDisplayChangedListeners.add(listener); 145 for (int i = 0; i < mDisplays.size(); ++i) { 146 listener.onDisplayAdded(mDisplays.keyAt(i)); 147 } 148 } 149 } 150 151 /** 152 * Remove a display window-container listener. 153 */ removeDisplayWindowListener(OnDisplaysChangedListener listener)154 public void removeDisplayWindowListener(OnDisplaysChangedListener listener) { 155 synchronized (mDisplays) { 156 mDisplayChangedListeners.remove(listener); 157 } 158 } 159 160 /** 161 * Adds a display rotation controller. 162 */ addDisplayChangingController(OnDisplayChangingListener controller)163 public void addDisplayChangingController(OnDisplayChangingListener controller) { 164 mChangeController.addDisplayChangeListener(controller); 165 } 166 167 /** 168 * Removes a display rotation controller. 169 */ removeDisplayChangingController(OnDisplayChangingListener controller)170 public void removeDisplayChangingController(OnDisplayChangingListener controller) { 171 mChangeController.removeDisplayChangeListener(controller); 172 } 173 onDisplayAdded(int displayId)174 private void onDisplayAdded(int displayId) { 175 synchronized (mDisplays) { 176 if (mDisplays.get(displayId) != null) { 177 return; 178 } 179 final Display display = getDisplay(displayId); 180 if (display == null) { 181 // It's likely that the display is private to some app and thus not 182 // accessible by system-ui. 183 return; 184 } 185 186 final Context context = (displayId == Display.DEFAULT_DISPLAY) 187 ? mContext 188 : mContext.createDisplayContext(display); 189 final DisplayRecord record = new DisplayRecord(displayId); 190 record.setDisplayLayout(context, new DisplayLayout(context, display)); 191 mDisplays.put(displayId, record); 192 for (int i = 0; i < mDisplayChangedListeners.size(); ++i) { 193 mDisplayChangedListeners.get(i).onDisplayAdded(displayId); 194 } 195 } 196 } 197 onDisplayConfigurationChanged(int displayId, Configuration newConfig)198 private void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { 199 synchronized (mDisplays) { 200 final DisplayRecord dr = mDisplays.get(displayId); 201 if (dr == null) { 202 Slog.w(TAG, "Skipping Display Configuration change on non-added" 203 + " display."); 204 return; 205 } 206 final Display display = getDisplay(displayId); 207 if (display == null) { 208 Slog.w(TAG, "Skipping Display Configuration change on invalid" 209 + " display. It may have been removed."); 210 return; 211 } 212 final Context perDisplayContext = (displayId == Display.DEFAULT_DISPLAY) 213 ? mContext 214 : mContext.createDisplayContext(display); 215 final Context context = perDisplayContext.createConfigurationContext(newConfig); 216 dr.setDisplayLayout(context, new DisplayLayout(context, display)); 217 for (int i = 0; i < mDisplayChangedListeners.size(); ++i) { 218 mDisplayChangedListeners.get(i).onDisplayConfigurationChanged( 219 displayId, newConfig); 220 } 221 } 222 } 223 onDisplayRemoved(int displayId)224 private void onDisplayRemoved(int displayId) { 225 synchronized (mDisplays) { 226 if (mDisplays.get(displayId) == null) { 227 return; 228 } 229 for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { 230 mDisplayChangedListeners.get(i).onDisplayRemoved(displayId); 231 } 232 mDisplays.remove(displayId); 233 } 234 } 235 onFixedRotationStarted(int displayId, int newRotation)236 private void onFixedRotationStarted(int displayId, int newRotation) { 237 synchronized (mDisplays) { 238 if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) { 239 Slog.w(TAG, "Skipping onFixedRotationStarted on unknown" 240 + " display, displayId=" + displayId); 241 return; 242 } 243 for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { 244 mDisplayChangedListeners.get(i).onFixedRotationStarted( 245 displayId, newRotation); 246 } 247 } 248 } 249 onFixedRotationFinished(int displayId)250 private void onFixedRotationFinished(int displayId) { 251 synchronized (mDisplays) { 252 if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) { 253 Slog.w(TAG, "Skipping onFixedRotationFinished on unknown" 254 + " display, displayId=" + displayId); 255 return; 256 } 257 for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { 258 mDisplayChangedListeners.get(i).onFixedRotationFinished(displayId); 259 } 260 } 261 } 262 onKeepClearAreasChanged(int displayId, Set<Rect> restricted, Set<Rect> unrestricted)263 private void onKeepClearAreasChanged(int displayId, Set<Rect> restricted, 264 Set<Rect> unrestricted) { 265 synchronized (mDisplays) { 266 if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) { 267 Slog.w(TAG, "Skipping onKeepClearAreasChanged on unknown" 268 + " display, displayId=" + displayId); 269 return; 270 } 271 for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { 272 mDisplayChangedListeners.get(i) 273 .onKeepClearAreasChanged(displayId, restricted, unrestricted); 274 } 275 } 276 } 277 278 private static class DisplayRecord { 279 private int mDisplayId; 280 private Context mContext; 281 private DisplayLayout mDisplayLayout; 282 private InsetsState mInsetsState = new InsetsState(); 283 DisplayRecord(int displayId)284 private DisplayRecord(int displayId) { 285 mDisplayId = displayId; 286 } 287 setDisplayLayout(Context context, DisplayLayout displayLayout)288 private void setDisplayLayout(Context context, DisplayLayout displayLayout) { 289 mContext = context; 290 mDisplayLayout = displayLayout; 291 mDisplayLayout.setInsets(mContext.getResources(), mInsetsState); 292 } 293 setInsets(InsetsState state)294 private void setInsets(InsetsState state) { 295 mInsetsState = state; 296 mDisplayLayout.setInsets(mContext.getResources(), state); 297 } 298 } 299 300 @BinderThread 301 private class DisplayWindowListenerImpl extends IDisplayWindowListener.Stub { 302 @Override onDisplayAdded(int displayId)303 public void onDisplayAdded(int displayId) { 304 mMainExecutor.execute(() -> { 305 DisplayController.this.onDisplayAdded(displayId); 306 }); 307 } 308 309 @Override onDisplayConfigurationChanged(int displayId, Configuration newConfig)310 public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { 311 mMainExecutor.execute(() -> { 312 DisplayController.this.onDisplayConfigurationChanged(displayId, newConfig); 313 }); 314 } 315 316 @Override onDisplayRemoved(int displayId)317 public void onDisplayRemoved(int displayId) { 318 mMainExecutor.execute(() -> { 319 DisplayController.this.onDisplayRemoved(displayId); 320 }); 321 } 322 323 @Override onFixedRotationStarted(int displayId, int newRotation)324 public void onFixedRotationStarted(int displayId, int newRotation) { 325 mMainExecutor.execute(() -> { 326 DisplayController.this.onFixedRotationStarted(displayId, newRotation); 327 }); 328 } 329 330 @Override onFixedRotationFinished(int displayId)331 public void onFixedRotationFinished(int displayId) { 332 mMainExecutor.execute(() -> { 333 DisplayController.this.onFixedRotationFinished(displayId); 334 }); 335 } 336 337 @Override onKeepClearAreasChanged(int displayId, List<Rect> restricted, List<Rect> unrestricted)338 public void onKeepClearAreasChanged(int displayId, List<Rect> restricted, 339 List<Rect> unrestricted) { 340 mMainExecutor.execute(() -> { 341 DisplayController.this.onKeepClearAreasChanged(displayId, 342 new ArraySet<>(restricted), new ArraySet<>(unrestricted)); 343 }); 344 } 345 } 346 347 /** 348 * Gets notified when a display is added/removed to the WM hierarchy and when a display's 349 * window-configuration changes. 350 * 351 * @see IDisplayWindowListener 352 */ 353 @ShellMainThread 354 public interface OnDisplaysChangedListener { 355 /** 356 * Called when a display has been added to the WM hierarchy. 357 */ onDisplayAdded(int displayId)358 default void onDisplayAdded(int displayId) {} 359 360 /** 361 * Called when a display's window-container configuration changes. 362 */ onDisplayConfigurationChanged(int displayId, Configuration newConfig)363 default void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {} 364 365 /** 366 * Called when a display is removed. 367 */ onDisplayRemoved(int displayId)368 default void onDisplayRemoved(int displayId) {} 369 370 /** 371 * Called when fixed rotation on a display is started. 372 */ onFixedRotationStarted(int displayId, int newRotation)373 default void onFixedRotationStarted(int displayId, int newRotation) {} 374 375 /** 376 * Called when fixed rotation on a display is finished. 377 */ onFixedRotationFinished(int displayId)378 default void onFixedRotationFinished(int displayId) {} 379 380 /** 381 * Called when keep-clear areas on a display have changed. 382 */ onKeepClearAreasChanged(int displayId, Set<Rect> restricted, Set<Rect> unrestricted)383 default void onKeepClearAreasChanged(int displayId, Set<Rect> restricted, 384 Set<Rect> unrestricted) {} 385 } 386 } 387