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