• 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 
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_CHANGED = 2;
46     public static final int DISPLAY_DEVICE_EVENT_REMOVED = 3;
47 
48     /**
49      * List of all currently connected display devices. Indexed by the displayId.
50      * TODO: multi-display - break the notion that this is indexed by displayId.
51      */
52     @GuardedBy("mSyncRoot")
53     private final List<DisplayDevice> mDisplayDevices = new ArrayList<>();
54 
55     /** Listeners for {link DisplayDevice} events. */
56     @GuardedBy("mSyncRoot")
57     private final List<Listener> mListeners = new ArrayList<>();
58 
59     /** Global lock object from {@link DisplayManagerService}. */
60     private final SyncRoot mSyncRoot;
61 
62     private final PersistentDataStore mPersistentDataStore;
63 
64     /**
65      * @param syncRoot The global lock for DisplayManager related objects.
66      * @param persistentDataStore Settings data store from {@link DisplayManagerService}.
67      */
DisplayDeviceRepository(@onNull SyncRoot syncRoot, @NonNull PersistentDataStore persistentDataStore)68     DisplayDeviceRepository(@NonNull SyncRoot syncRoot,
69             @NonNull PersistentDataStore persistentDataStore) {
70         mSyncRoot = syncRoot;
71         mPersistentDataStore = persistentDataStore;
72     }
73 
addListener(@onNull Listener listener)74     public void addListener(@NonNull Listener listener) {
75         mListeners.add(listener);
76     }
77 
78     @Override
onDisplayDeviceEvent(DisplayDevice device, int event)79     public void onDisplayDeviceEvent(DisplayDevice device, int event) {
80         String tag = null;
81         if (DEBUG) {
82             tag = "DisplayDeviceRepository#onDisplayDeviceEvent (event=" + event + ")";
83             Trace.beginAsyncSection(tag, 0);
84         }
85         switch (event) {
86             case DISPLAY_DEVICE_EVENT_ADDED:
87                 handleDisplayDeviceAdded(device);
88                 break;
89 
90             case DISPLAY_DEVICE_EVENT_CHANGED:
91                 handleDisplayDeviceChanged(device);
92                 break;
93 
94             case DISPLAY_DEVICE_EVENT_REMOVED:
95                 handleDisplayDeviceRemoved(device);
96                 break;
97         }
98         if (DEBUG) {
99             Trace.endAsyncSection(tag, 0);
100         }
101     }
102 
103     @Override
onTraversalRequested()104     public void onTraversalRequested() {
105         final int size = mListeners.size();
106         for (int i = 0; i < size; i++) {
107             mListeners.get(i).onTraversalRequested();
108         }
109     }
110 
containsLocked(DisplayDevice d)111     public boolean containsLocked(DisplayDevice d) {
112         return mDisplayDevices.contains(d);
113     }
114 
sizeLocked()115     public int sizeLocked() {
116         return mDisplayDevices.size();
117     }
118 
forEachLocked(Consumer<DisplayDevice> consumer)119     public void forEachLocked(Consumer<DisplayDevice> consumer) {
120         final int count = mDisplayDevices.size();
121         for (int i = 0; i < count; i++) {
122             consumer.accept(mDisplayDevices.get(i));
123         }
124     }
125 
getByAddressLocked(@onNull DisplayAddress address)126     public DisplayDevice getByAddressLocked(@NonNull DisplayAddress address) {
127         for (int i = mDisplayDevices.size() - 1; i >= 0; i--) {
128             final DisplayDevice device = mDisplayDevices.get(i);
129             if (address.equals(device.getDisplayDeviceInfoLocked().address)) {
130                 return device;
131             }
132         }
133         return null;
134     }
135 
136     // String uniqueId -> DisplayDevice object with that given uniqueId
getByUniqueIdLocked(@onNull String uniqueId)137     public DisplayDevice getByUniqueIdLocked(@NonNull String uniqueId) {
138         for (int i = mDisplayDevices.size() - 1; i >= 0; i--) {
139             final DisplayDevice displayDevice = mDisplayDevices.get(i);
140             if (displayDevice.getUniqueId().equals(uniqueId)) {
141                 return displayDevice;
142             }
143         }
144         return null;
145     }
146 
handleDisplayDeviceAdded(DisplayDevice device)147     private void handleDisplayDeviceAdded(DisplayDevice device) {
148         synchronized (mSyncRoot) {
149             DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
150             if (mDisplayDevices.contains(device)) {
151                 Slog.w(TAG, "Attempted to add already added display device: " + info);
152                 return;
153             }
154             Slog.i(TAG, "Display device added: " + info);
155             device.mDebugLastLoggedDeviceInfo = info;
156 
157             mDisplayDevices.add(device);
158             sendEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED);
159         }
160     }
161 
handleDisplayDeviceChanged(DisplayDevice device)162     private void handleDisplayDeviceChanged(DisplayDevice device) {
163         synchronized (mSyncRoot) {
164             final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked();
165             if (!mDisplayDevices.contains(device)) {
166                 Slog.w(TAG, "Attempted to change non-existent display device: " + info);
167                 return;
168             }
169             if (DEBUG) {
170                 Trace.beginSection("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 != 0) {
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             sendEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED);
191             if (DEBUG) {
192                 Trace.endSection();
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     /**
219      * Listens to {@link DisplayDevice} events from {@link DisplayDeviceRepository}.
220      */
221     public interface Listener {
onDisplayDeviceEventLocked(DisplayDevice device, int event)222         void onDisplayDeviceEventLocked(DisplayDevice device, int event);
223 
224         // TODO: multi-display - Try to remove the need for requestTraversal...it feels like
225         // a shoe-horned method for a shoe-horned feature.
onTraversalRequested()226         void onTraversalRequested();
227     };
228 }
229