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.PRIVATE_EVENT_TYPE_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("DisplayTrackerImpl.displayChangedDisplayListener#onDisplayAdded") { 51 val list = synchronized(displayCallbacks) { displayCallbacks.toList() } 52 onDisplayAdded(displayId, list) 53 } 54 } 55 onDisplayRemovednull56 override fun onDisplayRemoved(displayId: Int) { 57 traceSection("DisplayTrackerImpl.displayChangedDisplayListener#onDisplayRemoved") { 58 val list = synchronized(displayCallbacks) { displayCallbacks.toList() } 59 onDisplayRemoved(displayId, list) 60 } 61 } 62 onDisplayChangednull63 override fun onDisplayChanged(displayId: Int) { 64 traceSection("DisplayTrackerImpl.displayChangedDisplayListener#onDisplayChanged") { 65 val list = synchronized(displayCallbacks) { displayCallbacks.toList() } 66 onDisplayChanged(displayId, list) 67 } 68 } 69 } 70 71 @VisibleForTesting 72 val displayBrightnessChangedListener: DisplayManager.DisplayListener = 73 object : DisplayManager.DisplayListener { onDisplayAddednull74 override fun onDisplayAdded(displayId: Int) {} 75 onDisplayRemovednull76 override fun onDisplayRemoved(displayId: Int) {} 77 onDisplayChangednull78 override fun onDisplayChanged(displayId: Int) { 79 traceSection( 80 "DisplayTrackerImpl.displayBrightnessChangedDisplayListener#onDisplayChanged" 81 ) { 82 val list = synchronized(brightnessCallbacks) { brightnessCallbacks.toList() } 83 onDisplayChanged(displayId, list) 84 } 85 } 86 } 87 addDisplayChangeCallbacknull88 override fun addDisplayChangeCallback(callback: DisplayTracker.Callback, executor: Executor) { 89 synchronized(displayCallbacks) { 90 if (displayCallbacks.isEmpty()) { 91 displayManager.registerDisplayListener(displayChangedListener, backgroundHandler) 92 } 93 displayCallbacks.add(DisplayTrackerDataItem(WeakReference(callback), executor)) 94 } 95 } 96 addBrightnessChangeCallbacknull97 override fun addBrightnessChangeCallback( 98 callback: DisplayTracker.Callback, 99 executor: Executor, 100 ) { 101 synchronized(brightnessCallbacks) { 102 if (brightnessCallbacks.isEmpty()) { 103 displayManager.registerDisplayListener( 104 displayBrightnessChangedListener, 105 backgroundHandler, 106 /* eventFlags */ 0, 107 PRIVATE_EVENT_TYPE_DISPLAY_BRIGHTNESS, 108 ) 109 } 110 brightnessCallbacks.add(DisplayTrackerDataItem(WeakReference(callback), executor)) 111 } 112 } 113 removeCallbacknull114 override fun removeCallback(callback: DisplayTracker.Callback) { 115 synchronized(displayCallbacks) { 116 val changed = displayCallbacks.removeIf { it.sameOrEmpty(callback) } 117 if (changed && displayCallbacks.isEmpty()) { 118 displayManager.unregisterDisplayListener(displayChangedListener) 119 } 120 } 121 122 synchronized(brightnessCallbacks) { 123 val changed = brightnessCallbacks.removeIf { it.sameOrEmpty(callback) } 124 if (changed && brightnessCallbacks.isEmpty()) { 125 displayManager.unregisterDisplayListener(displayBrightnessChangedListener) 126 } 127 } 128 } 129 getDisplaynull130 override fun getDisplay(displayId: Int): Display { 131 return displayManager.getDisplay(displayId) 132 } 133 134 @WorkerThread onDisplayAddednull135 private fun onDisplayAdded(displayId: Int, list: List<DisplayTrackerDataItem>) { 136 Assert.isNotMainThread() 137 138 notifySubscribers({ onDisplayAdded(displayId) }, list) 139 } 140 141 @WorkerThread onDisplayRemovednull142 private fun onDisplayRemoved(displayId: Int, list: List<DisplayTrackerDataItem>) { 143 Assert.isNotMainThread() 144 145 notifySubscribers({ onDisplayRemoved(displayId) }, list) 146 } 147 148 @WorkerThread onDisplayChangednull149 private fun onDisplayChanged(displayId: Int, list: List<DisplayTrackerDataItem>) { 150 Assert.isNotMainThread() 151 152 notifySubscribers({ onDisplayChanged(displayId) }, list) 153 } 154 notifySubscribersnull155 private inline fun notifySubscribers( 156 crossinline action: DisplayTracker.Callback.() -> Unit, 157 list: List<DisplayTrackerDataItem>, 158 ) { 159 list.forEach { 160 if (it.callback.get() != null) { 161 it.executor.execute { it.callback.get()?.action() } 162 } 163 } 164 } 165 166 private data class DisplayTrackerDataItem( 167 val callback: WeakReference<DisplayTracker.Callback>, 168 val executor: Executor, 169 ) { sameOrEmptynull170 fun sameOrEmpty(other: DisplayTracker.Callback): Boolean { 171 return callback.get()?.equals(other) ?: true 172 } 173 } 174 } 175