Foreground service getting killed from Oreo

15,373

Solution 1

I've analyzed the issue on OnePlus, as I'm in the same situation as you. As I see currently, there is no solution. OnePlus clearly follows a bad practice.

As they haven't released the source code which kills processes in this manner, I downloaded a OnePlus ROM (I choose OnePlus 5T 5.1.5), extract it, find the .class which does this (OnePlusHighPowerDetector.class in services.vdex), decompile it, and tried to find out what's going on.

You can find a version of this class here (it is not by me, and maybe it is not the same version that I've used): https://github.com/joshuous/oneplus_blobs_decompiled/blob/master/com/android/server/am/OnePlusHighPowerDetector.java

Unfortunately, the most important functions aren't decompiled successfully. But we can analyze the bytecode anyway. Here's what I've found:

  • OnePlus kills background processes (it seems that it doesn't matter whether it has a foreground service or not) without mercy. Almost all of them. It doesn't really matter, how little CPU resources are used. My app uses less than 1%, and it gets killed.
  • If the app is pinned in the recent app list (determined by a list of com_oneplus_systemui_recent_task_lockd_list), it doesn't get killed. So, the user can save the app by pinning it. But it is inconvenient for them.
  • Oneplus provides a list of processes, which they don't kill. This list is in oneplus-framework-res.apk/res/values/array.xml, with the key string-array name="backgroundprocess_detection_app_whitelist". This list mostly contains map and fitness applications1.
  • Maybe there are other factors which can save a process being killed, I haven't analyzed the issue further. If you have the time, check out OnePlusHighPowerDetector.java.

Here are the steps needed to decompile a .vdex file:

  • use Vdex Extractor to create a .dex from .vdex (maybe you'll need to use the --ignore-crc-error option)
  • use JADX to decompile a .dex (this is currently the best decompiler in my opinion), or use dex2jar to create a .jar from .dex, and use any decompiler to decompile the .jar

1Rant: this shows how bad the current situation is. OnePlus, is this really the solution? You've chosen 10-15 applications, which can run in the background, and you don't care about all the other ones? How can one create a new fitness/map/music player application, which works safely on your device?

Solution 2

I spent several hours solving this issue. Xiaomi phone, Oreo. I have solution, 2 steps:

  1. Xiaomi needs additional permission: Settings->Permissions->Automatic startup. There is a list of applications that are allowed to be started automatically. BOOT_COMPLETED and START_STICKY is NOT enough. Facebook, Outlook, Skype + my app is listed here. I do not know how to ask user programatically to grant this permission.

  2. Service gets recreated but NOT restarted, even if you return START_STICKY. After you kill the app (swipe out from running apps), onCreate is called again but NOT onStartCommand. Try Log.d() to see it. I moved all the logic into onCreate EXCEPT creating the notification, calling startForeground() and returning START_STICKY. This has to remain in onStartCommand().

Batery optimization must be disabled for the application too, but it was required by previous version of Android already.

Now my service keeps running or is recreated with 100% success within few seconds.

Solution 3

I fixed this on my Samsung Galaxy S20+ 5G device by adding the foregroundServiceType attribute to my service in the manifest:

<service android:name=".RunningService"
        android:foregroundServiceType="dataSync"/>

Now my service stays alive.

Share:
15,373

Related videos on Youtube

Sagar Trehan
Author by

Sagar Trehan

Updated on July 24, 2022

Comments

  • Sagar Trehan
    Sagar Trehan almost 2 years

    I have written a foreground service which is working properly for all OS version lower than Oreo. From Oreo application process is getting killed after 5 minutes of closing and removing the application from recents.

    As per android developer documentation for background execution limitations OS should not kill application for which a foreground service is running and notification is shown in notification window.

    As per guidelines on developer documentation. I followed below steps to start foreground-service.

    1. Foreground service is started with startForegroundService() method
    2. Within 5 sec after starting service a notification is shown for the service using startForeground()
    3. Return START_STICKY from onStartCommand() of service

    I am facing this issue on following phones:

    1. OnePlus 5T
    2. Vivo
    3. Oppo
    4. Mi

    What I tried to prevent foreground-service from being destroyed?

    1. Disable the battery optimization for the application by showing system dialog to user to disable doze mode.

    What I tried to restart the foreground-service?

    1. Used AlarmManager to restart the service from onTaskRemoved(). Please check this link for details.

    As per my understanding these manufacturers have customized the AOSP and are not respecting the OS guidelines for allowing foreground service to run. May be these manufacturers have done this because of giving long battery life time to users.

    Foreground service class

        class DataCaptureService : Service() {
    
            private var isServiceStarted = false
    
            override fun onBind(intent: Intent?): IBinder? {
                return null
            }
    
    override fun onCreate() {
            super.onCreate()
            wakeLock = (getSystemService(Context.POWER_SERVICE) as PowerManager).run {
                        newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "WakelockTag123").apply {
                            acquire()
                        }
                    }
        }
    
    
            override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
                val serviceAction = intent?.action
                LogUtils.logD("OnStartCommand(). Action=$serviceAction")
                if (Constants.INTENT.ACTION_STOP_SERVICE == serviceAction) {
                    LogUtils.logD("Stopping data capture service")
                    stopForeground(true)
                    stopSelf()
                } else if (Constants.INTENT.ACTION_START_SERVICE == serviceAction && !isServiceStarted) {
                    LogUtils.logD("Starting data capture service")
                    isServiceStarted = true
                    // Here showing notification using a utility method (startForeground(id, notification))
                    createNotification(this)
                    // Doing some stuff here
                    ----------------------
                    //
                }
                return START_STICKY
            }
    
            override fun onDestroy() {
                super.onDestroy()
                if (isServiceStarted) {
                    LogUtils.logD("onDestroy of DataCaptureService method is invoked")
                    // Doing some stuff here
                    ----------------------
                    //
                    isServiceStarted = false
                    if (wakeLock.isHeld) {
                        wakeLock.release()
                    }
                }
            }
    
            override fun onTaskRemoved(rootIntent: Intent?) {
                LogUtils.logD("onTaskRemoved of DataCaptureService method is invoked")
                ensureServiceStaysRunning()
                super.onTaskRemoved(rootIntent)
            }
    
            private fun ensureServiceStaysRunning() {
                val restartAlarmInterval = 60 * 1000
                val resetAlarmTimer = 30 * 1000L
                // From this broadcast I am restarting the service
                val restartIntent = Intent(this, ServiceRestartBroadcast::class.java)
                restartIntent.action = "RestartedViaAlarm"
                restartIntent.flags = Intent.FLAG_RECEIVER_FOREGROUND
                val alarmMgr = getSystemService(Context.ALARM_SERVICE) as AlarmManager
                val restartServiceHandler = @SuppressLint("HandlerLeak")
                object : Handler() {
                    override fun handleMessage(msg: Message) {
                        val pendingIntent = PendingIntent.getBroadcast(applicationContext, 87, restartIntent, PendingIntent.FLAG_CANCEL_CURRENT)
                        val timer = System.currentTimeMillis() + restartAlarmInterval
                        val sdkInt = Build.VERSION.SDK_INT
                        if (sdkInt < Build.VERSION_CODES.KITKAT)
                            alarmMgr.set(AlarmManager.RTC_WAKEUP, timer, pendingIntent)
                        else if (Build.VERSION_CODES.KITKAT <= sdkInt && sdkInt < Build.VERSION_CODES.M)
                            alarmMgr.setExact(AlarmManager.RTC_WAKEUP, timer, pendingIntent)
                        else if (sdkInt >= Build.VERSION_CODES.M) {
                            alarmMgr.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, timer, pendingIntent)
                        }
                        sendEmptyMessageDelayed(0, resetAlarmTimer)
                        stopSelf()
                    }
                }
                restartServiceHandler.sendEmptyMessageDelayed(0, 0)
            }
    
        }
    

    Please share your suggestion if you have faced similar type of issue and managed to find a solution for this.

    • Sahil
      Sahil over 5 years
      are you creating notification channel for you notifications ?
    • Sagar Trehan
      Sagar Trehan over 5 years
      @Sahil Yes I am creating NotificationChannel for oreo
    • FreakyAli
      FreakyAli about 5 years
      @SagarTrehan were you able to solve this issue?
    • Boris Salimov
      Boris Salimov over 4 years
      @SagarTrehan Did you found any solution?
    • SAOUD SALIH HASSAN
      SAOUD SALIH HASSAN about 4 years
      did you get any solution?
  • Sagar Trehan
    Sagar Trehan over 5 years
    Thanks for your help. I am calling startForeground() as soon as service starts.
  • Viktor Vasyliev
    Viktor Vasyliev over 5 years
    Added more suggestions about your problem.
  • Sagar Trehan
    Sagar Trehan over 5 years
    Posted the service class. Yes I am using WakeLock as well. Please check. Thanks for your time.
  • Viktor Vasyliev
    Viktor Vasyliev over 5 years
    Try call startForeground(id, notification) in onCreate() method before you acquire wake lock. And if you start service with alarm broadcast, acquire wakelock with timeout (some seconds) in BroadcastReceiver's onReceive method, to give service time to start, and then start your service
  • Viktor Vasyliev
    Viktor Vasyliev over 5 years
    By the way, did you check your wake locks in the Terminal?
  • Sagar Trehan
    Sagar Trehan over 5 years
    Regarding "WakeLock on BroadcastReceiver": I am not receiving the broadcast once process is killed by OS. I will check the wakelock using the command you shared and will let you know.
  • Ranjith KP
    Ranjith KP over 5 years
    I want to do similar kind of investigation. what tool your using to decompile .vdex to java ?Please help.
  • Thom
    Thom over 5 years
    @ViktorVasyliev In my case i have to show notification when the service terminated like "your job was terminated , kindly white list the app from battery settings "
  • Azay Gupta
    Azay Gupta over 4 years
    Is step 1 possible programmatically?. How to detect Automatic startup enabled or disabled?.
  • SAOUD SALIH HASSAN
    SAOUD SALIH HASSAN about 4 years
    did you get any solution?
  • Seth
    Seth over 2 years
    Do not use a wake lock. The wake lock will NOT prevent the service from being killed. Wake lock will keep the CPU awake and prevent it from going into a standby state. If anything, the system may detect an extended wake lock on a background service and kill it.