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