• 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.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