I have an aidl interface in the client and server applications with which I connect from the Client application to the AIDLApiService service of my Server application. I call 2 methods on the interface: start (starting the VPN service) and getInfo (to get some data about it).
// AIDLApiService.kt
override fun onBind(intent: Intent): IBinder {
override fun getInfo(): VPNInfo {
Timber.d("[AIDL:getInfo()] CO: $VpnService")
return VPNInfo(
isVpnStarted = VpnService.isStarted(),
...
)
}
override fun start(): Error? = try {
Timber.d("[AIDL:start()] CO: $VpnService")
if (!VpnService.isVpnPrepared(applicationContext)) {
Error(ERROR_VPN_PERMISSION_NOT_GRANTED, null)
} else {
VpnService.start(applicationContext)
null
}
} catch (e: Exception) {
Timber.e(e.message)
Error(e)
}
}
where in the Timber logs I track which companion VPNService object I call these methods on.
When calling the start()
method, I initiate the creation and launch of the service and change the companion object isServiceStarted = true
flag, after which in the Client application I check by getting getInfo()
containing the value of the VPNService.isStarted()
method, but I always get false
.
class VPNService : VpnService() {
...
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
Timber.d("onStartCommand")
when (intent.action) {
ACTION_START_VPN -> try {
...
// !!! Here the companion object is different than on which I called start() and getInfo()
isServiceStarted = true
Timber.d("[onStartCommand] CO: $VpnService, VPN: $this")
} catch (e: Exception) {
Timber.e(e.message)
exception = e
...
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
stopForeground(STOP_FOREGROUND_REMOVE)
} else {
stopForeground(true)
}
stopSelf()
}
ACTION_STOP_VPN -> try {
...
}
}
return START_STICKY
}
companion object {
@Volatile
private var isServiceStarted = false
@Volatile
private var exception: Exception? = null
fun isStarted(): Boolean = isServiceStarted
fun start(context: Context, isWait: Boolean = false) {
Timber.e("[START] CO: $this")
startSync(context = context, wait = isWait)
}
@Synchronized
private fun startSync(context: Context, wait: Boolean) {
startNotSync(context = context, wait = wait)
}
private fun startNotSync(context: Context, wait: Boolean) {
Timber.d("starting")
exception = null
if (isServiceStarted) return
val intent = Intent(context, VpnService::class.java)
intent.action = ACTION_START_VPN
startService(context = context, intent = intent)
if (wait) {
while (true) {
Thread.sleep(SERVICE_STATE_CHECKING_INTERVAL_MILLIS)
if (isServiceStarted) return
val e = exception
if (e != null) {
throw e
}
}
}
}
private fun startService(context: Context, intent: Intent) {
Timber.d("start service: intent.action: ${intent.action}")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent)
} else {
context.startService(intent)
}
}
...
}
}
It turns out that when calling the start()
and getInfo()
methods, according to the logs, I am accessing the companion object (CO) VpnService$Companion@b6fa69a
, but in the onStartCommand
method, when the value of isServiceStarted
should be set to true
, the log shows that CO: VpnService$Companion@1785aa6
, i.e. the flag changes for another object. Why does this happen and how to fix it?