1 /* 2 * Copyright (C) 2022 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.systemui.settings 18 19 import android.hardware.display.DisplayManager 20 import android.hardware.display.DisplayManager.EVENT_FLAG_DISPLAY_BRIGHTNESS 21 import android.os.Handler 22 import android.view.Display 23 import androidx.annotation.GuardedBy 24 import androidx.annotation.VisibleForTesting 25 import androidx.annotation.WorkerThread 26 import com.android.app.tracing.traceSection 27 import com.android.systemui.dagger.qualifiers.Background 28 import com.android.systemui.util.Assert 29 import java.lang.ref.WeakReference 30 import java.util.concurrent.Executor 31 32 class DisplayTrackerImpl 33 internal constructor( 34 val displayManager: DisplayManager, 35 @Background val backgroundHandler: Handler 36 ) : DisplayTracker { 37 override val defaultDisplayId: Int = Display.DEFAULT_DISPLAY 38 override val allDisplays: Array<Display> 39 get() = displayManager.displays 40 41 @GuardedBy("displayCallbacks") 42 private val displayCallbacks: MutableList<DisplayTrackerDataItem> = ArrayList() 43 @GuardedBy("brightnessCallbacks") 44 private val brightnessCallbacks: MutableList<DisplayTrackerDataItem> = ArrayList() 45 46 @VisibleForTesting 47 val displayChangedListener: DisplayManager.DisplayListener = 48 object : DisplayManager.DisplayListener { onDisplayAddednull49 override fun onDisplayAdded(displayId: Int) { 50 traceSection( 51 "DisplayTrackerImpl.displayChangedDisplayListener#onDisplayAdded", 52 ) { 53 val list = synchronized(displayCallbacks) { displayCallbacks.toList() } 54 onDisplayAdded(displayId, list) 55 } 56 } 57 onDisplayRemovednull58 override fun onDisplayRemoved(displayId: Int) { 59 traceSection( 60 "DisplayTrackerImpl.displayChangedDisplayListener#onDisplayRemoved", 61 ) { 62 val list = synchronized(displayCallbacks) { displayCallbacks.toList() } 63 onDisplayRemoved(displayId, list) 64 } 65 } 66 onDisplayChangednull67 override fun onDisplayChanged(displayId: Int) { 68 traceSection( 69 "DisplayTrackerImpl.displayChangedDisplayListener#onDisplayChanged", 70 ) { 71 val list = synchronized(displayCallbacks) { displayCallbacks.toList() } 72 onDisplayChanged(displayId, list) 73 } 74 } 75 } 76 77 @VisibleForTesting 78 val displayBrightnessChangedListener: DisplayManager.DisplayListener = 79 object : DisplayManager.DisplayListener { onDisplayAddednull80 override fun onDisplayAdded(displayId: Int) {} 81 onDisplayRemovednull82 override fun onDisplayRemoved(displayId: Int) {} 83 onDisplayChangednull84 override fun onDisplayChanged(displayId: Int) { 85 traceSection( 86 "DisplayTrackerImpl.displayBrightnessChangedDisplayListener#onDisplayChanged", 87 ) { 88 val list = synchronized(brightnessCallbacks) { brightnessCallbacks.toList() } 89 onDisplayChanged(displayId, list) 90 } 91 } 92 } 93 addDisplayChangeCallbacknull94 override fun addDisplayChangeCallback(callback: DisplayTracker.Callback, executor: Executor) { 95 synchronized(displayCallbacks) { 96 if (displayCallbacks.isEmpty()) { 97 displayManager.registerDisplayListener(displayChangedListener, backgroundHandler) 98 } 99 displayCallbacks.add(DisplayTrackerDataItem(WeakReference(callback), executor)) 100 } 101 } 102 addBrightnessChangeCallbacknull103 override fun addBrightnessChangeCallback( 104 callback: DisplayTracker.Callback, 105 executor: Executor 106 ) { 107 synchronized(brightnessCallbacks) { 108 if (brightnessCallbacks.isEmpty()) { 109 displayManager.registerDisplayListener( 110 displayBrightnessChangedListener, 111 backgroundHandler, 112 EVENT_FLAG_DISPLAY_BRIGHTNESS 113 ) 114 } 115 brightnessCallbacks.add(DisplayTrackerDataItem(WeakReference(callback), executor)) 116 } 117 } 118 removeCallbacknull119 override fun removeCallback(callback: DisplayTracker.Callback) { 120 synchronized(displayCallbacks) { 121 val changed = displayCallbacks.removeIf { it.sameOrEmpty(callback) } 122 if (changed && displayCallbacks.isEmpty()) { 123 displayManager.unregisterDisplayListener(displayChangedListener) 124 } 125 } 126 127 synchronized(brightnessCallbacks) { 128 val changed = brightnessCallbacks.removeIf { it.sameOrEmpty(callback) } 129 if (changed && brightnessCallbacks.isEmpty()) { 130 displayManager.unregisterDisplayListener(displayBrightnessChangedListener) 131 } 132 } 133 } 134 getDisplaynull135 override fun getDisplay(displayId: Int): Display { 136 return displayManager.getDisplay(displayId) 137 } 138 139 @WorkerThread onDisplayAddednull140 private fun onDisplayAdded(displayId: Int, list: List<DisplayTrackerDataItem>) { 141 Assert.isNotMainThread() 142 143 notifySubscribers({ onDisplayAdded(displayId) }, list) 144 } 145 146 @WorkerThread onDisplayRemovednull147 private fun onDisplayRemoved(displayId: Int, list: List<DisplayTrackerDataItem>) { 148 Assert.isNotMainThread() 149 150 notifySubscribers({ onDisplayRemoved(displayId) }, list) 151 } 152 153 @WorkerThread onDisplayChangednull154 private fun onDisplayChanged(displayId: Int, list: List<DisplayTrackerDataItem>) { 155 Assert.isNotMainThread() 156 157 notifySubscribers({ onDisplayChanged(displayId) }, list) 158 } 159 notifySubscribersnull160 private inline fun notifySubscribers( 161 crossinline action: DisplayTracker.Callback.() -> Unit, 162 list: List<DisplayTrackerDataItem> 163 ) { 164 list.forEach { 165 if (it.callback.get() != null) { 166 it.executor.execute { it.callback.get()?.action() } 167 } 168 } 169 } 170 171 private data class DisplayTrackerDataItem( 172 val callback: WeakReference<DisplayTracker.Callback>, 173 val executor: Executor 174 ) { sameOrEmptynull175 fun sameOrEmpty(other: DisplayTracker.Callback): Boolean { 176 return callback.get()?.equals(other) ?: true 177 } 178 } 179 } 180