diff --git a/app/src/main/java/io/github/sds100/keymapper/inputmethod/latin/KeyEventRelayServiceWrapper.kt b/app/src/main/java/io/github/sds100/keymapper/inputmethod/latin/KeyEventRelayServiceWrapper.kt index a62564430..8ac8b9da1 100644 --- a/app/src/main/java/io/github/sds100/keymapper/inputmethod/latin/KeyEventRelayServiceWrapper.kt +++ b/app/src/main/java/io/github/sds100/keymapper/inputmethod/latin/KeyEventRelayServiceWrapper.kt @@ -1,14 +1,17 @@ package io.github.sds100.keymapper.inputmethod.latin +import android.content.BroadcastReceiver import android.content.ComponentName import android.content.Context import android.content.Intent +import android.content.IntentFilter import android.content.ServiceConnection import android.os.DeadObjectException import android.os.IBinder import android.os.RemoteException import android.util.Log import android.view.KeyEvent +import androidx.core.content.ContextCompat import io.github.sds100.keymapper.api.IKeyEventRelayService import io.github.sds100.keymapper.api.IKeyEventRelayServiceCallback @@ -29,7 +32,10 @@ class KeyEventRelayServiceWrapperImpl( ) : KeyEventRelayServiceWrapper { companion object { - const val ACTION_BIND_RELAY_SERVICE = "io.github.sds100.keymapper.ACTION_BIND_RELAY_SERVICE" + /** + * This is used to listen to when the key event relay service is restarted in Key Mapper. + */ + const val ACTION_REBIND_RELAY_SERVICE = "io.github.sds100.keymapper.ACTION_REBIND_RELAY_SERVICE" } private val ctx: Context = context.applicationContext @@ -46,23 +52,44 @@ class KeyEventRelayServiceWrapperImpl( ) { synchronized(keyEventRelayServiceLock) { keyEventRelayService = IKeyEventRelayService.Stub.asInterface(service) - Log.d(LatinIME.TAG, "Register with key event relay service") + Log.d(LatinIME.TAG, "Key event relay service started: $servicePackageName") keyEventRelayService?.registerCallback(callback) } } override fun onServiceDisconnected(name: ComponentName?) { synchronized(keyEventRelayServiceLock) { - try { - Log.d(LatinIME.TAG, "Unregister with key event relay service") - keyEventRelayService?.unregisterCallback() - } catch (_: RemoteException) { - } finally { - keyEventRelayService = null - } + // Do not unregister the callback in onServiceDisconnected + // because the connection is already broken at that point and it + // will fail. + + Log.d(LatinIME.TAG, "Key event relay service stopped: $servicePackageName") + + keyEventRelayService = null + } + } + } + + private val broadcastReceiver: BroadcastReceiver = object : BroadcastReceiver() { + override fun onReceive(context: Context?, intent: Intent?) { + context ?: return + intent ?: return + + when (intent.action) { + ACTION_REBIND_RELAY_SERVICE -> { + bind() } } } + } + + init { + val intentFilter = IntentFilter().apply { + addAction(ACTION_REBIND_RELAY_SERVICE) + } + + ContextCompat.registerReceiver(ctx, broadcastReceiver, intentFilter, ContextCompat.RECEIVER_EXPORTED) + } override fun sendKeyEvent( event: KeyEvent?, @@ -106,6 +133,15 @@ class KeyEventRelayServiceWrapperImpl( // an exception is thrown if you unbind from a service // while there is no registered connection. if (isBound) { + // Unregister the callback if this input method is unbinding + // from the relay service. This should not happen in onServiceDisconnected + // because the connection is already broken at that point and it + // will fail. + try { + keyEventRelayService?.unregisterCallback() + } catch (e: RemoteException) { + // do nothing + } ctx.unbindService(serviceConnection) } } diff --git a/app/src/main/java/io/github/sds100/keymapper/inputmethod/latin/LatinIME.java b/app/src/main/java/io/github/sds100/keymapper/inputmethod/latin/LatinIME.java index d352180fe..fddd5ddb9 100644 --- a/app/src/main/java/io/github/sds100/keymapper/inputmethod/latin/LatinIME.java +++ b/app/src/main/java/io/github/sds100/keymapper/inputmethod/latin/LatinIME.java @@ -58,6 +58,7 @@ import javax.annotation.Nonnull; +import androidx.core.content.ContextCompat; import io.github.sds100.keymapper.api.IKeyEventRelayServiceCallback; import io.github.sds100.keymapper.inputmethod.accessibility.AccessibilityUtils; import io.github.sds100.keymapper.inputmethod.annotations.UsedForTesting; @@ -744,31 +745,31 @@ public void onCreate() { // Register to receive ringer mode change. final IntentFilter filter = new IntentFilter(); filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION); - registerReceiver(mRingerModeChangeReceiver, filter); + ContextCompat.registerReceiver(this, mRingerModeChangeReceiver, filter, ContextCompat.RECEIVER_EXPORTED); // Register to receive installation and removal of a dictionary pack. final IntentFilter packageFilter = new IntentFilter(); packageFilter.addAction(Intent.ACTION_PACKAGE_ADDED); packageFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); packageFilter.addDataScheme(SCHEME_PACKAGE); - registerReceiver(mDictionaryPackInstallReceiver, packageFilter); + ContextCompat.registerReceiver(this, mDictionaryPackInstallReceiver, packageFilter, ContextCompat.RECEIVER_EXPORTED); final IntentFilter newDictFilter = new IntentFilter(); newDictFilter.addAction(DictionaryPackConstants.NEW_DICTIONARY_INTENT_ACTION); - registerReceiver(mDictionaryPackInstallReceiver, newDictFilter); + ContextCompat.registerReceiver(this, mDictionaryPackInstallReceiver, newDictFilter, ContextCompat.RECEIVER_NOT_EXPORTED); final IntentFilter dictDumpFilter = new IntentFilter(); dictDumpFilter.addAction(DictionaryDumpBroadcastReceiver.DICTIONARY_DUMP_INTENT_ACTION); - registerReceiver(mDictionaryDumpBroadcastReceiver, dictDumpFilter); + ContextCompat.registerReceiver(this, mDictionaryDumpBroadcastReceiver, dictDumpFilter, ContextCompat.RECEIVER_NOT_EXPORTED); final IntentFilter hideSoftInputFilter = new IntentFilter(); hideSoftInputFilter.addAction(ACTION_HIDE_SOFT_INPUT); - registerReceiver(mHideSoftInputReceiver, hideSoftInputFilter, PERMISSION_HIDE_SOFT_INPUT, - null /* scheduler */); + ContextCompat.registerReceiver(this, mHideSoftInputReceiver, hideSoftInputFilter, PERMISSION_HIDE_SOFT_INPUT, + null /* scheduler */, ContextCompat.RECEIVER_EXPORTED); final IntentFilter restartAfterUnlockFilter = new IntentFilter(); restartAfterUnlockFilter.addAction(Intent.ACTION_USER_UNLOCKED); - registerReceiver(mRestartAfterDeviceUnlockReceiver, restartAfterUnlockFilter); + ContextCompat.registerReceiver(this, mRestartAfterDeviceUnlockReceiver, restartAfterUnlockFilter, ContextCompat.RECEIVER_EXPORTED); final IntentFilter keyMapperIntentFilter = new IntentFilter(); keyMapperIntentFilter.addAction(KEY_MAPPER_INPUT_METHOD_ACTION_INPUT_DOWN_UP); @@ -776,7 +777,7 @@ public void onCreate() { keyMapperIntentFilter.addAction(KEY_MAPPER_INPUT_METHOD_ACTION_INPUT_UP); keyMapperIntentFilter.addAction(KEY_MAPPER_INPUT_METHOD_ACTION_TEXT); - registerReceiver(mKeyMapperBroadcastReceiver, keyMapperIntentFilter); + ContextCompat.registerReceiver(this, mKeyMapperBroadcastReceiver, keyMapperIntentFilter, ContextCompat.RECEIVER_EXPORTED); // Connect to the different key mapper build types. mKeyEventRelayServiceWrapperRelease =