Skip to content

Commit

Permalink
#1376 fix: allow multiple relay service callbacks to be registered fr…
Browse files Browse the repository at this point in the history
…om the same client.

This fixes issues with Key Mapper accessibility service and the built-in IME service both receiving key events.
  • Loading branch information
sds100 committed Dec 27, 2024
1 parent 08501e0 commit 297f9de
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,12 @@ interface IKeyEventRelayService {
void registerCallback(IKeyEventRelayServiceCallback client);

/**
* Unregister a callback to receive key events from this relay service. The service
* checks the process uid of the caller to this method and only permits certain applications
* from connecting.
* Unregister all the callbacks associated with the calling package from this relay service.
*/
void unregisterAllCallbacks();

/**
* Unregister a callback to receive key events from this relay service.
*/
void unregisterCallback();
void unregisterCallback(IKeyEventRelayServiceCallback client);
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,10 @@ class KeyEventRelayService : Service() {
Timber.d("KeyEventRelayService: onKeyEvent ${event?.keyCode}")

synchronized(callbackLock) {
if (!clients.containsKey(targetPackageName)) {
if (!clientConnections.containsKey(targetPackageName)) {
return false
}

val client = clients[targetPackageName]!!

try {
// Get the process ID of the app that called this service.
val sourcePackageName = getCallerPackageName() ?: return false
Expand All @@ -54,10 +52,18 @@ class KeyEventRelayService : Service() {
return false
}

return client.callback.onKeyEvent(event, targetPackageName)
var consumeKeyEvent = false

for (connection in clientConnections[targetPackageName]!!) {
if (connection.callback.onKeyEvent(event, targetPackageName)) {
consumeKeyEvent = true
}
}

return consumeKeyEvent
} catch (e: DeadObjectException) {
// If the application is no longer connected then delete the callback.
clients.remove(targetPackageName)
clientConnections.remove(targetPackageName)
return false
}
}
Expand All @@ -74,29 +80,45 @@ class KeyEventRelayService : Service() {
synchronized(callbackLock) {
Timber.d("Package $sourcePackageName registered a key event relay callback.")
val connection = ClientConnection(sourcePackageName, client)
clients[sourcePackageName] = connection

if (clientConnections.containsKey(sourcePackageName)) {
clientConnections[sourcePackageName] =
clientConnections[sourcePackageName]!!.plus(connection)
} else {
clientConnections[sourcePackageName] = arrayOf(connection)
}

client.asBinder().linkToDeath(connection, 0)
}
}

override fun unregisterCallback() {
override fun unregisterAllCallbacks() {
synchronized(callbackLock) {
val sourcePackageName = getCallerPackageName() ?: return

Timber.d("Package $sourcePackageName unregistered a key event relay callback.")
Timber.d("Package $sourcePackageName unregistered all key event relay callback.")

if (clientConnections.containsKey(sourcePackageName)) {
for (connection in clientConnections[sourcePackageName]!!) {
connection.callback.asBinder().unlinkToDeath(connection, 0)
}

if (clients.contains(sourcePackageName)) {
val client = clients[sourcePackageName]!!
client.callback.asBinder().unlinkToDeath(client, 0)
clients.remove(sourcePackageName)
clientConnections.remove(sourcePackageName)
}
}
}

override fun unregisterCallback(client: IKeyEventRelayServiceCallback?) {
client ?: return
val sourcePackageName = getCallerPackageName() ?: return

Timber.d("Package $sourcePackageName unregistered a key event relay callback.")
removeConnection(sourcePackageName, client)
}
}

private val callbackLock: Any = Any()
private var clients: ConcurrentHashMap<String, ClientConnection> =
private var clientConnections: ConcurrentHashMap<String, Array<ClientConnection>> =
ConcurrentHashMap()

override fun onCreate() {
Expand All @@ -114,7 +136,7 @@ class KeyEventRelayService : Service() {

override fun onDestroy() {
Timber.d("Relay service: onDestroy")
clients.clear()
clientConnections.clear()

super.onDestroy()
}
Expand All @@ -126,14 +148,33 @@ class KeyEventRelayService : Service() {
return applicationContext.packageManager.getNameForUid(sourceUid)
}

private fun removeConnection(packageName: String, callback: IKeyEventRelayServiceCallback) {
if (clientConnections.containsKey(packageName)) {
val newConnections = mutableListOf<ClientConnection>()

// Unlink the death recipient from the connection to remove and
// delete it from the list of connections for the package.
for (connection in clientConnections[packageName]!!) {
if (connection.callback == callback) {
connection.callback.asBinder().unlinkToDeath(connection, 0)
continue
}

newConnections.add(connection)
}

clientConnections[packageName] = newConnections.toTypedArray()
}
}

private inner class ClientConnection(
private val clientPackageName: String,
val callback: IKeyEventRelayServiceCallback
) : DeathRecipient {
override fun binderDied() {
Timber.d("Client binder died: $clientPackageName")
synchronized(callbackLock) {
clients.remove(clientPackageName)
removeConnection(clientPackageName, callback)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ class KeyEventRelayServiceWrapperImpl(
// because the connection is already broken at that point and it
// will fail.
try {
keyEventRelayService?.unregisterCallback()
keyEventRelayService?.unregisterCallback(callback)
ctx.unbindService(serviceConnection)
} catch (e: RemoteException) {
// do nothing
Expand Down

0 comments on commit 297f9de

Please sign in to comment.