• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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