Kugge
Kugge

Reputation: 38

onServiceConnected not called but context#bindService return true

I am experiencing an issue where the onServiceConnected callback is not invoked in my client code, despite the service's onCreate and onBind methods being called successfully and return a binder. The service is started, onBind return true, but onServiceConnected is not called. This issue occurs when attempting to bind to a custom service from a client within a different module.

I've written this PrinterManagerClient, everything works fine except the "onServiceConnected" that don't work.

internal class PrinterManagerClient(private val context: Context): AutoCloseable {
    private val timeout = 30L
    private var service: IPrinterManager? = null
    private var isBound = false
    private val serviceBoundLatch = CountDownLatch(1)

    private val connection = object : ServiceConnection {
        override fun onServiceConnected(className: ComponentName, service: IBinder) {
            logger.i("PrinterManagerService Connected")
            [email protected] = IPrinterManager.Stub.asInterface(service)
            isBound = true
            serviceBoundLatch.countDown()
        }

        override fun onServiceDisconnected(className: ComponentName) {
            logger.i("PrinterManagerService Disconnected")
            [email protected] = null
            isBound = false
        }
    }

    init {
        logger.i("Binding service")
        Intent().apply {
            component = ComponentName(
                "com.ingenico.ingp.printer.service",
                "com.ingenico.ingp.printer.service.PrinterManagerService"
            )
            val resolveInfo = context.packageManager.resolveService(this, PackageManager.MATCH_DEFAULT_ONLY)
            if (resolveInfo == null) {
                logger.e("PrinterManagerService not found! Is it installed?")
            } else {
                logger.i("PrinterManagerService found.")
                val isServiceBound = context.bindService(this, connection, Context.BIND_AUTO_CREATE)
                if (!isServiceBound) {
                    logger.e("Service binding failed")
                }
            }
        }
    }

    private fun ensureServiceBound(): Boolean {
        return isBound || awaitServiceBound()
    }

    private fun awaitServiceBound(): Boolean {
        return try {
            serviceBoundLatch.await(timeout, TimeUnit.SECONDS)
        } catch (e: InterruptedException) {
            Thread.currentThread().interrupt()
            false
        }
    }

    private fun <T> executeServiceOperation(operation: () -> T): T {
        if (!ensureServiceBound()) {
            //throw IllegalStateException("Service not bound and timed out") // Disabled for testing purposes
        }
        return operation()
    }

    fun getPrinter(printerName: String): IPrinter? = executeServiceOperation { 
        service!!.getPrinter(printerName)
    }

    fun getRegisteredPrinters(): List<ParcelablePrinterInstanceInfo> = executeServiceOperation { 
        service!!.getRegisteredPrinters()
    }

    fun getSupportedPrinters(): List<ParcelablePrinterDriverInfo> = executeServiceOperation { 
        service!!.getSupportedPrinters() 
    }

    fun getDefaultPrinterName(): String = executeServiceOperation { 
        service!!.defaultPrinterName 
    }

    fun setDefaultPrinter(
        printerName: String
    ): Boolean = executeServiceOperation { 
        service!!.setDefaultPrinter(printerName)
    }

    override fun close() {
        if (isBound) {
            context.unbindService(connection)
            isBound = false
        }
    }
}

And here is the service:

class PrinterManagerService : Service() {
    private lateinit var manager: AndroidPrinterManager

    private val binder = object : IPrinterManager.Stub() {
        override fun getPrinter(printerName: String): IPrinter? {
            return manager.getPrinter(printerName)?.toAidl()
        }

        override fun getDefaultPrinterName(): String =
            manager.defaultPrinter

        override fun getRegisteredPrinters(): MutableList<ParcelablePrinterInstanceInfo> {
            return manager.registeredPrinters.map { it.toAidl() }.toMutableList()
        }

        override fun setDefaultPrinter(printerName: String): Boolean {
            return try {
                manager.defaultPrinter = printerName
                true
            } catch (e: IllegalArgumentException) {
                false
            }
        }

        override fun getSupportedPrinters(): MutableList<ParcelablePrinterDriverInfo> {
            return manager.supportedPrinters.map { it.toAidl() }.toMutableList()
        }
    }

    override fun onBind(intent: Intent): IBinder {
        logger.i("onBind")
        return binder
    }

    override fun onCreate() {
        logger.i("onCreate")
        super.onCreate()
        instance = this
        serviceScope = CoroutineScope(SupervisorJob() + Dispatchers.Main)
        manager = AndroidPrinterManager(this)
    }

    override fun onDestroy() {
        logger.i("onDestroy")
        super.onDestroy()
        manager.close()
        serviceScope.cancel() // Cancel all coroutines
    }
}

When using context.bindService, the service's onCreate and onBind execute properly, and a binder is returned, yet the onServiceConnected callback in my client isn't triggered. I've already made the suggested manifest modifications from similar Stack Overflow discussions, confirming the service setup is correct. The main issue lies in the non-execution of the callback.

Returning a simple Binder() doesn't work. I'm using the applicationContext here.

Upvotes: 0

Views: 69

Answers (0)

Related Questions