1 /* 2 * Copyright (C) 2023 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.statusbar.notification.interruption 18 19 import android.app.NotificationManager.IMPORTANCE_HIGH 20 import android.os.PowerManager 21 import com.android.internal.logging.UiEventLogger.UiEventEnum 22 import com.android.systemui.plugins.statusbar.StatusBarStateController 23 import com.android.systemui.statusbar.StatusBarState.KEYGUARD 24 import com.android.systemui.statusbar.notification.collection.NotificationEntry 25 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_DEVICE_DREAMING 26 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_DEVICE_NOT_INTERACTIVE 27 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_DEVICE_NOT_PROVISIONED 28 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_KEYGUARD_OCCLUDED 29 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_KEYGUARD_SHOWING 30 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_LOCKED_SHADE 31 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.FSI_USER_SETUP_INCOMPLETE 32 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_EXPECTED_TO_HUN 33 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_NOT_IMPORTANT_ENOUGH 34 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_NO_FULL_SCREEN_INTENT 35 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_NO_HUN_OR_KEYGUARD 36 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_PACKAGE_SUSPENDED 37 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SHOW_STICKY_HUN 38 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SUPPRESSED_BY_DND 39 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SUPPRESSED_ONLY_BY_DND 40 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SUPPRESSIVE_BUBBLE_METADATA 41 import com.android.systemui.statusbar.notification.interruption.FullScreenIntentDecisionProvider.DecisionImpl.NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR 42 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD 43 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_BUBBLE_METADATA 44 import com.android.systemui.statusbar.notification.interruption.NotificationInterruptStateProviderImpl.NotificationInterruptEvent.FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR 45 import com.android.systemui.statusbar.notification.interruption.VisualInterruptionSuppressor.EventLogData 46 import com.android.systemui.statusbar.policy.DeviceProvisionedController 47 import com.android.systemui.statusbar.policy.KeyguardStateController 48 49 class FullScreenIntentDecisionProvider( 50 private val deviceProvisionedController: DeviceProvisionedController, 51 private val keyguardStateController: KeyguardStateController, 52 private val powerManager: PowerManager, 53 private val statusBarStateController: StatusBarStateController 54 ) { 55 interface Decision { 56 val shouldFsi: Boolean 57 val wouldFsiWithoutDnd: Boolean 58 val logReason: String 59 val shouldLog: Boolean 60 val isWarning: Boolean 61 val uiEventId: UiEventEnum? 62 val eventLogData: EventLogData? 63 } 64 65 private enum class DecisionImpl( 66 override val shouldFsi: Boolean, 67 override val logReason: String, 68 override val wouldFsiWithoutDnd: Boolean = shouldFsi, 69 val supersedesDnd: Boolean = false, 70 override val shouldLog: Boolean = true, 71 override val isWarning: Boolean = false, 72 override val uiEventId: UiEventEnum? = null, 73 override val eventLogData: EventLogData? = null 74 ) : Decision { 75 NO_FSI_NO_FULL_SCREEN_INTENT( 76 false, 77 "no full-screen intent", 78 supersedesDnd = true, 79 shouldLog = false 80 ), 81 NO_FSI_SHOW_STICKY_HUN(false, "full-screen intents are disabled", supersedesDnd = true), 82 NO_FSI_NOT_IMPORTANT_ENOUGH(false, "not important enough"), 83 NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR( 84 false, 85 "suppressive group alert behavior", 86 isWarning = true, 87 uiEventId = FSI_SUPPRESSED_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR, 88 eventLogData = EventLogData("231322873", "groupAlertBehavior") 89 ), 90 NO_FSI_SUPPRESSIVE_BUBBLE_METADATA( 91 false, 92 "suppressive bubble metadata", 93 isWarning = true, 94 uiEventId = FSI_SUPPRESSED_SUPPRESSIVE_BUBBLE_METADATA, 95 eventLogData = EventLogData("274759612", "bubbleMetadata") 96 ), 97 NO_FSI_PACKAGE_SUSPENDED(false, "package suspended"), 98 FSI_DEVICE_NOT_INTERACTIVE(true, "device is not interactive"), 99 FSI_DEVICE_DREAMING(true, "device is dreaming"), 100 FSI_KEYGUARD_SHOWING(true, "keyguard is showing"), 101 NO_FSI_EXPECTED_TO_HUN(false, "expected to heads-up instead"), 102 FSI_KEYGUARD_OCCLUDED(true, "keyguard is occluded"), 103 FSI_LOCKED_SHADE(true, "locked shade"), 104 FSI_DEVICE_NOT_PROVISIONED(true, "device not provisioned"), 105 FSI_USER_SETUP_INCOMPLETE(true, "user setup incomplete"), 106 NO_FSI_NO_HUN_OR_KEYGUARD( 107 false, 108 "no HUN or keyguard", 109 isWarning = true, 110 uiEventId = FSI_SUPPRESSED_NO_HUN_OR_KEYGUARD, 111 eventLogData = EventLogData("231322873", "no hun or keyguard") 112 ), 113 NO_FSI_SUPPRESSED_BY_DND(false, "suppressed by DND", wouldFsiWithoutDnd = false), 114 NO_FSI_SUPPRESSED_ONLY_BY_DND(false, "suppressed only by DND", wouldFsiWithoutDnd = true) 115 } 116 makeFullScreenIntentDecisionnull117 fun makeFullScreenIntentDecision(entry: NotificationEntry, couldHeadsUp: Boolean): Decision { 118 val reasonWithoutDnd = makeDecisionWithoutDnd(entry, couldHeadsUp) 119 120 val suppressedWithoutDnd = !reasonWithoutDnd.shouldFsi 121 val suppressedByDnd = entry.shouldSuppressFullScreenIntent() 122 123 val reasonWithDnd = 124 when { 125 reasonWithoutDnd.supersedesDnd -> reasonWithoutDnd 126 suppressedByDnd && !suppressedWithoutDnd -> NO_FSI_SUPPRESSED_ONLY_BY_DND 127 suppressedByDnd -> NO_FSI_SUPPRESSED_BY_DND 128 else -> reasonWithoutDnd 129 } 130 131 return reasonWithDnd 132 } 133 makeDecisionWithoutDndnull134 private fun makeDecisionWithoutDnd( 135 entry: NotificationEntry, 136 couldHeadsUp: Boolean 137 ): DecisionImpl { 138 val sbn = entry.sbn 139 val notification = sbn.notification!! 140 141 if (notification.fullScreenIntent == null) { 142 return if (entry.isStickyAndNotDemoted) { 143 NO_FSI_SHOW_STICKY_HUN 144 } else { 145 NO_FSI_NO_FULL_SCREEN_INTENT 146 } 147 } 148 149 if (entry.importance < IMPORTANCE_HIGH) { 150 return NO_FSI_NOT_IMPORTANT_ENOUGH 151 } 152 153 if (sbn.isGroup && notification.suppressAlertingDueToGrouping()) { 154 return NO_FSI_SUPPRESSIVE_GROUP_ALERT_BEHAVIOR 155 } 156 157 val bubbleMetadata = notification.bubbleMetadata 158 if (bubbleMetadata != null && bubbleMetadata.isNotificationSuppressed) { 159 return NO_FSI_SUPPRESSIVE_BUBBLE_METADATA 160 } 161 162 if (entry.ranking.isSuspended) { 163 return NO_FSI_PACKAGE_SUSPENDED 164 } 165 166 if (!powerManager.isInteractive) { 167 return FSI_DEVICE_NOT_INTERACTIVE 168 } 169 170 if (statusBarStateController.isDreaming) { 171 return FSI_DEVICE_DREAMING 172 } 173 174 if (statusBarStateController.state == KEYGUARD) { 175 return FSI_KEYGUARD_SHOWING 176 } 177 178 if (couldHeadsUp) { 179 return NO_FSI_EXPECTED_TO_HUN 180 } 181 182 if (keyguardStateController.isShowing) { 183 return if (keyguardStateController.isOccluded) { 184 FSI_KEYGUARD_OCCLUDED 185 } else { 186 FSI_LOCKED_SHADE 187 } 188 } 189 190 if (!deviceProvisionedController.isDeviceProvisioned) { 191 return FSI_DEVICE_NOT_PROVISIONED 192 } 193 194 if (!deviceProvisionedController.isCurrentUserSetup) { 195 return FSI_USER_SETUP_INCOMPLETE 196 } 197 198 return NO_FSI_NO_HUN_OR_KEYGUARD 199 } 200 } 201