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