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.systemui.dagger.qualifiers.Background 27 import com.android.systemui.util.Assert 28 import java.lang.ref.WeakReference 29 import java.util.concurrent.Executor 30 31 class DisplayTrackerImpl 32 internal constructor( 33 val displayManager: DisplayManager, 34 @Background val backgroundHandler: Handler 35 ) : DisplayTracker { 36 override val defaultDisplayId: Int = Display.DEFAULT_DISPLAY 37 override val allDisplays: Array<Display> 38 get() = displayManager.displays 39 40 @GuardedBy("displayCallbacks") 41 private val displayCallbacks: MutableList<DisplayTrackerDataItem> = ArrayList() 42 @GuardedBy("brightnessCallbacks") 43 private val brightnessCallbacks: MutableList<DisplayTrackerDataItem> = ArrayList() 44 45 @VisibleForTesting 46 val displayChangedListener: DisplayManager.DisplayListener = 47 object : DisplayManager.DisplayListener { 48 override fun onDisplayAdded(displayId: Int) { 49 val list = synchronized(displayCallbacks) { displayCallbacks.toList() } 50 onDisplayAdded(displayId, list) 51 } 52 53 override fun onDisplayRemoved(displayId: Int) { 54 val list = synchronized(displayCallbacks) { displayCallbacks.toList() } 55 onDisplayRemoved(displayId, list) 56 } 57 58 override fun onDisplayChanged(displayId: Int) { 59 val list = synchronized(displayCallbacks) { displayCallbacks.toList() } 60 onDisplayChanged(displayId, list) 61 } 62 } 63 64 @VisibleForTesting 65 val displayBrightnessChangedListener: DisplayManager.DisplayListener = 66 object : DisplayManager.DisplayListener { 67 override fun onDisplayAdded(displayId: Int) {} 68 69 override fun onDisplayRemoved(displayId: Int) {} 70 71 override fun onDisplayChanged(displayId: Int) { 72 val list = synchronized(brightnessCallbacks) { brightnessCallbacks.toList() } 73 onDisplayChanged(displayId, list) 74 } 75 } 76 77 override fun addDisplayChangeCallback(callback: DisplayTracker.Callback, executor: Executor) { 78 synchronized(displayCallbacks) { 79 if (displayCallbacks.isEmpty()) { 80 displayManager.registerDisplayListener(displayChangedListener, backgroundHandler) 81 } 82 displayCallbacks.add(DisplayTrackerDataItem(WeakReference(callback), executor)) 83 } 84 } 85 86 override fun addBrightnessChangeCallback( 87 callback: DisplayTracker.Callback, 88 executor: Executor 89 ) { 90 synchronized(brightnessCallbacks) { 91 if (brightnessCallbacks.isEmpty()) { 92 displayManager.registerDisplayListener( 93 displayBrightnessChangedListener, 94 backgroundHandler, 95 EVENT_FLAG_DISPLAY_BRIGHTNESS 96 ) 97 } 98 brightnessCallbacks.add(DisplayTrackerDataItem(WeakReference(callback), executor)) 99 } 100 } 101 102 override fun removeCallback(callback: DisplayTracker.Callback) { 103 synchronized(displayCallbacks) { 104 val changed = displayCallbacks.removeIf { it.sameOrEmpty(callback) } 105 if (changed && displayCallbacks.isEmpty()) { 106 displayManager.unregisterDisplayListener(displayChangedListener) 107 } 108 } 109 110 synchronized(brightnessCallbacks) { 111 val changed = brightnessCallbacks.removeIf { it.sameOrEmpty(callback) } 112 if (changed && brightnessCallbacks.isEmpty()) { 113 displayManager.unregisterDisplayListener(displayBrightnessChangedListener) 114 } 115 } 116 } 117 118 override fun getDisplay(displayId: Int): Display { 119 return displayManager.getDisplay(displayId) 120 } 121 122 @WorkerThread 123 private fun onDisplayAdded(displayId: Int, list: List<DisplayTrackerDataItem>) { 124 Assert.isNotMainThread() 125 126 notifySubscribers({ onDisplayAdded(displayId) }, list) 127 } 128 129 @WorkerThread 130 private fun onDisplayRemoved(displayId: Int, list: List<DisplayTrackerDataItem>) { 131 Assert.isNotMainThread() 132 133 notifySubscribers({ onDisplayRemoved(displayId) }, list) 134 } 135 136 @WorkerThread 137 private fun onDisplayChanged(displayId: Int, list: List<DisplayTrackerDataItem>) { 138 Assert.isNotMainThread() 139 140 notifySubscribers({ onDisplayChanged(displayId) }, list) 141 } 142 143 private inline fun notifySubscribers( 144 crossinline action: DisplayTracker.Callback.() -> Unit, 145 list: List<DisplayTrackerDataItem> 146 ) { 147 list.forEach { 148 if (it.callback.get() != null) { 149 it.executor.execute { it.callback.get()?.action() } 150 } 151 } 152 } 153 154 private data class DisplayTrackerDataItem( 155 val callback: WeakReference<DisplayTracker.Callback>, 156 val executor: Executor 157 ) { 158 fun sameOrEmpty(other: DisplayTracker.Callback): Boolean { 159 return callback.get()?.equals(other) ?: true 160 } 161 } 162 } 163