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