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.hardware.display.DisplayManager; 23 import android.os.RemoteException; 24 import android.util.Slog; 25 import android.util.SparseArray; 26 import android.view.Display; 27 import android.view.IDisplayWindowListener; 28 import android.view.IWindowManager; 29 30 import androidx.annotation.BinderThread; 31 32 import com.android.wm.shell.common.DisplayChangeController.OnDisplayChangingListener; 33 import com.android.wm.shell.common.annotations.ShellMainThread; 34 35 import java.util.ArrayList; 36 37 /** 38 * This module deals with display rotations coming from WM. When WM starts a rotation: after it has 39 * frozen the screen, it will call into this class. This will then call all registered local 40 * controllers and give them a chance to queue up task changes to be applied synchronously with that 41 * rotation. 42 */ 43 public class DisplayController { 44 private static final String TAG = "DisplayController"; 45 46 private final ShellExecutor mMainExecutor; 47 private final Context mContext; 48 private final IWindowManager mWmService; 49 private final DisplayChangeController mChangeController; 50 private final IDisplayWindowListener mDisplayContainerListener; 51 52 private final SparseArray<DisplayRecord> mDisplays = new SparseArray<>(); 53 private final ArrayList<OnDisplaysChangedListener> mDisplayChangedListeners = new ArrayList<>(); 54 55 /** 56 * Gets a display by id from DisplayManager. 57 */ getDisplay(int displayId)58 public Display getDisplay(int displayId) { 59 final DisplayManager displayManager = mContext.getSystemService(DisplayManager.class); 60 return displayManager.getDisplay(displayId); 61 } 62 DisplayController(Context context, IWindowManager wmService, ShellExecutor mainExecutor)63 public DisplayController(Context context, IWindowManager wmService, 64 ShellExecutor mainExecutor) { 65 mMainExecutor = mainExecutor; 66 mContext = context; 67 mWmService = wmService; 68 mChangeController = new DisplayChangeController(mWmService, mainExecutor); 69 mDisplayContainerListener = new DisplayWindowListenerImpl(); 70 try { 71 mWmService.registerDisplayWindowListener(mDisplayContainerListener); 72 } catch (RemoteException e) { 73 throw new RuntimeException("Unable to register hierarchy listener"); 74 } 75 } 76 77 /** 78 * Gets the DisplayLayout associated with a display. 79 */ getDisplayLayout(int displayId)80 public @Nullable DisplayLayout getDisplayLayout(int displayId) { 81 final DisplayRecord r = mDisplays.get(displayId); 82 return r != null ? r.mDisplayLayout : null; 83 } 84 85 /** 86 * Gets a display-specific context for a display. 87 */ getDisplayContext(int displayId)88 public @Nullable Context getDisplayContext(int displayId) { 89 final DisplayRecord r = mDisplays.get(displayId); 90 return r != null ? r.mContext : null; 91 } 92 93 /** 94 * Add a display window-container listener. It will get notified whenever a display's 95 * configuration changes or when displays are added/removed from the WM hierarchy. 96 */ addDisplayWindowListener(OnDisplaysChangedListener listener)97 public void addDisplayWindowListener(OnDisplaysChangedListener listener) { 98 synchronized (mDisplays) { 99 if (mDisplayChangedListeners.contains(listener)) { 100 return; 101 } 102 mDisplayChangedListeners.add(listener); 103 for (int i = 0; i < mDisplays.size(); ++i) { 104 listener.onDisplayAdded(mDisplays.keyAt(i)); 105 } 106 } 107 } 108 109 /** 110 * Remove a display window-container listener. 111 */ removeDisplayWindowListener(OnDisplaysChangedListener listener)112 public void removeDisplayWindowListener(OnDisplaysChangedListener listener) { 113 synchronized (mDisplays) { 114 mDisplayChangedListeners.remove(listener); 115 } 116 } 117 118 /** 119 * Adds a display rotation controller. 120 */ addDisplayChangingController(OnDisplayChangingListener controller)121 public void addDisplayChangingController(OnDisplayChangingListener controller) { 122 mChangeController.addRotationListener(controller); 123 } 124 125 /** 126 * Removes a display rotation controller. 127 */ removeDisplayChangingController(OnDisplayChangingListener controller)128 public void removeDisplayChangingController(OnDisplayChangingListener controller) { 129 mChangeController.removeRotationListener(controller); 130 } 131 onDisplayAdded(int displayId)132 private void onDisplayAdded(int displayId) { 133 synchronized (mDisplays) { 134 if (mDisplays.get(displayId) != null) { 135 return; 136 } 137 Display display = getDisplay(displayId); 138 if (display == null) { 139 // It's likely that the display is private to some app and thus not 140 // accessible by system-ui. 141 return; 142 } 143 DisplayRecord record = new DisplayRecord(); 144 record.mDisplayId = displayId; 145 record.mContext = (displayId == Display.DEFAULT_DISPLAY) ? mContext 146 : mContext.createDisplayContext(display); 147 record.mDisplayLayout = new DisplayLayout(record.mContext, display); 148 mDisplays.put(displayId, record); 149 for (int i = 0; i < mDisplayChangedListeners.size(); ++i) { 150 mDisplayChangedListeners.get(i).onDisplayAdded(displayId); 151 } 152 } 153 } 154 onDisplayConfigurationChanged(int displayId, Configuration newConfig)155 private void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { 156 synchronized (mDisplays) { 157 DisplayRecord dr = mDisplays.get(displayId); 158 if (dr == null) { 159 Slog.w(TAG, "Skipping Display Configuration change on non-added" 160 + " display."); 161 return; 162 } 163 Display display = getDisplay(displayId); 164 if (display == null) { 165 Slog.w(TAG, "Skipping Display Configuration change on invalid" 166 + " display. It may have been removed."); 167 return; 168 } 169 Context perDisplayContext = mContext; 170 if (displayId != Display.DEFAULT_DISPLAY) { 171 perDisplayContext = mContext.createDisplayContext(display); 172 } 173 dr.mContext = perDisplayContext.createConfigurationContext(newConfig); 174 dr.mDisplayLayout = new DisplayLayout(dr.mContext, display); 175 for (int i = 0; i < mDisplayChangedListeners.size(); ++i) { 176 mDisplayChangedListeners.get(i).onDisplayConfigurationChanged( 177 displayId, newConfig); 178 } 179 } 180 } 181 onDisplayRemoved(int displayId)182 private void onDisplayRemoved(int displayId) { 183 synchronized (mDisplays) { 184 if (mDisplays.get(displayId) == null) { 185 return; 186 } 187 for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { 188 mDisplayChangedListeners.get(i).onDisplayRemoved(displayId); 189 } 190 mDisplays.remove(displayId); 191 } 192 } 193 onFixedRotationStarted(int displayId, int newRotation)194 private void onFixedRotationStarted(int displayId, int newRotation) { 195 synchronized (mDisplays) { 196 if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) { 197 Slog.w(TAG, "Skipping onFixedRotationStarted on unknown" 198 + " display, displayId=" + displayId); 199 return; 200 } 201 for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { 202 mDisplayChangedListeners.get(i).onFixedRotationStarted( 203 displayId, newRotation); 204 } 205 } 206 } 207 onFixedRotationFinished(int displayId)208 private void onFixedRotationFinished(int displayId) { 209 synchronized (mDisplays) { 210 if (mDisplays.get(displayId) == null || getDisplay(displayId) == null) { 211 Slog.w(TAG, "Skipping onFixedRotationFinished on unknown" 212 + " display, displayId=" + displayId); 213 return; 214 } 215 for (int i = mDisplayChangedListeners.size() - 1; i >= 0; --i) { 216 mDisplayChangedListeners.get(i).onFixedRotationFinished(displayId); 217 } 218 } 219 } 220 221 private static class DisplayRecord { 222 int mDisplayId; 223 Context mContext; 224 DisplayLayout mDisplayLayout; 225 } 226 227 @BinderThread 228 private class DisplayWindowListenerImpl extends IDisplayWindowListener.Stub { 229 @Override onDisplayAdded(int displayId)230 public void onDisplayAdded(int displayId) { 231 mMainExecutor.execute(() -> { 232 DisplayController.this.onDisplayAdded(displayId); 233 }); 234 } 235 236 @Override onDisplayConfigurationChanged(int displayId, Configuration newConfig)237 public void onDisplayConfigurationChanged(int displayId, Configuration newConfig) { 238 mMainExecutor.execute(() -> { 239 DisplayController.this.onDisplayConfigurationChanged(displayId, newConfig); 240 }); 241 } 242 243 @Override onDisplayRemoved(int displayId)244 public void onDisplayRemoved(int displayId) { 245 mMainExecutor.execute(() -> { 246 DisplayController.this.onDisplayRemoved(displayId); 247 }); 248 } 249 250 @Override onFixedRotationStarted(int displayId, int newRotation)251 public void onFixedRotationStarted(int displayId, int newRotation) { 252 mMainExecutor.execute(() -> { 253 DisplayController.this.onFixedRotationStarted(displayId, newRotation); 254 }); 255 } 256 257 @Override onFixedRotationFinished(int displayId)258 public void onFixedRotationFinished(int displayId) { 259 mMainExecutor.execute(() -> { 260 DisplayController.this.onFixedRotationFinished(displayId); 261 }); 262 } 263 } 264 265 /** 266 * Gets notified when a display is added/removed to the WM hierarchy and when a display's 267 * window-configuration changes. 268 * 269 * @see IDisplayWindowListener 270 */ 271 @ShellMainThread 272 public interface OnDisplaysChangedListener { 273 /** 274 * Called when a display has been added to the WM hierarchy. 275 */ onDisplayAdded(int displayId)276 default void onDisplayAdded(int displayId) {} 277 278 /** 279 * Called when a display's window-container configuration changes. 280 */ onDisplayConfigurationChanged(int displayId, Configuration newConfig)281 default void onDisplayConfigurationChanged(int displayId, Configuration newConfig) {} 282 283 /** 284 * Called when a display is removed. 285 */ onDisplayRemoved(int displayId)286 default void onDisplayRemoved(int displayId) {} 287 288 /** 289 * Called when fixed rotation on a display is started. 290 */ onFixedRotationStarted(int displayId, int newRotation)291 default void onFixedRotationStarted(int displayId, int newRotation) {} 292 293 /** 294 * Called when fixed rotation on a display is finished. 295 */ onFixedRotationFinished(int displayId)296 default void onFixedRotationFinished(int displayId) {} 297 } 298 } 299