mirror of
https://github.com/streamyfin/streamyfin.git
synced 2026-01-15 15:48:05 +00:00
fix: BOOT_COMPLETED runtime protection
This commit is contained in:
@@ -5,42 +5,92 @@ import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.content.pm.ServiceInfo
|
||||
import android.os.Binder
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.os.SystemClock
|
||||
import android.util.Log
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.app.ServiceCompat
|
||||
|
||||
class DownloadService : Service() {
|
||||
private val TAG = "DownloadService"
|
||||
private val NOTIFICATION_ID = 1001
|
||||
private val CHANNEL_ID = "download_channel"
|
||||
|
||||
|
||||
// Time threshold to detect if we're in boot context (10 minutes after boot)
|
||||
private val BOOT_THRESHOLD_MS = 10 * 60 * 1000L
|
||||
|
||||
private val binder = DownloadServiceBinder()
|
||||
private var activeDownloadCount = 0
|
||||
private var currentDownloadTitle = "Preparing download..."
|
||||
private var currentProgress = 0
|
||||
|
||||
private var isForegroundStarted = false
|
||||
|
||||
inner class DownloadServiceBinder : Binder() {
|
||||
fun getService(): DownloadService = this@DownloadService
|
||||
}
|
||||
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
Log.d(TAG, "DownloadService created")
|
||||
createNotificationChannel()
|
||||
}
|
||||
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder {
|
||||
Log.d(TAG, "DownloadService bound")
|
||||
return binder
|
||||
}
|
||||
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||
Log.d(TAG, "DownloadService started")
|
||||
startForeground(NOTIFICATION_ID, createNotification())
|
||||
|
||||
// On Android 15+, dataSync foreground services cannot be started from BOOT_COMPLETED context
|
||||
// Check if we're likely in a boot context and skip foreground start if so
|
||||
if (Build.VERSION.SDK_INT >= 35 && isLikelyBootContext()) {
|
||||
Log.w(TAG, "Skipping foreground start - likely boot context on Android 15+")
|
||||
stopSelf()
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
startForegroundSafely()
|
||||
return START_STICKY
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if we're likely in a boot context by checking system uptime.
|
||||
* If the system has been up for less than the threshold, we might be in boot context.
|
||||
*/
|
||||
private fun isLikelyBootContext(): Boolean {
|
||||
val uptimeMs = SystemClock.elapsedRealtime()
|
||||
return uptimeMs < BOOT_THRESHOLD_MS
|
||||
}
|
||||
|
||||
/**
|
||||
* Start foreground service safely with proper service type for Android 14+
|
||||
*/
|
||||
private fun startForegroundSafely() {
|
||||
if (isForegroundStarted) return
|
||||
|
||||
try {
|
||||
if (Build.VERSION.SDK_INT >= 34) {
|
||||
ServiceCompat.startForeground(
|
||||
this,
|
||||
NOTIFICATION_ID,
|
||||
createNotification(),
|
||||
ServiceInfo.FOREGROUND_SERVICE_TYPE_DATA_SYNC
|
||||
)
|
||||
} else {
|
||||
startForeground(NOTIFICATION_ID, createNotification())
|
||||
}
|
||||
isForegroundStarted = true
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Failed to start foreground service", e)
|
||||
// If we can't start foreground, stop the service
|
||||
stopSelf()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
Log.d(TAG, "DownloadService destroyed")
|
||||
@@ -86,7 +136,7 @@ class DownloadService : Service() {
|
||||
activeDownloadCount++
|
||||
Log.d(TAG, "Download started, active count: $activeDownloadCount")
|
||||
if (activeDownloadCount == 1) {
|
||||
startForeground(NOTIFICATION_ID, createNotification())
|
||||
startForegroundSafely()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -94,7 +144,10 @@ class DownloadService : Service() {
|
||||
activeDownloadCount = maxOf(0, activeDownloadCount - 1)
|
||||
Log.d(TAG, "Download stopped, active count: $activeDownloadCount")
|
||||
if (activeDownloadCount == 0) {
|
||||
stopForeground(STOP_FOREGROUND_REMOVE)
|
||||
if (isForegroundStarted) {
|
||||
ServiceCompat.stopForeground(this, ServiceCompat.STOP_FOREGROUND_REMOVE)
|
||||
isForegroundStarted = false
|
||||
}
|
||||
stopSelf()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user