1 /* <lambda>null2 * Copyright (C) 2024 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.keyguard.domain.interactor 18 19 import android.annotation.SuppressLint 20 import android.app.StatusBarManager 21 import android.content.Context 22 import android.os.Binder 23 import android.os.IBinder 24 import android.os.RemoteException 25 import android.os.UserManager 26 import android.provider.DeviceConfig 27 import android.util.Log 28 import com.android.app.tracing.coroutines.launchTraced as launch 29 import com.android.internal.config.sysui.SystemUiDeviceConfigFlags 30 import com.android.internal.statusbar.IStatusBarService 31 import com.android.systemui.CoreStartable 32 import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor 33 import com.android.systemui.authentication.shared.model.AuthenticationMethodModel 34 import com.android.systemui.dagger.SysUISingleton 35 import com.android.systemui.dagger.qualifiers.Application 36 import com.android.systemui.dagger.qualifiers.Background 37 import com.android.systemui.deviceconfig.domain.interactor.DeviceConfigInteractor 38 import com.android.systemui.deviceentry.domain.interactor.DeviceEntryFaceAuthInteractor 39 import com.android.systemui.keyguard.KeyguardWmStateRefactor 40 import com.android.systemui.keyguard.shared.model.KeyguardState 41 import com.android.systemui.navigation.domain.interactor.NavigationInteractor 42 import com.android.systemui.power.domain.interactor.PowerInteractor 43 import com.android.systemui.power.shared.model.WakeSleepReason 44 import com.android.systemui.power.shared.model.WakefulnessModel 45 import com.android.systemui.process.ProcessWrapper 46 import com.android.systemui.scene.shared.flag.SceneContainerFlag 47 import com.android.systemui.shade.ShadeDisplayAware 48 import com.android.systemui.user.domain.interactor.SelectedUserInteractor 49 import javax.inject.Inject 50 import kotlinx.coroutines.CoroutineDispatcher 51 import kotlinx.coroutines.CoroutineScope 52 import kotlinx.coroutines.flow.combine 53 import kotlinx.coroutines.flow.distinctUntilChanged 54 import kotlinx.coroutines.flow.flowOf 55 import kotlinx.coroutines.flow.map 56 import kotlinx.coroutines.withContext 57 58 private val TAG = StatusBarDisableFlagsInteractor::class.simpleName 59 60 /** 61 * Logic around StatusBarService#disableForUser, which is used to disable the home and recents 62 * button in certain device states. 63 * 64 * TODO(b/362313975): Remove post-Flexiglass, this duplicates StatusBarStartable logic. 65 */ 66 @SysUISingleton 67 class StatusBarDisableFlagsInteractor 68 @Inject 69 constructor( 70 @Application private val scope: CoroutineScope, 71 @ShadeDisplayAware private val context: Context, 72 @Background private val backgroundDispatcher: CoroutineDispatcher, 73 private val deviceEntryFaceAuthInteractor: DeviceEntryFaceAuthInteractor, 74 private val statusBarService: IStatusBarService, 75 private val processWrapper: ProcessWrapper, 76 keyguardTransitionInteractor: KeyguardTransitionInteractor, 77 selectedUserInteractor: SelectedUserInteractor, 78 deviceConfigInteractor: DeviceConfigInteractor, 79 navigationInteractor: NavigationInteractor, 80 authenticationInteractor: AuthenticationInteractor, 81 powerInteractor: PowerInteractor, 82 ) : CoreStartable { 83 84 private val disableToken: IBinder = Binder() 85 86 private val disableFlagsForUserId = 87 if (!KeyguardWmStateRefactor.isEnabled || SceneContainerFlag.isEnabled) { 88 flowOf(Pair(0, StatusBarManager.DISABLE_NONE)) 89 } else { 90 combine( 91 selectedUserInteractor.selectedUser, 92 keyguardTransitionInteractor.startedKeyguardTransitionStep.map { it.to }, 93 deviceConfigInteractor.property( 94 namespace = DeviceConfig.NAMESPACE_SYSTEMUI, 95 name = SystemUiDeviceConfigFlags.NAV_BAR_HANDLE_SHOW_OVER_LOCKSCREEN, 96 default = true, 97 ), 98 navigationInteractor.isGesturalMode, 99 authenticationInteractor.authenticationMethod, 100 powerInteractor.detailedWakefulness, 101 ) { values -> 102 val selectedUserId = values[0] as Int 103 val startedState = values[1] as KeyguardState 104 val isShowHomeOverLockscreen = values[2] as Boolean 105 val isGesturalMode = values[3] as Boolean 106 val authenticationMethod = values[4] as AuthenticationMethodModel 107 val wakefulnessModel = values[5] as WakefulnessModel 108 val isOccluded = startedState == KeyguardState.OCCLUDED 109 110 val hideHomeAndRecentsForBouncer = 111 startedState == KeyguardState.PRIMARY_BOUNCER || 112 startedState == KeyguardState.ALTERNATE_BOUNCER 113 val isKeyguardShowing = startedState != KeyguardState.GONE 114 val isPowerGestureIntercepted = 115 with(wakefulnessModel) { 116 isAwake() && 117 powerButtonLaunchGestureTriggered && 118 lastSleepReason == WakeSleepReason.POWER_BUTTON 119 } 120 121 var flags = StatusBarManager.DISABLE_NONE 122 123 if (hideHomeAndRecentsForBouncer || (isKeyguardShowing && !isOccluded)) { 124 // Hide the home button/nav handle if we're on keyguard and either: 125 // - Going to AOD, in which case the handle should animate away. 126 // - Nav handle is configured not to show on lockscreen. 127 // - There is no nav handle. 128 if ( 129 startedState == KeyguardState.AOD || 130 !isShowHomeOverLockscreen || 131 !isGesturalMode 132 ) { 133 flags = flags or StatusBarManager.DISABLE_HOME 134 } 135 flags = flags or StatusBarManager.DISABLE_RECENT 136 } 137 138 if ( 139 isPowerGestureIntercepted && 140 isOccluded && 141 authenticationMethod.isSecure && 142 deviceEntryFaceAuthInteractor.isFaceAuthEnabledAndEnrolled() 143 ) { 144 flags = flags or StatusBarManager.DISABLE_RECENT 145 } 146 147 selectedUserId to flags 148 } 149 .distinctUntilChanged() 150 } 151 152 @SuppressLint("WrongConstant", "NonInjectedService") 153 override fun start() { 154 if (!KeyguardWmStateRefactor.isEnabled || SceneContainerFlag.isEnabled) { 155 return 156 } 157 158 // TODO(b/341604160): Remove this blocking logic once StatusBarManagerService supports 159 // visible background users properly. 160 if ( 161 UserManager.isVisibleBackgroundUsersEnabled() && 162 !processWrapper.isSystemUser() && 163 !processWrapper.isForegroundUserOrProfile() 164 ) { 165 // Currently, only one SysUI process can register with IStatusBarService to listen 166 // for the CommandQueue events. 167 // In the Multi Display configuration with concurrent multi users (primarily used 168 // in Automotive), a visible background user (Automotive Multi Display passengers) 169 // could also access this code path. Given this limitation and we only allow the 170 // current user's SysUI process to register with IStatusBarService, we need to prevent 171 // calls into IStatusBarService from visible background users. 172 Log.d(TAG, "Status bar manager is disabled for visible background users") 173 return 174 } 175 176 scope.launch { 177 disableFlagsForUserId.collect { (selectedUserId, flags) -> 178 if (context.getSystemService(Context.STATUS_BAR_SERVICE) == null) { 179 return@collect 180 } 181 182 withContext(backgroundDispatcher) { 183 try { 184 statusBarService.disableForUser( 185 flags, 186 disableToken, 187 context.packageName, 188 selectedUserId, 189 ) 190 } catch (e: RemoteException) { 191 e.printStackTrace() 192 } 193 } 194 } 195 } 196 } 197 } 198