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 18 package com.android.systemui.power.data.repository 19 20 import android.content.BroadcastReceiver 21 import android.content.Context 22 import android.content.Intent 23 import android.content.IntentFilter 24 import android.os.PowerManager 25 import com.android.systemui.broadcast.BroadcastDispatcher 26 import com.android.systemui.common.coroutine.ChannelExt.trySendWithFailureLogging 27 import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow 28 import com.android.systemui.dagger.SysUISingleton 29 import com.android.systemui.dagger.qualifiers.Application 30 import com.android.systemui.power.shared.model.ScreenPowerState 31 import com.android.systemui.power.shared.model.WakeSleepReason 32 import com.android.systemui.power.shared.model.WakefulnessModel 33 import com.android.systemui.power.shared.model.WakefulnessState 34 import com.android.systemui.util.time.SystemClock 35 import javax.inject.Inject 36 import kotlinx.coroutines.channels.awaitClose 37 import kotlinx.coroutines.flow.Flow 38 import kotlinx.coroutines.flow.MutableStateFlow 39 import kotlinx.coroutines.flow.StateFlow 40 import kotlinx.coroutines.flow.asStateFlow 41 42 /** Defines interface for classes that act as source of truth for power-related data. */ 43 interface PowerRepository { 44 /** Whether the device is interactive. Starts with the current state. */ 45 val isInteractive: Flow<Boolean> 46 47 /** 48 * Whether the device is awake or asleep. [WakefulnessState.AWAKE] means the screen is fully 49 * powered on, and the user can interact with the device. [WakefulnessState.ASLEEP] means the 50 * screen is either off, or in low-power always-on-display mode - in either case, the user 51 * cannot interact with the device and will need to wake it up somehow if they wish to do so. 52 */ 53 val wakefulness: StateFlow<WakefulnessModel> 54 55 /** 56 * The physical on/off state of the display. [ScreenPowerState.SCREEN_OFF] means the display is 57 * unpowered and nothing is visible. [ScreenPowerState.SCREEN_ON] means the display is either 58 * fully powered on, or it's in low-power always-on-display (AOD) mode showing the time and 59 * other info. 60 * 61 * YOU PROBABLY DO NOT WANT TO USE THIS STATE. Almost all System UI use cases for screen state 62 * expect that the screen would be considered "off" if we're on AOD, which is not the case for 63 * [screenPowerState]. Consider [wakefulness] instead. 64 */ 65 val screenPowerState: StateFlow<ScreenPowerState> 66 67 /** Wakes up the device. */ wakeUpnull68 fun wakeUp(why: String, @PowerManager.WakeReason wakeReason: Int) 69 70 /** 71 * Notifies the power repository that a user touch happened. 72 * 73 * @param noChangeLights If true, does not cause the keyboard backlight to turn on because of 74 * this event. This is set when the power key is pressed. We want the device to stay on while 75 * the button is down, but we're about to turn off the screen so we don't want the keyboard 76 * backlight to turn on again. Otherwise the lights flash on and then off and it looks weird. 77 */ 78 fun userTouch(noChangeLights: Boolean = false) 79 80 /** Updates the wakefulness state, keeping previous values by default. */ 81 fun updateWakefulness( 82 rawState: WakefulnessState = wakefulness.value.internalWakefulnessState, 83 lastWakeReason: WakeSleepReason = wakefulness.value.lastWakeReason, 84 lastSleepReason: WakeSleepReason = wakefulness.value.lastSleepReason, 85 powerButtonLaunchGestureTriggered: Boolean = 86 wakefulness.value.powerButtonLaunchGestureTriggered, 87 ) 88 89 /** Updates the screen power state. */ 90 fun setScreenPowerState(state: ScreenPowerState) 91 } 92 93 @SysUISingleton 94 class PowerRepositoryImpl 95 @Inject 96 constructor( 97 private val manager: PowerManager, 98 @Application private val applicationContext: Context, 99 private val systemClock: SystemClock, 100 dispatcher: BroadcastDispatcher, 101 ) : PowerRepository { 102 103 override val isInteractive: Flow<Boolean> = conflatedCallbackFlow { 104 fun send() { 105 trySendWithFailureLogging(manager.isInteractive, TAG) 106 } 107 108 val receiver = 109 object : BroadcastReceiver() { 110 override fun onReceive(context: Context?, intent: Intent?) { 111 send() 112 } 113 } 114 115 dispatcher.registerReceiver( 116 receiver, 117 IntentFilter().apply { 118 addAction(Intent.ACTION_SCREEN_ON) 119 addAction(Intent.ACTION_SCREEN_OFF) 120 }, 121 ) 122 send() 123 124 awaitClose { dispatcher.unregisterReceiver(receiver) } 125 } 126 127 private val _wakefulness = MutableStateFlow(WakefulnessModel()) 128 override val wakefulness = _wakefulness.asStateFlow() 129 130 override fun updateWakefulness( 131 rawState: WakefulnessState, 132 lastWakeReason: WakeSleepReason, 133 lastSleepReason: WakeSleepReason, 134 powerButtonLaunchGestureTriggered: Boolean, 135 ) { 136 _wakefulness.value = 137 WakefulnessModel( 138 rawState, 139 lastWakeReason, 140 lastSleepReason, 141 powerButtonLaunchGestureTriggered, 142 ) 143 } 144 145 private val _screenPowerState = MutableStateFlow(ScreenPowerState.SCREEN_OFF) 146 override val screenPowerState = _screenPowerState.asStateFlow() 147 148 override fun setScreenPowerState(state: ScreenPowerState) { 149 _screenPowerState.value = state 150 } 151 152 override fun wakeUp(why: String, wakeReason: Int) { 153 manager.wakeUp( 154 systemClock.uptimeMillis(), 155 wakeReason, 156 "${applicationContext.packageName}:$why", 157 ) 158 } 159 160 override fun userTouch(noChangeLights: Boolean) { 161 manager.userActivity( 162 systemClock.uptimeMillis(), 163 PowerManager.USER_ACTIVITY_EVENT_TOUCH, 164 if (noChangeLights) PowerManager.USER_ACTIVITY_FLAG_NO_CHANGE_LIGHTS else 0, 165 ) 166 } 167 168 companion object { 169 private const val TAG = "PowerRepository" 170 } 171 } 172