Fix direct alert delivery across users
This commit is contained in:
@@ -52,6 +52,15 @@
|
|||||||
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
<action android:name="android.intent.action.BOOT_COMPLETED" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</receiver>
|
</receiver>
|
||||||
|
|
||||||
|
<receiver
|
||||||
|
android:name=".module.DirectAlertReceiver"
|
||||||
|
android:exported="true"
|
||||||
|
android:permission="se.ajpanton.notificationsmaster.permission.UPDATE_ALERT_POLICY">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="se.ajpanton.notificationsmaster.PLAY_DIRECT_ALERT" />
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
</manifest>
|
</manifest>
|
||||||
|
|||||||
@@ -2,12 +2,15 @@ package se.ajpanton.notificationsmaster
|
|||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
|
import se.ajpanton.notificationsmaster.alerts.AlertPlaybackRuntime
|
||||||
import se.ajpanton.notificationsmaster.data.EncryptedNotificationLogStore
|
import se.ajpanton.notificationsmaster.data.EncryptedNotificationLogStore
|
||||||
import se.ajpanton.notificationsmaster.settings.AppFilterSettingsStore
|
import se.ajpanton.notificationsmaster.settings.AppFilterSettingsStore
|
||||||
import se.ajpanton.notificationsmaster.settings.NotificationListenerComponentController
|
import se.ajpanton.notificationsmaster.settings.NotificationListenerComponentController
|
||||||
import se.ajpanton.notificationsmaster.settings.PerAppEventSettingsStore
|
import se.ajpanton.notificationsmaster.settings.PerAppEventSettingsStore
|
||||||
|
|
||||||
class NotificationLogApplication : Application() {
|
class NotificationLogApplication : Application() {
|
||||||
|
val alertPlayback by lazy { AlertPlaybackRuntime(this) }
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
synchronizeListenerComponent()
|
synchronizeListenerComponent()
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
package se.ajpanton.notificationsmaster.alerts
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
|
||||||
|
/** Process-wide playback queue shared by the notification listener and direct-alert handoff. */
|
||||||
|
class AlertPlaybackRuntime(context: Context) {
|
||||||
|
private var queueSettings = AlertQueueSettings()
|
||||||
|
private val controller = AlertPlaybackController(AndroidAlertEffectPlayer(context)) { queueSettings }
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun enqueue(alert: QueuedAlert, settings: AlertQueueSettings) {
|
||||||
|
queueSettings = settings
|
||||||
|
controller.enqueue(alert)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun stop() = controller.stop()
|
||||||
|
}
|
||||||
+5
-27
@@ -1,9 +1,7 @@
|
|||||||
package se.ajpanton.notificationsmaster.capture
|
package se.ajpanton.notificationsmaster.capture
|
||||||
|
|
||||||
import android.app.Notification
|
import android.app.Notification
|
||||||
import android.content.BroadcastReceiver
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.content.IntentFilter
|
|
||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.service.notification.NotificationListenerService
|
import android.service.notification.NotificationListenerService
|
||||||
import android.service.notification.StatusBarNotification
|
import android.service.notification.StatusBarNotification
|
||||||
@@ -11,12 +9,10 @@ import android.util.Log
|
|||||||
import se.ajpanton.notificationsmaster.alerts.AlertConfigurationStore
|
import se.ajpanton.notificationsmaster.alerts.AlertConfigurationStore
|
||||||
import se.ajpanton.notificationsmaster.alerts.AlertEvent
|
import se.ajpanton.notificationsmaster.alerts.AlertEvent
|
||||||
import se.ajpanton.notificationsmaster.alerts.AlertNotificationDispatcher
|
import se.ajpanton.notificationsmaster.alerts.AlertNotificationDispatcher
|
||||||
import se.ajpanton.notificationsmaster.alerts.AlertPlaybackController
|
|
||||||
import se.ajpanton.notificationsmaster.alerts.AlertQueueSettings
|
import se.ajpanton.notificationsmaster.alerts.AlertQueueSettings
|
||||||
import se.ajpanton.notificationsmaster.alerts.AlertSource
|
import se.ajpanton.notificationsmaster.alerts.AlertSource
|
||||||
import se.ajpanton.notificationsmaster.alerts.AndroidAlertEffectPlayer
|
import se.ajpanton.notificationsmaster.NotificationLogApplication
|
||||||
import se.ajpanton.notificationsmaster.module.AlertPolicySync
|
import se.ajpanton.notificationsmaster.module.AlertPolicySync
|
||||||
import se.ajpanton.notificationsmaster.module.DirectAlertSync
|
|
||||||
import se.ajpanton.notificationsmaster.data.EncryptedNotificationLogStore
|
import se.ajpanton.notificationsmaster.data.EncryptedNotificationLogStore
|
||||||
import se.ajpanton.notificationsmaster.data.EncryptedImageStore
|
import se.ajpanton.notificationsmaster.data.EncryptedImageStore
|
||||||
import se.ajpanton.notificationsmaster.model.NotificationAction
|
import se.ajpanton.notificationsmaster.model.NotificationAction
|
||||||
@@ -29,7 +25,6 @@ import se.ajpanton.notificationsmaster.settings.AppFilterSettingsStore
|
|||||||
import se.ajpanton.notificationsmaster.settings.PerAppEventSettingsStore
|
import se.ajpanton.notificationsmaster.settings.PerAppEventSettingsStore
|
||||||
import java.util.concurrent.ExecutorService
|
import java.util.concurrent.ExecutorService
|
||||||
import java.util.concurrent.Executors
|
import java.util.concurrent.Executors
|
||||||
import java.util.UUID
|
|
||||||
|
|
||||||
class NotificationCaptureService : NotificationListenerService() {
|
class NotificationCaptureService : NotificationListenerService() {
|
||||||
private val activeNotifications = mutableMapOf<String, NotificationSnapshot>()
|
private val activeNotifications = mutableMapOf<String, NotificationSnapshot>()
|
||||||
@@ -41,23 +36,8 @@ class NotificationCaptureService : NotificationListenerService() {
|
|||||||
private lateinit var perAppEventSettings: PerAppEventSettingsStore
|
private lateinit var perAppEventSettings: PerAppEventSettingsStore
|
||||||
private lateinit var imageStore: EncryptedImageStore
|
private lateinit var imageStore: EncryptedImageStore
|
||||||
private lateinit var alertConfigurationStore: AlertConfigurationStore
|
private lateinit var alertConfigurationStore: AlertConfigurationStore
|
||||||
private lateinit var alertPlayback: AlertPlaybackController
|
private lateinit var alertPlayback: se.ajpanton.notificationsmaster.alerts.AlertPlaybackRuntime
|
||||||
private var alertQueueSettings = AlertQueueSettings()
|
private var alertQueueSettings = AlertQueueSettings()
|
||||||
private val directAlertToken = UUID.randomUUID().toString()
|
|
||||||
private val directAlertReceiver = object : BroadcastReceiver() {
|
|
||||||
override fun onReceive(context: android.content.Context, intent: Intent) {
|
|
||||||
if (intent.action != DirectAlertSync.ACTION) return
|
|
||||||
val request = DirectAlertSync.snapshot(intent, directAlertToken)
|
|
||||||
if (request == null) {
|
|
||||||
Log.w(TAG, "Rejected malformed direct alert request")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
request.let { (ruleId, snapshot) ->
|
|
||||||
Log.i(TAG, "Playing matched direct notification vibration")
|
|
||||||
alertPlayback.enqueue(se.ajpanton.notificationsmaster.alerts.QueuedAlert(ruleId, snapshot))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onCreate() {
|
override fun onCreate() {
|
||||||
super.onCreate()
|
super.onCreate()
|
||||||
@@ -68,8 +48,7 @@ class NotificationCaptureService : NotificationListenerService() {
|
|||||||
perAppEventSettings = PerAppEventSettingsStore(this)
|
perAppEventSettings = PerAppEventSettingsStore(this)
|
||||||
imageStore = EncryptedImageStore(this)
|
imageStore = EncryptedImageStore(this)
|
||||||
alertConfigurationStore = AlertConfigurationStore(this)
|
alertConfigurationStore = AlertConfigurationStore(this)
|
||||||
alertPlayback = AlertPlaybackController(AndroidAlertEffectPlayer(this)) { alertQueueSettings }
|
alertPlayback = (application as NotificationLogApplication).alertPlayback
|
||||||
registerReceiver(directAlertReceiver, IntentFilter(DirectAlertSync.ACTION), null, null, RECEIVER_EXPORTED)
|
|
||||||
writeExecutor = Executors.newSingleThreadExecutor { runnable ->
|
writeExecutor = Executors.newSingleThreadExecutor { runnable ->
|
||||||
Thread(runnable, "notification-log-writer")
|
Thread(runnable, "notification-log-writer")
|
||||||
}
|
}
|
||||||
@@ -77,7 +56,7 @@ class NotificationCaptureService : NotificationListenerService() {
|
|||||||
|
|
||||||
override fun onListenerConnected() {
|
override fun onListenerConnected() {
|
||||||
super.onListenerConnected()
|
super.onListenerConnected()
|
||||||
AlertPolicySync.publishPlaybackState(this, true, directAlertToken)
|
AlertPolicySync.publishPlaybackState(this, true)
|
||||||
getActiveNotifications()?.forEach { sbn ->
|
getActiveNotifications()?.forEach { sbn ->
|
||||||
val snapshot = NotificationContents.snapshot(sbn)
|
val snapshot = NotificationContents.snapshot(sbn)
|
||||||
SeenApps.markSeen(snapshot.packageName)
|
SeenApps.markSeen(snapshot.packageName)
|
||||||
@@ -136,7 +115,6 @@ class NotificationCaptureService : NotificationListenerService() {
|
|||||||
override fun onDestroy() {
|
override fun onDestroy() {
|
||||||
AlertPolicySync.publishPlaybackState(this, false)
|
AlertPolicySync.publishPlaybackState(this, false)
|
||||||
alertPlayback.stop()
|
alertPlayback.stop()
|
||||||
unregisterReceiver(directAlertReceiver)
|
|
||||||
writeExecutor.shutdown()
|
writeExecutor.shutdown()
|
||||||
super.onDestroy()
|
super.onDestroy()
|
||||||
}
|
}
|
||||||
@@ -211,7 +189,7 @@ class NotificationCaptureService : NotificationListenerService() {
|
|||||||
configuration,
|
configuration,
|
||||||
AlertEvent(snapshot.packageName, source, snapshot.alertTitle, snapshot.alertBody, snapshot.alertSender),
|
AlertEvent(snapshot.packageName, source, snapshot.alertTitle, snapshot.alertBody, snapshot.alertSender),
|
||||||
isRoutineUpdate = source == AlertSource.NOTIFICATION_UPDATE && snapshot.isRoutine,
|
isRoutineUpdate = source == AlertSource.NOTIFICATION_UPDATE && snapshot.isRoutine,
|
||||||
).forEach(alertPlayback::enqueue)
|
).forEach { alertPlayback.enqueue(it, alertQueueSettings) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun appName(packageName: String): String = try {
|
private fun appName(packageName: String): String = try {
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ internal object AlertPolicySync {
|
|||||||
const val PLAYBACK_STATE_ACTION = "se.ajpanton.notificationsmaster.UPDATE_ALERT_PLAYBACK_STATE"
|
const val PLAYBACK_STATE_ACTION = "se.ajpanton.notificationsmaster.UPDATE_ALERT_PLAYBACK_STATE"
|
||||||
const val EXTRA_CONFIGURATION = "configuration"
|
const val EXTRA_CONFIGURATION = "configuration"
|
||||||
const val EXTRA_PLAYBACK_READY = "playback_ready"
|
const val EXTRA_PLAYBACK_READY = "playback_ready"
|
||||||
const val EXTRA_DIRECT_ALERT_TOKEN = "direct_alert_token"
|
|
||||||
const val PERMISSION = "se.ajpanton.notificationsmaster.permission.UPDATE_ALERT_POLICY"
|
const val PERMISSION = "se.ajpanton.notificationsmaster.permission.UPDATE_ALERT_POLICY"
|
||||||
private const val SYSTEM_PACKAGE = "android"
|
private const val SYSTEM_PACKAGE = "android"
|
||||||
private const val MAX_CONFIGURATION_BYTES = 64 * 1024
|
private const val MAX_CONFIGURATION_BYTES = 64 * 1024
|
||||||
@@ -36,12 +35,11 @@ internal object AlertPolicySync {
|
|||||||
Log.i(TAG, "Published alert policy snapshot")
|
Log.i(TAG, "Published alert policy snapshot")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun publishPlaybackState(context: Context, ready: Boolean, directAlertToken: String? = null) {
|
fun publishPlaybackState(context: Context, ready: Boolean) {
|
||||||
context.sendBroadcast(
|
context.sendBroadcast(
|
||||||
Intent(PLAYBACK_STATE_ACTION)
|
Intent(PLAYBACK_STATE_ACTION)
|
||||||
.setPackage(SYSTEM_PACKAGE)
|
.setPackage(SYSTEM_PACKAGE)
|
||||||
.putExtra(EXTRA_PLAYBACK_READY, ready)
|
.putExtra(EXTRA_PLAYBACK_READY, ready),
|
||||||
.putExtra(EXTRA_DIRECT_ALERT_TOKEN, directAlertToken),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
package se.ajpanton.notificationsmaster.module
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.util.Log
|
||||||
|
import se.ajpanton.notificationsmaster.NotificationLogApplication
|
||||||
|
import se.ajpanton.notificationsmaster.alerts.AlertConfigurationStore
|
||||||
|
import se.ajpanton.notificationsmaster.alerts.QueuedAlert
|
||||||
|
|
||||||
|
/** Signature-protected handoff that can start the app when its listener process was evicted. */
|
||||||
|
class DirectAlertReceiver : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
if (intent.action != DirectAlertSync.ACTION) return
|
||||||
|
val request = DirectAlertSync.snapshot(intent) ?: run {
|
||||||
|
Log.w(TAG, "Rejected malformed direct alert request")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
val (ruleId, snapshot) = request
|
||||||
|
val settings = AlertConfigurationStore(context).load().queueSettings
|
||||||
|
(context.applicationContext as NotificationLogApplication).alertPlayback.enqueue(
|
||||||
|
QueuedAlert(ruleId, snapshot),
|
||||||
|
settings,
|
||||||
|
)
|
||||||
|
Log.i(TAG, "Playing matched direct notification alert")
|
||||||
|
}
|
||||||
|
|
||||||
|
private companion object {
|
||||||
|
const val TAG = "NotificationCapture"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,36 +1,44 @@
|
|||||||
package se.ajpanton.notificationsmaster.module
|
package se.ajpanton.notificationsmaster.module
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.ComponentName
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
|
import android.os.UserHandle
|
||||||
|
import android.os.Binder
|
||||||
import se.ajpanton.notificationsmaster.alerts.AlertDecision
|
import se.ajpanton.notificationsmaster.alerts.AlertDecision
|
||||||
import se.ajpanton.notificationsmaster.alerts.AlertSnapshot
|
import se.ajpanton.notificationsmaster.alerts.AlertSnapshot
|
||||||
import se.ajpanton.notificationsmaster.alerts.snapshot
|
import se.ajpanton.notificationsmaster.alerts.snapshot
|
||||||
|
|
||||||
/** One-way requests from system_server to the live listener for direct vibration matches. */
|
/** One-way requests from system_server to the app's private direct-alert receiver. */
|
||||||
internal object DirectAlertSync {
|
internal object DirectAlertSync {
|
||||||
const val ACTION = "se.ajpanton.notificationsmaster.PLAY_DIRECT_ALERT"
|
const val ACTION = "se.ajpanton.notificationsmaster.PLAY_DIRECT_ALERT"
|
||||||
private const val PACKAGE = "se.ajpanton.notificationsmaster"
|
private const val PACKAGE = "se.ajpanton.notificationsmaster"
|
||||||
|
private const val RECEIVER = "$PACKAGE.module.DirectAlertReceiver"
|
||||||
private const val EXTRA_RULE_ID = "ruleId"
|
private const val EXTRA_RULE_ID = "ruleId"
|
||||||
private const val EXTRA_SOUND_URI = "soundUri"
|
private const val EXTRA_SOUND_URI = "soundUri"
|
||||||
private const val EXTRA_VIBRATION = "vibration"
|
private const val EXTRA_VIBRATION = "vibration"
|
||||||
private const val EXTRA_PROTECTED = "protected"
|
private const val EXTRA_PROTECTED = "protected"
|
||||||
private const val EXTRA_ALLOW_DND = "allowDnd"
|
private const val EXTRA_ALLOW_DND = "allowDnd"
|
||||||
private const val EXTRA_TOKEN = "token"
|
|
||||||
|
|
||||||
fun send(context: Context, decision: AlertDecision, token: String): Boolean {
|
@SuppressLint("MissingPermission") // This method runs only in system_server via the LSPosed module.
|
||||||
|
fun send(context: Context, decision: AlertDecision, userId: Int): Boolean {
|
||||||
val snapshot = decision.snapshot() ?: return false
|
val snapshot = decision.snapshot() ?: return false
|
||||||
context.sendBroadcast(Intent(ACTION).setPackage(PACKAGE)
|
val identity = Binder.clearCallingIdentity()
|
||||||
.putExtra(EXTRA_RULE_ID, decision.ruleId)
|
try {
|
||||||
.putExtra(EXTRA_SOUND_URI, snapshot.soundUri)
|
context.sendBroadcastAsUser(Intent(ACTION).setComponent(ComponentName(PACKAGE, RECEIVER))
|
||||||
.putExtra(EXTRA_VIBRATION, snapshot.vibrationPattern.toLongArray())
|
.putExtra(EXTRA_RULE_ID, decision.ruleId)
|
||||||
.putExtra(EXTRA_PROTECTED, snapshot.playToCompletion)
|
.putExtra(EXTRA_SOUND_URI, snapshot.soundUri)
|
||||||
.putExtra(EXTRA_ALLOW_DND, snapshot.allowDuringDnd)
|
.putExtra(EXTRA_VIBRATION, snapshot.vibrationPattern.toLongArray())
|
||||||
.putExtra(EXTRA_TOKEN, token))
|
.putExtra(EXTRA_PROTECTED, snapshot.playToCompletion)
|
||||||
|
.putExtra(EXTRA_ALLOW_DND, snapshot.allowDuringDnd), userHandle(userId))
|
||||||
|
} finally {
|
||||||
|
Binder.restoreCallingIdentity(identity)
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun snapshot(intent: Intent, token: String): Pair<String, AlertSnapshot>? = runCatching {
|
fun snapshot(intent: Intent): Pair<String, AlertSnapshot>? = runCatching {
|
||||||
require(intent.getStringExtra(EXTRA_TOKEN) == token)
|
|
||||||
val soundUri = intent.getStringExtra(EXTRA_SOUND_URI)
|
val soundUri = intent.getStringExtra(EXTRA_SOUND_URI)
|
||||||
val vibration = intent.getLongArrayExtra(EXTRA_VIBRATION)?.toList().orEmpty()
|
val vibration = intent.getLongArrayExtra(EXTRA_VIBRATION)?.toList().orEmpty()
|
||||||
val snapshot = AlertSnapshot(
|
val snapshot = AlertSnapshot(
|
||||||
@@ -41,4 +49,8 @@ internal object DirectAlertSync {
|
|||||||
)
|
)
|
||||||
intent.getStringExtra(EXTRA_RULE_ID)?.takeIf(String::isNotBlank)?.let { it to snapshot }
|
intent.getStringExtra(EXTRA_RULE_ID)?.takeIf(String::isNotBlank)?.let { it to snapshot }
|
||||||
}.getOrNull()
|
}.getOrNull()
|
||||||
|
|
||||||
|
private fun userHandle(userId: Int): UserHandle = UserHandle::class.java
|
||||||
|
.getDeclaredConstructor(Int::class.javaPrimitiveType)
|
||||||
|
.newInstance(userId)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,12 +18,13 @@ internal class DirectVibrationBridge(private val framework: XposedInterface) {
|
|||||||
}
|
}
|
||||||
method.isAccessible = true
|
method.isAccessible = true
|
||||||
framework.hook(method).intercept { chain ->
|
framework.hook(method).intercept { chain ->
|
||||||
|
val uid = chain.args.getOrNull(UID_INDEX) as? Int
|
||||||
val packageName = chain.args.getOrNull(PACKAGE_INDEX) as? String
|
val packageName = chain.args.getOrNull(PACKAGE_INDEX) as? String
|
||||||
val attributes = chain.args.getOrNull(ATTRIBUTES_INDEX) as? VibrationAttributes
|
val attributes = chain.args.getOrNull(ATTRIBUTES_INDEX) as? VibrationAttributes
|
||||||
val event = packageName?.takeIf { attributes?.usage == VibrationAttributes.USAGE_NOTIFICATION }
|
val event = packageName?.takeIf { attributes?.usage == VibrationAttributes.USAGE_NOTIFICATION }
|
||||||
?.let { se.ajpanton.notificationsmaster.alerts.AlertEvent(it, AlertSource.DIRECT_NOTIFICATION_VIBRATION) }
|
?.let { se.ajpanton.notificationsmaster.alerts.AlertEvent(it, AlertSource.DIRECT_NOTIFICATION_VIBRATION) }
|
||||||
if (event != null && SystemAlertPolicyCache.shouldSuppress(event)) {
|
if (event != null && SystemAlertPolicyCache.shouldSuppress(event)) {
|
||||||
SystemAlertPolicyCache.sendDirectAlerts(event)
|
SystemAlertPolicyCache.sendDirectAlerts(event, (uid ?: 0).coerceAtLeast(0) / USER_UID_RANGE)
|
||||||
Log.i(TAG, "Suppressed matched direct notification vibration from $packageName")
|
Log.i(TAG, "Suppressed matched direct notification vibration from $packageName")
|
||||||
null
|
null
|
||||||
} else {
|
} else {
|
||||||
@@ -44,7 +45,9 @@ internal class DirectVibrationBridge(private val framework: XposedInterface) {
|
|||||||
const val TAG = "NotificationsMaster"
|
const val TAG = "NotificationsMaster"
|
||||||
const val VIBRATOR_MANAGER_SERVICE = "com.android.server.vibrator.VibratorManagerService"
|
const val VIBRATOR_MANAGER_SERVICE = "com.android.server.vibrator.VibratorManagerService"
|
||||||
const val PACKAGE_INDEX = 2
|
const val PACKAGE_INDEX = 2
|
||||||
|
const val UID_INDEX = 0
|
||||||
const val ATTRIBUTES_INDEX = 4
|
const val ATTRIBUTES_INDEX = 4
|
||||||
|
const val USER_UID_RANGE = 100_000
|
||||||
val VIBRATE_PARAMETERS = arrayOf(
|
val VIBRATE_PARAMETERS = arrayOf(
|
||||||
Int::class.javaPrimitiveType,
|
Int::class.javaPrimitiveType,
|
||||||
Int::class.javaPrimitiveType,
|
Int::class.javaPrimitiveType,
|
||||||
|
|||||||
@@ -26,8 +26,6 @@ internal object SystemAlertPolicyCache {
|
|||||||
private var configuration = AlertConfiguration(enabled = false)
|
private var configuration = AlertConfiguration(enabled = false)
|
||||||
@Volatile
|
@Volatile
|
||||||
private var playbackReady = false
|
private var playbackReady = false
|
||||||
@Volatile
|
|
||||||
private var directAlertToken: String? = null
|
|
||||||
private var installed = false
|
private var installed = false
|
||||||
private var attempts = 0
|
private var attempts = 0
|
||||||
|
|
||||||
@@ -64,15 +62,13 @@ internal object SystemAlertPolicyCache {
|
|||||||
fun evaluate(event: AlertEvent): AlertEvaluation =
|
fun evaluate(event: AlertEvent): AlertEvaluation =
|
||||||
AlertPolicyEvaluator.evaluate(configuration, event, isRoutineUpdate = false)
|
AlertPolicyEvaluator.evaluate(configuration, event, isRoutineUpdate = false)
|
||||||
|
|
||||||
fun shouldSuppress(event: AlertEvent): Boolean = playbackReady &&
|
fun shouldSuppress(event: AlertEvent): Boolean =
|
||||||
(!event.source.isDirect || directAlertToken != null) &&
|
(event.source.isDirect || playbackReady) && evaluate(event).suppressesOriginal()
|
||||||
evaluate(event).suppressesOriginal()
|
|
||||||
|
|
||||||
fun sendDirectAlerts(event: AlertEvent) {
|
fun sendDirectAlerts(event: AlertEvent, userId: Int) {
|
||||||
val context = systemContext() ?: return
|
val context = systemContext() ?: return
|
||||||
val token = directAlertToken ?: return
|
|
||||||
val decisions = evaluate(event).decisions
|
val decisions = evaluate(event).decisions
|
||||||
val sent = decisions.count { DirectAlertSync.send(context, it, token) }
|
val sent = decisions.count { DirectAlertSync.send(context, it, userId) }
|
||||||
Log.i(TAG, "Sent $sent direct alert request(s) for ${event.packageName}")
|
Log.i(TAG, "Sent $sent direct alert request(s) for ${event.packageName}")
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -95,8 +91,6 @@ internal object SystemAlertPolicyCache {
|
|||||||
override fun onReceive(context: Context, intent: Intent) {
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
if (intent.action == AlertPolicySync.PLAYBACK_STATE_ACTION) {
|
if (intent.action == AlertPolicySync.PLAYBACK_STATE_ACTION) {
|
||||||
playbackReady = intent.getBooleanExtra(AlertPolicySync.EXTRA_PLAYBACK_READY, false)
|
playbackReady = intent.getBooleanExtra(AlertPolicySync.EXTRA_PLAYBACK_READY, false)
|
||||||
directAlertToken = intent.getStringExtra(AlertPolicySync.EXTRA_DIRECT_ALERT_TOKEN)
|
|
||||||
?.takeIf { playbackReady }
|
|
||||||
Log.i(TAG, "Alert playback listener ready=$playbackReady")
|
Log.i(TAG, "Alert playback listener ready=$playbackReady")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user