blob: 57f44864d2c09a320afbdc9d55bea85db9ee6674 [file] [log] [blame]
/*
* 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;
}
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();
};
}