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