• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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