1 package com.android.systemui.statusbar.notification.interruption
2 
3 import android.app.Notification.VISIBILITY_SECRET
4 import android.content.Context
5 import android.database.ContentObserver
6 import android.net.Uri
7 import android.os.Handler
8 import android.os.HandlerExecutor
9 import android.os.UserHandle
10 import android.provider.Settings
11 import com.android.keyguard.KeyguardUpdateMonitor
12 import com.android.keyguard.KeyguardUpdateMonitorCallback
13 import com.android.systemui.CoreStartable
14 import com.android.systemui.dagger.SysUISingleton
15 import com.android.systemui.dagger.qualifiers.Main
16 import com.android.systemui.flags.FeatureFlagsClassic
17 import com.android.systemui.plugins.statusbar.StatusBarStateController
18 import com.android.systemui.settings.UserTracker
19 import com.android.systemui.statusbar.NotificationLockscreenUserManager
20 import com.android.systemui.statusbar.StatusBarState
21 import com.android.systemui.statusbar.SysuiStatusBarStateController
22 import com.android.systemui.statusbar.notification.collection.ListEntry
23 import com.android.systemui.statusbar.notification.collection.NotificationEntry
24 import com.android.systemui.statusbar.notification.collection.provider.HighPriorityProvider
25 import com.android.systemui.statusbar.policy.KeyguardStateController
26 import com.android.systemui.util.ListenerSet
27 import com.android.systemui.util.asIndenting
28 import com.android.systemui.util.println
29 import com.android.systemui.util.settings.GlobalSettings
30 import com.android.systemui.util.settings.SecureSettings
31 import com.android.systemui.util.withIncreasedIndent
32 import dagger.Binds
33 import dagger.Module
34 import dagger.multibindings.ClassKey
35 import dagger.multibindings.IntoMap
36 import java.io.PrintWriter
37 import java.util.function.Consumer
38 import javax.inject.Inject
39 
40 /** Determines if notifications should be visible based on the state of the keyguard. */
41 interface KeyguardNotificationVisibilityProvider {
42     /**
43      * Determines if the given notification should be hidden based on the current keyguard state. If
44      * a [Consumer] registered via [addOnStateChangedListener] is invoked, the results of this
45      * method may no longer be valid and should be re-queried.
46      */
shouldHideNotificationnull47     fun shouldHideNotification(entry: NotificationEntry): Boolean
48 
49     /** Registers a listener to be notified when the internal keyguard state has been updated. */
50     fun addOnStateChangedListener(listener: Consumer<String>)
51 
52     /** Unregisters a listener previously registered with [addOnStateChangedListener]. */
53     fun removeOnStateChangedListener(listener: Consumer<String>)
54 }
55 
56 /** Provides a [KeyguardNotificationVisibilityProvider] in [SysUISingleton] scope. */
57 @Module(includes = [KeyguardNotificationVisibilityProviderImplModule::class])
58 object KeyguardNotificationVisibilityProviderModule
59 
60 @Module
61 interface KeyguardNotificationVisibilityProviderImplModule {
62     @Binds
63     fun bindImpl(
64         impl: KeyguardNotificationVisibilityProviderImpl
65     ): KeyguardNotificationVisibilityProvider
66 
67     @Binds
68     @IntoMap
69     @ClassKey(KeyguardNotificationVisibilityProvider::class)
70     fun bindStartable(impl: KeyguardNotificationVisibilityProviderImpl): CoreStartable
71 }
72 
73 @SysUISingleton
74 class KeyguardNotificationVisibilityProviderImpl
75 @Inject
76 constructor(
77     @Main private val handler: Handler,
78     private val keyguardStateController: KeyguardStateController,
79     private val lockscreenUserManager: NotificationLockscreenUserManager,
80     private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
81     private val highPriorityProvider: HighPriorityProvider,
82     private val statusBarStateController: SysuiStatusBarStateController,
83     private val userTracker: UserTracker,
84     private val secureSettings: SecureSettings,
85     private val globalSettings: GlobalSettings,
86     private val featureFlags: FeatureFlagsClassic
87 ) : CoreStartable, KeyguardNotificationVisibilityProvider {
88     private val showSilentNotifsUri =
89         secureSettings.getUriFor(Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS)
90     private val onStateChangedListeners = ListenerSet<Consumer<String>>()
91     private var hideSilentNotificationsOnLockscreen: Boolean = false
92 
93     private val userTrackerCallback =
94         object : UserTracker.Callback {
onUserChangednull95             override fun onUserChanged(newUser: Int, userContext: Context) {
96                 readShowSilentNotificationSetting()
97                 if (isLockedOrLocking) {
98                     // maybe public mode changed
99                     notifyStateChanged("onUserSwitched")
100                 }
101             }
102         }
103 
startnull104     override fun start() {
105         readShowSilentNotificationSetting()
106         keyguardStateController.addCallback(
107             object : KeyguardStateController.Callback {
108                 override fun onUnlockedChanged() {
109                     notifyStateChanged("onUnlockedChanged")
110                 }
111 
112                 override fun onKeyguardShowingChanged() {
113                     notifyStateChanged("onKeyguardShowingChanged")
114                 }
115             }
116         )
117         keyguardUpdateMonitor.registerCallback(
118             object : KeyguardUpdateMonitorCallback() {
119                 override fun onStrongAuthStateChanged(userId: Int) {
120                     notifyStateChanged("onStrongAuthStateChanged")
121                 }
122             }
123         )
124 
125         // register lockscreen settings changed callbacks:
126         val settingsObserver: ContentObserver =
127             object : ContentObserver(handler) {
128                 override fun onChange(selfChange: Boolean, uri: Uri?) {
129                     if (uri == showSilentNotifsUri) {
130                         readShowSilentNotificationSetting()
131                     }
132                     if (isLockedOrLocking) {
133                         notifyStateChanged("Settings $uri changed")
134                     }
135                 }
136             }
137 
138         secureSettings.registerContentObserverForUserSync(
139             Settings.Secure.LOCK_SCREEN_SHOW_NOTIFICATIONS,
140             settingsObserver,
141             UserHandle.USER_ALL
142         )
143 
144         secureSettings.registerContentObserverForUserSync(
145             Settings.Secure.LOCK_SCREEN_ALLOW_PRIVATE_NOTIFICATIONS,
146             true,
147             settingsObserver,
148             UserHandle.USER_ALL
149         )
150 
151         globalSettings.registerContentObserverSync(Settings.Global.ZEN_MODE, settingsObserver)
152 
153         secureSettings.registerContentObserverForUserSync(
154             Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS,
155             settingsObserver,
156             UserHandle.USER_ALL
157         )
158 
159         // register (maybe) public mode changed callbacks:
160         statusBarStateController.addCallback(
161             object : StatusBarStateController.StateListener {
162                 override fun onStateChanged(newState: Int) {
163                     notifyStateChanged("onStatusBarStateChanged")
164                 }
165 
166                 override fun onUpcomingStateChanged(state: Int) {
167                     notifyStateChanged("onStatusBarUpcomingStateChanged")
168                 }
169             }
170         )
171         userTracker.addCallback(userTrackerCallback, HandlerExecutor(handler))
172     }
173 
addOnStateChangedListenernull174     override fun addOnStateChangedListener(listener: Consumer<String>) {
175         onStateChangedListeners.addIfAbsent(listener)
176     }
177 
removeOnStateChangedListenernull178     override fun removeOnStateChangedListener(listener: Consumer<String>) {
179         onStateChangedListeners.remove(listener)
180     }
181 
notifyStateChangednull182     private fun notifyStateChanged(reason: String) {
183         onStateChangedListeners.forEach { it.accept(reason) }
184     }
185 
shouldHideNotificationnull186     override fun shouldHideNotification(entry: NotificationEntry): Boolean =
187         when {
188             // Keyguard state doesn't matter if the keyguard is not showing.
189             !isLockedOrLocking -> false
190             // Notifications not allowed on the lockscreen, always hide.
191             !lockscreenUserManager.shouldShowLockscreenNotifications() -> true
192             // User settings do not allow this notification on the lockscreen, so hide it.
193             userSettingsDisallowNotification(entry) -> true
194             // Entry is explicitly marked SECRET, so hide it.
195             entry.sbn.notification.visibility == VISIBILITY_SECRET -> true
196             // if entry is silent, apply custom logic to see if should hide
197             shouldHideIfEntrySilent(entry) -> true
198             else -> false
199         }
200 
shouldHideIfEntrySilentnull201     private fun shouldHideIfEntrySilent(entry: ListEntry): Boolean =
202         when {
203             // Show if explicitly high priority (not hidden)
204             highPriorityProvider.isExplicitlyHighPriority(entry) -> false
205             // Ambient notifications are hidden always from lock screen
206             entry.representativeEntry?.isAmbient == true -> true
207             // [Now notification is silent]
208             // Hide regardless of parent priority if user wants silent notifs hidden
209             hideSilentNotificationsOnLockscreen -> true
210             // Parent priority is high enough to be shown on the lockscreen, do not hide.
211             entry.parent?.let(::shouldHideIfEntrySilent) == false -> false
212             // Show when silent notifications are allowed on lockscreen
213             else -> false
214         }
215 
userSettingsDisallowNotificationnull216     private fun userSettingsDisallowNotification(entry: NotificationEntry): Boolean {
217         fun disallowForUser(user: Int) =
218             when {
219                 // user is in lockdown, always disallow
220                 keyguardUpdateMonitor.isUserInLockdown(user) -> true
221                 // device isn't public, no need to check public-related settings, so allow
222                 !lockscreenUserManager.isLockscreenPublicMode(user) -> false
223                 // entry is meant to be secret on the lockscreen, disallow
224                 isRankingVisibilitySecret(entry) -> true
225                 // disallow if user disallows notifications in public
226                 else -> !lockscreenUserManager.userAllowsNotificationsInPublic(user)
227             }
228         val currentUser = lockscreenUserManager.currentUserId
229         val notifUser = entry.sbn.user.identifier
230         return when {
231             disallowForUser(currentUser) -> true
232             notifUser == UserHandle.USER_ALL -> false
233             notifUser == currentUser -> false
234             else -> disallowForUser(notifUser)
235         }
236     }
237 
isRankingVisibilitySecretnull238     private fun isRankingVisibilitySecret(entry: NotificationEntry): Boolean {
239         // ranking.lockscreenVisibilityOverride contains possibly out of date DPC and Setting
240         // info, and NotificationLockscreenUserManagerImpl is already listening for updates
241         // to those
242         return entry.ranking.channel != null &&
243             entry.ranking.channel.lockscreenVisibility == VISIBILITY_SECRET
244     }
245 
dumpnull246     override fun dump(pw: PrintWriter, args: Array<out String>) =
247         pw.asIndenting().run {
248             println("isLockedOrLocking", isLockedOrLocking)
249             withIncreasedIndent {
250                 println("keyguardStateController.isShowing", keyguardStateController.isShowing)
251                 println(
252                     "statusBarStateController.currentOrUpcomingState",
253                     statusBarStateController.currentOrUpcomingState
254                 )
255             }
256             println("hideSilentNotificationsOnLockscreen", hideSilentNotificationsOnLockscreen)
257         }
258 
259     private val isLockedOrLocking
260         get() =
261             keyguardStateController.isShowing ||
262                 statusBarStateController.currentOrUpcomingState == StatusBarState.KEYGUARD
263 
readShowSilentNotificationSettingnull264     private fun readShowSilentNotificationSetting() {
265         val showSilentNotifs =
266             secureSettings.getBoolForUser(
267                 Settings.Secure.LOCK_SCREEN_SHOW_SILENT_NOTIFICATIONS,
268                 false,
269                 UserHandle.USER_CURRENT
270             )
271         hideSilentNotificationsOnLockscreen = !showSilentNotifs
272     }
273 }
274