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