| /* |
| * Copyright (C) 2020 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.server.display; |
| |
| import android.annotation.NonNull; |
| import android.util.Slog; |
| import android.view.Display; |
| import android.view.DisplayAddress; |
| |
| import com.android.internal.annotations.GuardedBy; |
| import com.android.server.display.DisplayManagerService.SyncRoot; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.function.Consumer; |
| |
| /** |
| * Container for all the display devices present in the system. If an object wants to get events |
| * about all the DisplayDevices without needing to listen to all of the DisplayAdapters, they can |
| * listen and interact with the instance of this class. |
| * <p> |
| * The collection of {@link DisplayDevice}s and their usage is protected by the provided |
| * {@link DisplayManagerService.SyncRoot} lock object. |
| */ |
| class DisplayDeviceRepository implements DisplayAdapter.Listener { |
| private static final String TAG = "DisplayDeviceRepository"; |
| |
| public static final int DISPLAY_DEVICE_EVENT_ADDED = 1; |
| public static final int DISPLAY_DEVICE_EVENT_CHANGED = 2; |
| public static final int DISPLAY_DEVICE_EVENT_REMOVED = 3; |
| |
| /** |
| * List of all currently connected display devices. Indexed by the displayId. |
| * TODO: multi-display - break the notion that this is indexed by displayId. |
| */ |
| @GuardedBy("mSyncRoot") |
| private final List<DisplayDevice> mDisplayDevices = new ArrayList<>(); |
| |
| /** Listeners for {link DisplayDevice} events. */ |
| @GuardedBy("mSyncRoot") |
| private final List<Listener> mListeners = new ArrayList<>(); |
| |
| /** Global lock object from {@link DisplayManagerService}. */ |
| private final SyncRoot mSyncRoot; |
| |
| private final PersistentDataStore mPersistentDataStore; |
| |
| /** |
| * @param syncRoot The global lock for DisplayManager related objects. |
| * @param persistentDataStore Settings data store from {@link DisplayManagerService}. |
| */ |
| DisplayDeviceRepository(@NonNull SyncRoot syncRoot, |
| @NonNull PersistentDataStore persistentDataStore) { |
| mSyncRoot = syncRoot; |
| mPersistentDataStore = persistentDataStore; |
| } |
| |
| public void addListener(@NonNull Listener listener) { |
| mListeners.add(listener); |
| } |
| |
| @Override |
| public void onDisplayDeviceEvent(DisplayDevice device, int event) { |
| switch (event) { |
| case DISPLAY_DEVICE_EVENT_ADDED: |
| handleDisplayDeviceAdded(device); |
| break; |
| |
| case DISPLAY_DEVICE_EVENT_CHANGED: |
| handleDisplayDeviceChanged(device); |
| break; |
| |
| case DISPLAY_DEVICE_EVENT_REMOVED: |
| handleDisplayDeviceRemoved(device); |
| break; |
| } |
| } |
| |
| @Override |
| public void onTraversalRequested() { |
| final int size = mListeners.size(); |
| for (int i = 0; i < size; i++) { |
| mListeners.get(i).onTraversalRequested(); |
| } |
| } |
| |
| public boolean containsLocked(DisplayDevice d) { |
| return mDisplayDevices.contains(d); |
| } |
| |
| public int sizeLocked() { |
| return mDisplayDevices.size(); |
| } |
| |
| public void forEachLocked(Consumer<DisplayDevice> consumer) { |
| final int count = mDisplayDevices.size(); |
| for (int i = 0; i < count; i++) { |
| consumer.accept(mDisplayDevices.get(i)); |
| } |
| } |
| |
| public DisplayDevice getByAddressLocked(@NonNull DisplayAddress address) { |
| for (int i = mDisplayDevices.size() - 1; i >= 0; i--) { |
| final DisplayDevice device = mDisplayDevices.get(i); |
| if (address.equals(device.getDisplayDeviceInfoLocked().address)) { |
| return device; |
| } |
| } |
| return null; |
| } |
| |
| // String uniqueId -> DisplayDevice object with that given uniqueId |
| public DisplayDevice getByUniqueIdLocked(@NonNull String uniqueId) { |
| for (int i = mDisplayDevices.size() - 1; i >= 0; i--) { |
| final DisplayDevice displayDevice = mDisplayDevices.get(i); |
| if (displayDevice.getUniqueId().equals(uniqueId)) { |
| return displayDevice; |
| } |
| } |
| return null; |
| } |
| |
| private void handleDisplayDeviceAdded(DisplayDevice device) { |
| synchronized (mSyncRoot) { |
| DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); |
| if (mDisplayDevices.contains(device)) { |
| Slog.w(TAG, "Attempted to add already added display device: " + info); |
| return; |
| } |
| Slog.i(TAG, "Display device added: " + info); |
| device.mDebugLastLoggedDeviceInfo = info; |
| |
| mDisplayDevices.add(device); |
| sendEventLocked(device, DISPLAY_DEVICE_EVENT_ADDED); |
| } |
| } |
| |
| private void handleDisplayDeviceChanged(DisplayDevice device) { |
| synchronized (mSyncRoot) { |
| final DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); |
| if (!mDisplayDevices.contains(device)) { |
| Slog.w(TAG, "Attempted to change non-existent display device: " + info); |
| return; |
| } |
| |
| int diff = device.mDebugLastLoggedDeviceInfo.diff(info); |
| if (diff == DisplayDeviceInfo.DIFF_STATE) { |
| Slog.i(TAG, "Display device changed state: \"" + info.name |
| + "\", " + Display.stateToString(info.state)); |
| } else if (diff != 0) { |
| Slog.i(TAG, "Display device changed: " + info); |
| } |
| |
| if ((diff & DisplayDeviceInfo.DIFF_COLOR_MODE) != 0) { |
| try { |
| mPersistentDataStore.setColorMode(device, info.colorMode); |
| } finally { |
| mPersistentDataStore.saveIfNeeded(); |
| } |
| } |
| device.mDebugLastLoggedDeviceInfo = info; |
| |
| device.applyPendingDisplayDeviceInfoChangesLocked(); |
| sendEventLocked(device, DISPLAY_DEVICE_EVENT_CHANGED); |
| } |
| } |
| |
| private void handleDisplayDeviceRemoved(DisplayDevice device) { |
| synchronized (mSyncRoot) { |
| DisplayDeviceInfo info = device.getDisplayDeviceInfoLocked(); |
| if (!mDisplayDevices.remove(device)) { |
| Slog.w(TAG, "Attempted to remove non-existent display device: " + info); |
| return; |
| } |
| |
| Slog.i(TAG, "Display device removed: " + info); |
| device.mDebugLastLoggedDeviceInfo = info; |
| sendEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED); |
| } |
| } |
| |
| private void sendEventLocked(DisplayDevice device, int event) { |
| final int size = mListeners.size(); |
| for (int i = 0; i < size; i++) { |
| mListeners.get(i).onDisplayDeviceEventLocked(device, event); |
| } |
| } |
| |
| /** |
| * Listens to {@link DisplayDevice} events from {@link DisplayDeviceRepository}. |
| */ |
| public interface Listener { |
| void onDisplayDeviceEventLocked(DisplayDevice device, int event); |
| |
| // TODO: multi-display - Try to remove the need for requestTraversal...it feels like |
| // a shoe-horned method for a shoe-horned feature. |
| void onTraversalRequested(); |
| }; |
| } |