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