javaandroidkotlinandroid-servicealarmmanager

Foreground Service Scheduling Issue: AlarmManager Fails to start Foreground Service When App is Not in the Background


I'm encountering an issue with scheduling a foreground service using AlarmManager in my Android application. Despite setting up the code to trigger a foreground service at specific times using alarms, the alarms are unable to start foreground Service when the app is not in the background (it works fine when the app is in the foreground or in the background).

I have a requirement in my app to execute a foreground service at a specific time range of the day. To achieve this, I'm using AlarmManager to schedule the service to start and stop at the desired intervals.

and what are the different ways other than alarmManager, to schedule a foreground service.

TimeBasedScheduler Class:

class TimeBasedScheduler : BroadcastReceiver() {
    private val TAG = "TimeBasedScheduler"

    override fun onReceive(context: Context, intent: Intent) {
        val action = intent.action
        Log.d(TAG, "Received action: $action")

        if (action != null) {
            when (action) {
                "START_SERVICE" -> {
                    Log.d(TAG, "Initializing socket for START_SERVICE")
                    initializeSocket(context)
                    // Now execution is started and resumed
                    LocalPrefrenceUtils.insertDataInBoolean(context, AppConstants.BACKGROUND_WORK_ALLOWED_KEY_FOR_LOCAL_PREFERENCE, true)
                    LocalPrefrenceUtils.insertDataInBoolean(context, AppConstants.BACKGROUND_WORK_TEMPORARILY_PAUSED_KEY_FOR_LOCAL_PREFERENCE, false
                    )
                }
                "STOP_SERVICE" -> {
                    Log.d(TAG, "Stopping service for STOP_SERVICE")
                    LocalPrefrenceUtils.insertDataInBoolean(
                        context,
                        AppConstants.BACKGROUND_WORK_TEMPORARILY_PAUSED_KEY_FOR_LOCAL_PREFERENCE,
                        true
                    )
                    val stopIntent = Intent(context, SocketManagerService::class.java)
                    context.stopService(stopIntent)
                }
            }
        }
    }

    private fun initializeSocket(context: Context?) {
        Log.d(TAG, "Initializing socket")
            val intent = Intent(context, SocketManagerService::class.java)
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                Log.d(TAG, "Starting foreground service")
                context?.startForegroundService(intent)
            } else {
                Log.d(TAG, "Starting service")
                context?.startService(intent)
            }

    }
}

SettingActivity Class:

private fun setupForegroundServiceAlarms() {
        val TAG = "TaskBasedScheduler"
        // Get the start and end times from preferences
        val startTime = LocalPrefrenceUtils.getStartTime(this)
        val endTime = LocalPrefrenceUtils.getEndTime(this)

        Log.d(TAG, "Start Time: $startTime")
        Log.d(TAG, "End Time: $endTime")

        // Calculate start and end times based on step sizes and special cases for value 48
        val startHour = 7 // Starting hour (7:00 am)
        val stepSizeMinutes = 30 // Step size in minutes

        val startHourForAlarms = startHour + (stepSizeMinutes * startTime) / 60
        val endHourForAlarms = if (endTime == 48) startHour + (stepSizeMinutes * endTime) / 60 - 1 else startHour + (stepSizeMinutes * endTime) / 60

        val startMinute = (stepSizeMinutes * startTime) % 60
        val endMinute = if (endTime == 48) ((stepSizeMinutes * endTime) % 60) - 1 else ((stepSizeMinutes * endTime) % 60)

        // Set the Calendar instances for start and end times
        val startCalendar = Calendar.getInstance().apply {
            timeInMillis = System.currentTimeMillis()
            set(Calendar.HOUR_OF_DAY, startHourForAlarms)
            set(Calendar.MINUTE, startMinute)
            set(Calendar.SECOND, 0)
        }

        val endCalendar = Calendar.getInstance().apply {
            timeInMillis = System.currentTimeMillis()
            set(Calendar.HOUR_OF_DAY, endHourForAlarms)
            set(Calendar.MINUTE, endMinute)
            set(Calendar.SECOND, 0)
        }

        Log.d(TAG, "Start Calendar Time: ${startCalendar.time}")
        Log.d(TAG, "End Calendar Time: ${endCalendar.time}")

        // Cancel previously set alarms
        alarmManager.cancel(startPendingIntent)
        alarmManager.cancel(endPendingIntent)

        Log.d(TAG, "Canceled previous alarms")

        // Create intents for starting and stopping the service
        val startIntent = Intent(this, TimeBasedScheduler::class.java).apply {
            action = "START_SERVICE"
        }
        val endIntent = Intent(this, TimeBasedScheduler::class.java).apply {
            action = "STOP_SERVICE"
        }

        // Create pending intents for start and end actions
        startPendingIntent = PendingIntent.getBroadcast(this, 0, startIntent, PendingIntent.FLAG_IMMUTABLE)
        endPendingIntent = PendingIntent.getBroadcast(this, 1, endIntent, PendingIntent.FLAG_IMMUTABLE)

        Log.d(TAG, "Created pending intents")

        // Set alarms using AlarmManager for daily repetition
        alarmManager.setInexactRepeating(
            AlarmManager.RTC_WAKEUP,
            startCalendar.timeInMillis,
            AlarmManager.INTERVAL_DAY,
            startPendingIntent
        )

        alarmManager.setInexactRepeating(
            AlarmManager.RTC_WAKEUP,
            endCalendar.timeInMillis,
            AlarmManager.INTERVAL_DAY,
            endPendingIntent
        )

        Log.d(TAG, "Alarms set for start: ${startCalendar.time}, end: ${endCalendar.time}")
    }

and manifest related code:

<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="com.android.alarm.permission.SET_ALARM" />

<receiver android:name=".schedulers.TimeBasedScheduler"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="START_SERVICE" />
                <action android:name="STOP_SERVICE" />
            </intent-filter>
        </receiver>

works perfectly fine when the app is in the foreground or background:

enter image description here

Receiver when the app is in the foreground or background: enter image description here

I've checked the logs using Logcat, and it doesn't show any error messages related to alarms or the foreground service not starting.

I'm seeking guidance on why the alarms are not triggering the foreground service as expected. I'd appreciate any insights, suggestions, or potential solutions to resolve this issue.

Thank you for your help.


Solution

  • try
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        alarmManager.setExactAndAllowWhileIdle(
            AlarmManager.RTC_WAKEUP,
            startCalendar.timeInMillis,
            startPendingIntent
        )
    
        alarmManager.setExactAndAllowWhileIdle(
            AlarmManager.RTC_WAKEUP,
            endCalendar.timeInMillis,
            endPendingIntent
        )
    } else {
        // For versions below M, use setExact
        alarmManager.setExact(
            AlarmManager.RTC_WAKEUP,
            startCalendar.timeInMillis,
            startPendingIntent
        )
    
        alarmManager.setExact(
            AlarmManager.RTC_WAKEUP,
            endCalendar.timeInMillis,
            endPendingIntent
        )
    }
    

    and use alarm manager with workmanager for intervals lower than 15 min for greater than 15 min use just workmanager and set your interval and start foreground service