• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.server.display;
18 
19 import android.annotation.NonNull;
20 import android.os.Trace;
21 import android.util.Slog;
22 import android.view.Display;
23 import android.view.DisplayAddress;
24 import android.view.Surface;
25 
26 import com.android.internal.annotations.GuardedBy;
27 import com.android.server.display.DisplayManagerService.SyncRoot;
28 import com.android.server.display.utils.DebugUtils;
29 
30 import java.util.Arrays;
31 import java.util.ArrayList;
32 import java.util.List;
33 import java.util.function.Consumer;
34 
35 /**
36  * Container for all the display devices present in the system.  If an object wants to get events
37  * about all the DisplayDevices without needing to listen to all of the DisplayAdapters, they can
38  * listen and interact with the instance of this class.
39  * <p>
40  * The collection of {@link DisplayDevice}s and their usage is protected by the provided
41  * {@link DisplayManagerService.SyncRoot} lock object.
42  */
43 class DisplayDeviceRepository implements DisplayAdapter.Listener {
44     private static final String TAG = "DisplayDeviceRepository";
45 
46     // To enable these logs, run:
47     // 'adb shell setprop persist.log.tag.DisplayDeviceRepository DEBUG && adb reboot'
48     private static final boolean DEBUG = DebugUtils.isDebuggable(TAG);
49 
50     public static final int DISPLAY_DEVICE_EVENT_ADDED = 1;
51     public static final int DISPLAY_DEVICE_EVENT_REMOVED = 3;
52 
53     /**
54      * List of all currently connected display devices. Indexed by the displayId.
55      * TODO: multi-display - break the notion that this is indexed by displayId.
56      */
57     @GuardedBy("mSyncRoot")
58     private final List<DisplayDevice> mDisplayDevices = new ArrayList<>();
59 
60     /** Listeners for {link DisplayDevice} events. */
61     @GuardedBy("mSyncRoot")
62     private final List<Listener> mListeners = new ArrayList<>();
63 
64     /** Global lock object from {@link DisplayManagerService}. */
65     private final SyncRoot mSyncRoot;
66 
67     private final PersistentDataStore mPersistentDataStore;
68 
69     /**
70      * @param syncRoot The global lock for DisplayManager related objects.
71      * @param persistentDataStore Settings data store from {@link DisplayManagerService}.
72      */
DisplayDeviceRepository(@onNull SyncRoot syncRoot, @NonNull PersistentDataStore persistentDataStore)73     DisplayDeviceRepository(@NonNull SyncRoot syncRoot,
74             @NonNull PersistentDataStore persistentDataStore) {
75         mSyncRoot = syncRoot;
76         mPersistentDataStore = persistentDataStore;
77     }
78 
addListener(@onNull Listener listener)79     public void addListener(@NonNull Listener listener) {
80         mListeners.add(listener);
81     }
82 
83     @Override
onDisplayDeviceEvent(DisplayDevice device, int event)84     public void onDisplayDeviceEvent(DisplayDevice device, int event) {
85         String tag = null;
86         if (DEBUG) {
87             tag = "DisplayDeviceRepository#onDisplayDeviceEvent (event=" + event + ")";
88             Trace.beginAsyncSection(tag, 0);
89         }
90         switch (event) {
91             case DisplayAdapter.DISPLAY_DEVICE_EVENT_ADDED:
92                 handleDisplayDeviceAdded(device);
93                 break;
94 
95             case DisplayAdapter.DISPLAY_DEVICE_EVENT_CHANGED:
96                 handleDisplayDeviceChanged(device);
97                 break;
98 
99             case DisplayAdapter.DISPLAY_DEVICE_EVENT_REMOVED:
100                 handleDisplayDeviceRemoved(device);
101                 break;
102         }
103         if (DEBUG) {
104             Trace.endAsyncSection(tag, 0);
105         }
106     }
107 
108     @Override
onTraversalRequested()109     public void onTraversalRequested() {
110         final int size = mListeners.size();
111         for (int i = 0; i < size; i++) {
112             mListeners.get(i).onTraversalRequested();
113         }
114     }
115 
containsLocked(DisplayDevice d)116     public boolean containsLocked(DisplayDevice d) {
117         return mDisplayDevices.contains(d);
118     }
119 
sizeLocked()120     public int sizeLocked() {
121         return mDisplayDevices.size();
122     }
123 
forEachLocked(Consumer<DisplayDevice> consumer)124     public void forEachLocked(Consumer<DisplayDevice> consumer) {
125         final int count = mDisplayDevices.size();
126         for (int i = 0; i < count; i++) {
127             consumer.accept(mDisplayDevices.get(i));
128         }
129     }
130 
getByAddressLocked(@onNull DisplayAddress address)131     public DisplayDevice getByAddressLocked(@NonNull DisplayAddress address) {
132         for (int i = mDisplayDevices.size() - 1; i >= 0; i--) {
133             final DisplayDevice device = mDisplayDevices.get(i);
134             final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
135             if (address.equals(info.address)
136                     || DisplayAddress.Physical.isPortMatch(address, info.address)) {
137                 return device;
138             }
139         }
140         return null;
141     }
142 
143     // String uniqueId -> DisplayDevice object with that given uniqueId
getByUniqueIdLocked(@onNull String uniqueId)144     public DisplayDevice getByUniqueIdLocked(@NonNull String uniqueId) {
145         for (int i = mDisplayDevices.size() - 1; i >= 0; i--) {
146             final DisplayDevice displayDevice = mDisplayDevices.get(i);
147             if (displayDevice.getUniqueId().equals(uniqueId)) {
148                 return displayDevice;
149             }
150         }
151         return null;
152     }
153 
handleDisplayDeviceAdded(DisplayDevice device)154     private void handleDisplayDeviceAdded(DisplayDevice device) {
155         synchronized (mSyncRoot) {
156             DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
157             if (mDisplayDevices.contains(device)) {
158                 Slog.w(TAG, "Attempted to add already added display device: " + info);
159                 return;
160             }
161             Slog.i(TAG, "Display device added: " + info);
162             device.mDebugLastLoggedDeviceInfo = info;
163 
164             mDisplayDevices.add(device);
165             sendEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
166         }
167     }
168 
handleDisplayDeviceChanged(DisplayDevice device)169     private void handleDisplayDeviceChanged(DisplayDevice device) {
170         synchronized (mSyncRoot) {
171             final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
172             if (!mDisplayDevices.contains(device)) {
173                 Slog.w(TAG, "Attempted to change non-existent display device: " + info);
174                 return;
175             }
176             if (DEBUG) {
177                 Trace.traceBegin(Trace.TRACE_TAG_POWER,
178                         "handleDisplayDeviceChanged");
179             }
180             int diff = device.mDebugLastLoggedDeviceInfo.diff(info);
181             if (diff == 0) {
182                 Slog.i(TAG, "Display device same: " + info);
183             } else if (diff == DisplayDeviceInfo.DIFF_STATE) {
184                 Slog.i(TAG, "Display device changed state: \"" + info.name
185                         + "\", " + Display.stateToString(info.state));
186             } else if (diff == DisplayDeviceInfo.DIFF_ROTATION) {
187                 Slog.i(TAG, "Display device rotated: \"" + info.name
188                         + "\", " + Surface.rotationToString(info.rotation));
189             } else if ((diff &
190                     (DisplayDeviceInfo.DIFF_MODE_ID | DisplayDeviceInfo.DIFF_RENDER_TIMINGS
191                             | DisplayDeviceInfo.DIFF_FRAME_RATE_OVERRIDE)) != 0) {
192                 Slog.i(TAG, "Display device changed render timings: \"" + info.name
193                         + "\", renderFrameRate=" + info.renderFrameRate
194                         + ", presentationDeadlineNanos=" + info.presentationDeadlineNanos
195                         + ", appVsyncOffsetNanos=" + info.appVsyncOffsetNanos
196                         + ", frameRateOverrides=" + Arrays.toString(info.frameRateOverrides));
197             } else if (diff == DisplayDeviceInfo.DIFF_COMMITTED_STATE) {
198                 if (DEBUG) {
199                     Slog.i(TAG, "Display device changed committed state: \"" + info.name
200                             + "\", " + Display.stateToString(info.committedState));
201                 }
202             } else if (diff != DisplayDeviceInfo.DIFF_HDR_SDR_RATIO) {
203                 Slog.i(TAG, "Display device changed: " + info);
204             }
205 
206             if ((diff & DisplayDeviceInfo.DIFF_COLOR_MODE) != 0) {
207                 try {
208                     mPersistentDataStore.setColorMode(device, info.colorMode);
209                 } finally {
210                     mPersistentDataStore.saveIfNeeded();
211                 }
212             }
213             device.mDebugLastLoggedDeviceInfo = info;
214 
215             device.applyPendingDisplayDeviceInfoChangesLocked();
216             sendChangedEventLocked(device, diff);
217             if (DEBUG) {
218                 Trace.traceEnd(Trace.TRACE_TAG_POWER);
219             }
220         }
221     }
222 
handleDisplayDeviceRemoved(DisplayDevice device)223     private void handleDisplayDeviceRemoved(DisplayDevice device) {
224         synchronized (mSyncRoot) {
225             DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
226             if (!mDisplayDevices.remove(device)) {
227                 Slog.w(TAG, "Attempted to remove non-existent display device: " + info);
228                 return;
229             }
230 
231             Slog.i(TAG, "Display device removed: " + info);
232             device.mDebugLastLoggedDeviceInfo = info;
233             sendEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED);
234         }
235     }
236 
sendEventLocked(DisplayDevice device, int event)237     private void sendEventLocked(DisplayDevice device, int event) {
238         final int size = mListeners.size();
239         for (int i = 0; i < size; i++) {
240             mListeners.get(i).onDisplayDeviceEventLocked(device, event);
241         }
242     }
243 
244     @GuardedBy("mSyncRoot")
sendChangedEventLocked(DisplayDevice device, int diff)245     private void sendChangedEventLocked(DisplayDevice device, int diff) {
246         final int size = mListeners.size();
247         for (int i = 0; i < size; i++) {
248             mListeners.get(i).onDisplayDeviceChangedLocked(device, diff);
249         }
250     }
251 
252     /**
253      * Listens to {@link DisplayDevice} events from {@link DisplayDeviceRepository}.
254      */
255     public interface Listener {
onDisplayDeviceEventLocked(DisplayDevice device, int event)256         void onDisplayDeviceEventLocked(DisplayDevice device, int event);
257 
onDisplayDeviceChangedLocked(DisplayDevice device, int diff)258         void onDisplayDeviceChangedLocked(DisplayDevice device, int diff);
259 
260         // TODO: multi-display - Try to remove the need for requestTraversal...it feels like
261         // a shoe-horned method for a shoe-horned feature.
onTraversalRequested()262         void onTraversalRequested();
263     };
264 }
265