0

1) I'am starting Service or JobService via startService, startForegroundService for android Oreo and ContextCompat.startForegroundService

2) in the onCreate method of the service I do startForeground (1, notification), and in onDestroy I do stopForeground (false)

In the onCreate service method, the BroadcastReceiver is launched, to receive TelephonyManager.EXTRA_STATE events like - OFFHOOK, RINGING, IDLE

Here is the code for registrations receiver

applicationContext.registerReceiver (
    broadcastReceiver, 
    IntentFilter (TelephonyManager.ACTION_PHONE_STATE_CHANGED),
    Manifest.permission.CALL_PHONE, null)

Immediately after starting the service, I call

    val intent = Intent (Intent.ACTION_DIAL)
    intent.data = Uri.parse ("tel: +77787020453")
    startActivity (intent)

And I start to call

I get in the service logs

18: 38: 17.008 I / main >>>>: Unstoppable: onCreate
18: 38: 17.013 4371-4371 / me.myapp I / main >>>>: UnstoppableJob: onStartCommand
18: 38: 17.231 4371-5110 / me.myapp I / main >>>>: Unstoppable - 1
18: 38: 18.730 4371-5110 / me.myapp I / main >>>>: Unstoppable - 2
18: 38: 19.082 4371-4371 / me.myapp I / Unstoppable: blockCallReciever: onRecieve:
18: 38: 19.082 4371-4371 / me.myapp I / Unstoppable: android.intent.action.PHONE_STATE = OFFHOOK
18: 38: 21.715 5169-5169 /? I / main >>>>: Unstoppable: onCreate ⁉️
18: 38: 21.741 5169-5169 /? I / main >>>>: UnstoppableJob: onStartCommand
18: 38: 21.947 5169-5231 /? I / main >>>>: Unstoppable - 1 ⁉️
18: 38: 24.114 5242-5242 / me.myapp I / main >>>>: Unstoppable: onCreate
18: 38: 24.145 5242-5242 / me.myapp I / main >>>>: UnstoppableJob: onStartCommand

Unstoppable - 1, Unstoppable - 2 - this is a timer I started to see service is running or not in real time.

I can not understand why the service calls onCreate again without calling onDestroy, who can help me solve this and make the service work stably when making a call?

Service code

package me.test.ui.test.job

import android.Manifest
import android.annotation.TargetApi
import android.app.Service
import android.content.*
import android.os.Build
import android.os.IBinder
import android.telephony.PhoneNumberUtils
import android.telephony.TelephonyManager
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import io.reactivex.Completable
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.disposables.Disposable
import me.testapp.TestApp
import me.testapp.SyncedTime
import me.testapp.entity.CallLogItemPojo
import me.testapp.entity.CallLogPhoneStatePojo
import me.testapp.entity.PhoneStateString
import me.testapp.interactors.ContactsInteractor
import me.testapp.interactors.LoginScreenInteractor
import me.testapp.interactors.TaskInteractor
import me.testapp.managers.BlockedTelephonyManager
import me.testapp.managers.SettingsManager
import me.testapp.ui.test.TestActivity
import me.testapp.ui.test.TestActivity.Companion.log
import java.lang.Exception
import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject

@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
class UnstoppableJob : Service() {
    override fun onBind(p0: Intent?): IBinder? {
        return null
    }

    private lateinit var timer: Timer

    private var TAG = "Unstoppable"
    private var timerTask = object : TimerTask() {
        override fun run() {
            var lifetimeOfTask = preferences.getLong(SettingsManager.TASK_LIFETIME_MILLIES, 0)

            if (lifetimeOfTask != 0.toLong() && lifetimeOfTask < System.currentTimeMillis()) {
                stopSelf()
                log(message = "Unstoppable: stopSelf")
            }
            log(message = "Unstoppable - $count")
            count++

        }

    }

    @Inject
    lateinit var loginInteractor: LoginScreenInteractor

    @Inject
    lateinit var interactor: TaskInteractor

    private var count = 1
    @Inject
    lateinit var preferences: SharedPreferences

    private var dispose: Disposable? = null
    private val disposable = CompositeDisposable()

    @Inject
    lateinit var contactsInteractor: ContactsInteractor

    private val listPhoneState = LinkedList<PhoneStateString>()
    private var phoneNumber: String? = null
    private var id: Int? = null
    private var token: String? = null
    private var type: String? = null
    private var callTypeForReceiver: String? = null

    private val phoneList = mutableListOf<String>()
    private val idListOfTask = mutableSetOf<Int>()
    private val timeMap = mutableMapOf<Int, Disposable>()


    private val blockCallReciever = object: BroadcastReceiver() {
        override fun onReceive(context: Context?, intent: Intent?) {
            Log.i(TAG, "blockCallReciever: onRecieve:")
            var lifetimeOfTask = preferences.getLong(SettingsManager.TASK_LIFETIME_MILLIES, 0)

            Log.i(
                TAG, intent?.action + " " + intent?.getStringExtra(
                TelephonyManager.EXTRA_STATE) + " " + intent?.getStringExtra(
                TelephonyManager.EXTRA_STATE_RINGING))

            if (intent == null || intent.action != "android.intent.action.PHONE_STATE") {
                return
            }

            val callNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER)

            //val number = intent?.getStringExtra(TelephonyManager.EXTRA_)
            val state = intent.getStringExtra(TelephonyManager.EXTRA_STATE)

            if (callNumber != null && callNumber.isNotEmpty()) {
                var it = PhoneStateString(callNumber, state, Date())
                listPhoneState.add(it)

                // Send CDR
                if (it.state == "IDLE" &&
                    lifetimeOfTask >= System.currentTimeMillis()) {
                    Log.i(TAG, "TestApplistener: IDLE send cdr")
                    sendCDR()
                }


                Log.i(TAG, "RingApplistener: ${it.state == "RINGING"} && ${preferences.getBoolean(SettingsManager.AUTO_RESET_CALLS, false)} && ${lifetimeOfTask >= System.currentTimeMillis()}")
                if (it.state == "RINGING" &&
                    preferences.getBoolean(SettingsManager.AUTO_RESET_CALLS, false) &&
                    lifetimeOfTask >= System.currentTimeMillis()) {
                    var isCallEnded = BlockedTelephonyManager.findHandler(applicationContext!!).endCall()
                    Log.i(TAG, "RingApplistener: auto reset call: isCallEnded=$isCallEnded")

                    // TODO: check it
                    if (type != null && type == "incoming" && callTypeForReceiver != null && callTypeForReceiver == "decline_call") {
                        val isCallEnded = BlockedTelephonyManager.findHandler(applicationContext!!).endCall()
                        Log.i(TAG, "RingApplistener: auto reset call: isCallEnded=$isCallEnded")
                    }


                }
            }

            Log.d(TAG, "blockCallReciever: onRecieve - number $callNumber, state: $state")
        }

    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    private fun sendCDR() {
        val bufList = mutableListOf<PhoneStateString>()
        do {
            bufList.add(listPhoneState.pop())
        } while (bufList.last().state != "IDLE")

        if (bufList.isNotEmpty()) {
            dispose = contactsInteractor.loadLastCallLog(phoneNumber).flatMapCompletable {
                val list = if (!phoneNumber.isNullOrEmpty())
                    it.filter { PhoneNumberUtils.normalizeNumber(it.number) == PhoneNumberUtils.normalizeNumber(
                        phoneNumber
                    )
                    }
                else
                    it
                list.map { it.callLogPhoneState = CallLogPhoneStatePojo(bufList) }
                Log.i(TAG, "TestAppListenerService: $token $id sendCdr ${list.toString()}")

                if (token == null || id == null) {
                    Completable.error(Throwable("null"))
                } else {
                    interactor.sendCdr(token!!, id!!, CallLogItemPojo(list))
                }

            }.subscribe({
                Log.i(TAG, "TaskPresenter: " + "successful")
            }, {
                Log.i(TAG, "TaskPresenter: " + "unsuccessful ${it.message}")
            })
        }
    }

    override fun onCreate() {
        super.onCreate()
        log(message = "Unstoppable: onCreate")

        timer = Timer()
        timer.schedule(timerTask, 222, 1500)

        TestApp.component.inject(this)

        applicationContext.registerReceiver(blockCallReciever, IntentFilter(TelephonyManager.ACTION_PHONE_STATE_CHANGED),
            Manifest.permission.CALL_PHONE, null)


        val notification = NotificationCompat.Builder(this, TestApp.CHANNEL_ID)
            .setContentTitle("TestApp")
            .setContentText("Foreground service")
            .build()

        startForeground(1, notification)
    }


    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        TestActivity.log(message = "UnstoppableJob: onStartCommand")
        if (intent?.extras != null) {
            intent.extras!!.also {
                val buf = it
                buf.keySet().forEach {
                    Log.i(TAG ,"Command: $it = ${buf.get(it)}")
                }
                if (it.containsKey("exit")) {
                    disposable.dispose()
                    dispose?.dispose()
                    this@UnstoppableJob.stopSelf()
                    stopSelf()
                }

                if (it.containsKey("phoneNumber")) {
                    phoneNumber = it.getString("phoneNumber")
                    if (phoneNumber != null) {
                        phoneList.add(phoneNumber!!)
                    }
                }

                if (it.containsKey("id"))  id = it.getInt("id")
                if (it.containsKey("token")) token = it.getString("token")
                if (it.containsKey("type")) type = it.getString("type")
                if (it.containsKey("callTypeForReceiver")) callTypeForReceiver = it.getString("callTypeForReceiver")

                if (it.getBoolean("delete", false)) {
                    removeTask(it.getInt("id"))
                }
                if (it.containsKey("ringing") && it.containsKey("id")) {
                    SyncedTime.requestTimeFromTestApp()
                    if (!idListOfTask.contains(it.getInt("id"))) {
                        timeMap[it.getInt("id")] = Completable.complete().delay(60, TimeUnit.SECONDS).subscribe {
                            removeTask(it.getInt("id"))
                        }
                    }

                    idListOfTask.add(it.getInt("id"))
                }
            }
        }

        return super.onStartCommand(intent, flags, startId)
    }

    private fun removeTask(id: Int) {
        Log.i(TAG,"remove tasks by $id")
        idListOfTask.remove(id)
        if (idListOfTask.size == 0) {
            dispose?.dispose()
            disposable.dispose()
            onClear()
        }
    }

    fun onClear() {
        disposable.dispose()
        timeMap.values.forEach {
            it.dispose()
        }
        this@UnstoppableJob.stopSelf()
    }


    override fun bindService(service: Intent?, conn: ServiceConnection, flags: Int): Boolean {
        return super.bindService(service, conn, flags)
    }

    override fun unbindService(conn: ServiceConnection) {
        super.unbindService(conn)
    }


    override fun onDestroy() {
        super.onDestroy()
        TestActivity.log(message = "UnstoppableJob: onDestroy")

        try {
            timer.cancel()
            timerTask.cancel()
            count = 1
        } catch (e: Exception) {

            log(message = "Unstoppable: onDestroy - error cancel timer: ${e.message}")
        }


        dispose?.dispose()
        disposable.dispose()

        applicationContext.unregisterReceiver(blockCallReciever)

        var restoreServiceIntent = Intent("con.raone.start.serivce")
        restoreServiceIntent.putExtra("startService", true)
        sendBroadcast(restoreServiceIntent)

        stopForeground(false)
    }

}

1 Answers1

0

I solved the problem by registering Broadcastreceiver in the manifest file.

    <receiver
        android:name=".broadcast.ReadPhoneStateReceiver"
        android:enabled="true"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.PHONE_STATE" />
        </intent-filter>
    </receiver>