ntfy-android/app/src/main/java/io/heckel/ntfy/up/BroadcastReceiver.kt

148 lines
6.8 KiB
Kotlin
Raw Normal View History

2021-12-30 08:33:17 +13:00
package io.heckel.ntfy.up
import android.content.Context
import android.content.Intent
import io.heckel.ntfy.R
import io.heckel.ntfy.app.Application
2022-05-06 08:56:06 +12:00
import io.heckel.ntfy.db.Repository
2022-01-19 08:28:48 +13:00
import io.heckel.ntfy.db.Subscription
import io.heckel.ntfy.service.SubscriberServiceManager
2022-06-20 06:45:01 +12:00
import io.heckel.ntfy.util.*
2021-12-30 08:33:17 +13:00
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
2022-05-04 10:01:00 +12:00
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
2021-12-30 08:33:17 +13:00
import java.util.*
2022-01-01 03:30:49 +13:00
/**
* This is the UnifiedPush broadcast receiver to handle the distributor actions REGISTER and UNREGISTER.
* See https://unifiedpush.org/spec/android/ for details.
*/
2021-12-30 08:33:17 +13:00
class BroadcastReceiver : android.content.BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
2021-12-31 02:23:47 +13:00
if (context == null || intent == null) {
return
}
2022-01-17 18:19:05 +13:00
Log.init(context) // Init in all entrypoints
2021-12-31 02:23:47 +13:00
when (intent.action) {
ACTION_REGISTER -> register(context, intent)
ACTION_UNREGISTER -> unregister(context, intent)
}
}
private fun register(context: Context, intent: Intent) {
val appId = intent.getStringExtra(EXTRA_APPLICATION) ?: return
val connectorToken = intent.getStringExtra(EXTRA_TOKEN) ?: return
val app = context.applicationContext as Application
val repository = app.repository
val distributor = Distributor(app)
Log.d(TAG, "REGISTER received for app $appId (connectorToken=$connectorToken)")
2023-03-04 08:15:38 +13:00
if (!repository.getUnifiedPushEnabled()) {
2023-03-04 03:57:36 +13:00
Log.w(TAG, "Refusing registration because 'EnableUP' is disabled")
2023-03-04 08:15:38 +13:00
distributor.sendRegistrationFailed(appId, connectorToken, "UnifiedPush is disabled in ntfy")
2023-03-04 03:57:36 +13:00
return
}
if (appId.isBlank()) {
Log.w(TAG, "Refusing registration: Empty application")
2022-03-23 13:30:41 +13:00
distributor.sendRegistrationFailed(appId, connectorToken, "Empty application string")
2021-12-31 02:23:47 +13:00
return
}
GlobalScope.launch(Dispatchers.IO) {
2022-05-04 10:01:00 +12:00
// We're doing all of this inside a critical section, because of possible races.
// See https://github.com/binwiederhier/ntfy/issues/230 for details.
mutex.withLock {
val existingSubscription = repository.getSubscriptionByConnectorToken(connectorToken)
if (existingSubscription != null) {
if (existingSubscription.upAppId == appId) {
val endpoint = topicUrlUp(existingSubscription.baseUrl, existingSubscription.topic)
Log.d(TAG, "Subscription with connectorToken $connectorToken exists. Sending endpoint $endpoint.")
distributor.sendEndpoint(appId, connectorToken, endpoint)
} else {
Log.d(TAG, "Subscription with connectorToken $connectorToken exists for a different app. Refusing registration.")
distributor.sendRegistrationFailed(appId, connectorToken, "Connector token already exists")
}
return@launch
2021-12-30 08:33:17 +13:00
}
2022-05-04 10:01:00 +12:00
// Add subscription
val baseUrl = repository.getDefaultBaseUrl() ?: context.getString(R.string.app_base_url)
val topic = UP_PREFIX + randomString(TOPIC_RANDOM_ID_LENGTH)
val endpoint = topicUrlUp(baseUrl, topic)
val subscription = Subscription(
2022-06-20 06:45:01 +12:00
id = randomSubscriptionId(),
2022-05-04 10:01:00 +12:00
baseUrl = baseUrl,
topic = topic,
instant = true, // No Firebase, always instant!
dedicatedChannels = false,
2022-05-04 10:01:00 +12:00
mutedUntil = 0,
2022-05-06 08:56:06 +12:00
minPriority = Repository.MIN_PRIORITY_USE_GLOBAL,
autoDelete = Repository.AUTO_DELETE_USE_GLOBAL,
insistent = Repository.INSISTENT_MAX_PRIORITY_USE_GLOBAL,
2022-06-19 13:01:05 +12:00
lastNotificationId = null,
2022-05-07 13:03:15 +12:00
icon = null,
2022-05-04 10:01:00 +12:00
upAppId = appId,
upConnectorToken = connectorToken,
displayName = null,
2022-05-04 10:01:00 +12:00
totalCount = 0,
newCount = 0,
lastActive = Date().time/1000
)
Log.d(TAG, "Adding subscription with for app $appId (connectorToken $connectorToken): $subscription")
try {
// Note, this may fail due to a SQL constraint exception, see https://github.com/binwiederhier/ntfy/issues/185
repository.addSubscription(subscription)
distributor.sendEndpoint(appId, connectorToken, endpoint)
2021-12-31 02:23:47 +13:00
2022-05-04 10:01:00 +12:00
// Refresh (and maybe start) foreground service
SubscriberServiceManager.refresh(app)
} catch (e: Exception) {
Log.w(TAG, "Failed to add subscription", e)
distributor.sendRegistrationFailed(appId, connectorToken, e.message)
}
2022-05-06 13:06:21 +12:00
// Add to log scrubber
Log.addScrubTerm(shortUrl(baseUrl), Log.TermType.Domain)
Log.addScrubTerm(topic)
2022-03-23 13:30:41 +13:00
}
2021-12-31 02:23:47 +13:00
}
}
private fun unregister(context: Context, intent: Intent) {
val connectorToken = intent.getStringExtra(EXTRA_TOKEN) ?: return
val app = context.applicationContext as Application
val repository = app.repository
val distributor = Distributor(app)
Log.d(TAG, "UNREGISTER received (connectorToken=$connectorToken)")
GlobalScope.launch(Dispatchers.IO) {
2022-05-04 10:01:00 +12:00
// We're doing all of this inside a critical section, because of possible races.
// See https://github.com/binwiederhier/ntfy/issues/230 for details.
mutex.withLock {
val existingSubscription = repository.getSubscriptionByConnectorToken(connectorToken)
if (existingSubscription == null) {
Log.d(TAG, "Subscription with connectorToken $connectorToken does not exist. Ignoring.")
return@launch
}
2021-12-31 02:23:47 +13:00
2022-05-04 10:01:00 +12:00
// Remove subscription
Log.d(TAG, "Removing subscription ${existingSubscription.id} with connectorToken $connectorToken")
repository.removeSubscription(existingSubscription.id)
existingSubscription.upAppId?.let { appId -> distributor.sendUnregistered(appId, connectorToken) }
2021-12-31 02:23:47 +13:00
2022-05-04 10:01:00 +12:00
// Refresh (and maybe stop) foreground service
SubscriberServiceManager.refresh(context)
}
2021-12-30 08:33:17 +13:00
}
}
companion object {
private const val TAG = "NtfyUpBroadcastRecv"
2021-12-31 02:23:47 +13:00
private const val UP_PREFIX = "up"
2021-12-31 13:34:25 +13:00
private const val TOPIC_RANDOM_ID_LENGTH = 12
2022-05-04 10:01:00 +12:00
val mutex = Mutex() // https://github.com/binwiederhier/ntfy/issues/230
2021-12-30 08:33:17 +13:00
}
}