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