Skip to content

Commit c8851a3

Browse files
committed
#2034 catch errors when injecting events with Expert mode on Xiaomi devices and show warning to fix on home screen
1 parent 9493130 commit c8851a3

8 files changed

Lines changed: 129 additions & 82 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@
1313
## Fixed
1414

1515
- #2030 do not filter out unknown evdev key events.
16-
- #2028 work around Shizuku bug on Mediatek devices that prevents Expert mode from starting
16+
- #2028 work around Shizuku bug on Mediatek devices that prevents Expert mode from starting.
17+
- #2034 catch errors when injecting events with Expert mode on Xiaomi devices and show warning to fix on home screen.
1718

1819
## [4.0.3](https://github.com/sds100/KeyMapper/releases/tag/v4.0.3)
1920

base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeScreen.kt

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -318,15 +318,6 @@ private fun LoadedContent(
318318

319319
when (state) {
320320
is ExpertModeState.Started -> {
321-
ExpertModeStartedCard(
322-
modifier = Modifier
323-
.fillMaxWidth()
324-
.padding(horizontal = 8.dp),
325-
onStopClick = onStopServiceClick,
326-
)
327-
328-
Spacer(modifier = Modifier.height(8.dp))
329-
330321
if (!state.isDefaultUsbModeCompatible) {
331322
IncompatibleUsbModeCard(
332323
modifier = Modifier
@@ -344,12 +335,20 @@ private fun LoadedContent(
344335
modifier = Modifier
345336
.fillMaxWidth()
346337
.padding(horizontal = 8.dp),
347-
onLaunchDeveloperOptionsClick = onLaunchDeveloperOptionsClick,
348338
)
349339

350340
Spacer(modifier = Modifier.height(8.dp))
351341
}
352342

343+
ExpertModeStartedCard(
344+
modifier = Modifier
345+
.fillMaxWidth()
346+
.padding(horizontal = 8.dp),
347+
onStopClick = onStopServiceClick,
348+
)
349+
350+
Spacer(modifier = Modifier.height(8.dp))
351+
353352
SwitchPreferenceCompose(
354353
modifier = Modifier.padding(horizontal = 8.dp),
355354
title = stringResource(R.string.title_pref_expert_mode_auto_start),
@@ -530,10 +529,8 @@ private fun IncompatibleUsbModeCard(modifier: Modifier = Modifier) {
530529
}
531530

532531
@Composable
533-
private fun UsbDebuggingSecuritySettingsCard(
534-
modifier: Modifier = Modifier,
535-
onLaunchDeveloperOptionsClick: () -> Unit = {},
536-
) {
532+
private fun UsbDebuggingSecuritySettingsCard(modifier: Modifier = Modifier) {
533+
val ctx = LocalContext.current
537534
SetupCard(
538535
modifier = modifier,
539536
color = MaterialTheme.colorScheme.errorContainer,
@@ -558,7 +555,13 @@ private fun UsbDebuggingSecuritySettingsCard(
558555
buttonText = stringResource(
559556
R.string.expert_mode_usb_debugging_security_settings_button,
560557
),
561-
onButtonClick = onLaunchDeveloperOptionsClick,
558+
onButtonClick = {
559+
SettingsUtils.launchSettingsScreen(
560+
ctx,
561+
Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS,
562+
null,
563+
)
564+
},
562565
)
563566
}
564567

base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeViewModel.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -213,15 +213,15 @@ class ExpertModeViewModel @Inject constructor(
213213
private fun startedStateFlow(): Flow<ExpertModeState.Started> = combine(
214214
useCase.isAutoStartBootEnabled,
215215
useCase.isAutoStartBootAllowed,
216-
useCase.shellHasGrantRuntimePermissions,
217-
) { autoStartBootChecked, autoStartBootEnabled, shellHasGrantRuntimePermissions ->
216+
useCase.xiaomiAdbSecuritySettingsEnabled,
217+
) { autoStartBootChecked, autoStartBootEnabled, xiaomiAdbSecuritySettingsEnabled ->
218218
ExpertModeState.Started(
219219
isDefaultUsbModeCompatible =
220220
useCase.isCompatibleUsbModeSelected().valueOrNull()
221221
?: false,
222222
autoStartBootChecked = autoStartBootChecked,
223223
autoStartBootEnabled = autoStartBootEnabled,
224-
showXiaomiAdbInputSecurityWarning = !shellHasGrantRuntimePermissions,
224+
showXiaomiAdbInputSecurityWarning = !xiaomiAdbSecuritySettingsEnabled,
225225
)
226226
}
227227
}

base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeSetupUseCase.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,8 +187,8 @@ class SystemBridgeSetupUseCaseImpl @Inject constructor(
187187
systemBridgeSetupController.launchDeveloperOptions()
188188
}
189189

190-
override val shellHasGrantRuntimePermissions: Flow<Boolean> =
191-
systemBridgeSetupController.shellHasGrantRuntimePermissions
190+
override val xiaomiAdbSecuritySettingsEnabled: Flow<Boolean> =
191+
systemBridgeSetupController.xiaomiAdbSecuritySettingsEnabled
192192

193193
override fun connectWifiNetwork() {
194194
networkAdapter.connectWifiNetwork()
@@ -352,7 +352,7 @@ interface SystemBridgeSetupUseCase {
352352
fun startSystemBridgeWithAdb()
353353
fun autoStartSystemBridgeWithAdb()
354354

355-
val shellHasGrantRuntimePermissions: Flow<Boolean>
355+
val xiaomiAdbSecuritySettingsEnabled: Flow<Boolean>
356356

357357
fun isCompatibleUsbModeSelected(): KMResult<Boolean>
358358

base/src/main/java/io/github/sds100/keymapper/base/home/KeyMapListViewModel.kt

Lines changed: 70 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ import kotlinx.coroutines.flow.distinctUntilChanged
6666
import kotlinx.coroutines.flow.filterIsInstance
6767
import kotlinx.coroutines.flow.first
6868
import kotlinx.coroutines.flow.flatMapLatest
69+
import kotlinx.coroutines.flow.flowOf
6970
import kotlinx.coroutines.flow.flowOn
7071
import kotlinx.coroutines.flow.map
7172
import kotlinx.coroutines.flow.stateIn
@@ -99,6 +100,7 @@ class KeyMapListViewModel(
99100
const val ID_BATTERY_OPTIMISATION_LIST_ITEM = "battery_optimised"
100101
const val ID_LOGGING_ENABLED_LIST_ITEM = "logging_enabled"
101102
const val ID_NOTIFICATION_PERMISSION_DENIED_LIST_ITEM = "notification_permission_denied"
103+
const val ID_XIAOMI_ADB_SECURITY_SETTINGS_LIST_ITEM = "xiaomi_adb_security_settings"
102104
}
103105

104106
val sortViewModel = SortViewModel(coroutineScope, sortKeyMaps)
@@ -144,70 +146,83 @@ class KeyMapListViewModel(
144146

145147
private val isEditingGroupName = MutableStateFlow(false)
146148

147-
private val warnings: Flow<List<HomeWarningListItem>> = combine(
148-
showAlertsUseCase.isBatteryOptimised,
149-
accessibilityServiceState,
150-
showAlertsUseCase.hideAlerts,
151-
showAlertsUseCase.isLoggingEnabled,
152-
showAlertsUseCase.showNotificationPermissionAlert,
153-
) {
154-
isBatteryOptimised,
155-
serviceState,
156-
isHidden,
157-
isLoggingEnabled,
158-
showNotificationPermissionAlert,
159-
->
160-
if (isHidden) {
161-
return@combine emptyList()
162-
}
163-
164-
buildList {
165-
when (serviceState) {
166-
AccessibilityServiceState.CRASHED ->
167-
add(
168-
HomeWarningListItem(
169-
ID_ACCESSIBILITY_SERVICE_CRASHED_LIST_ITEM,
170-
getString(R.string.home_error_accessibility_service_is_crashed),
171-
),
172-
)
173-
174-
AccessibilityServiceState.DISABLED ->
175-
add(
176-
HomeWarningListItem(
177-
ID_ACCESSIBILITY_SERVICE_DISABLED_LIST_ITEM,
178-
getString(R.string.home_error_accessibility_service_is_disabled),
179-
),
180-
)
181-
182-
AccessibilityServiceState.ENABLED -> {}
149+
private val warnings: Flow<List<HomeWarningListItem>> =
150+
showAlertsUseCase.hideAlerts.flatMapLatest { hideAlerts ->
151+
if (hideAlerts) {
152+
flowOf(emptyList())
153+
} else {
154+
combine(
155+
showAlertsUseCase.isBatteryOptimised,
156+
accessibilityServiceState,
157+
showAlertsUseCase.isLoggingEnabled,
158+
showAlertsUseCase.showNotificationPermissionAlert,
159+
showAlertsUseCase.showXiaomiAdbSecuritySettingsWarning,
160+
this::buildWarningListItems,
161+
)
183162
}
163+
}
184164

185-
if (isBatteryOptimised) {
165+
private fun buildWarningListItems(
166+
isBatteryOptimised: Boolean,
167+
serviceState: AccessibilityServiceState,
168+
isLoggingEnabled: Boolean,
169+
showNotificationPermissionAlert: Boolean,
170+
showXiaomiAdbSecuritySettingsWarning: Boolean,
171+
): List<HomeWarningListItem> = buildList {
172+
when (serviceState) {
173+
AccessibilityServiceState.CRASHED ->
186174
add(
187175
HomeWarningListItem(
188-
ID_BATTERY_OPTIMISATION_LIST_ITEM,
189-
getString(R.string.home_error_is_battery_optimised),
176+
ID_ACCESSIBILITY_SERVICE_CRASHED_LIST_ITEM,
177+
getString(R.string.home_error_accessibility_service_is_crashed),
190178
),
191179
)
192-
} // don't show a success message for this
193180

194-
if (showNotificationPermissionAlert) {
181+
AccessibilityServiceState.DISABLED ->
195182
add(
196183
HomeWarningListItem(
197-
ID_NOTIFICATION_PERMISSION_DENIED_LIST_ITEM,
198-
getString(R.string.home_error_notification_permission),
184+
ID_ACCESSIBILITY_SERVICE_DISABLED_LIST_ITEM,
185+
getString(R.string.home_error_accessibility_service_is_disabled),
199186
),
200187
)
201-
}
202188

203-
if (isLoggingEnabled) {
204-
add(
205-
HomeWarningListItem(
206-
ID_LOGGING_ENABLED_LIST_ITEM,
207-
getString(R.string.home_error_logging_enabled),
208-
),
209-
)
210-
}
189+
AccessibilityServiceState.ENABLED -> {}
190+
}
191+
192+
if (isBatteryOptimised) {
193+
add(
194+
HomeWarningListItem(
195+
ID_BATTERY_OPTIMISATION_LIST_ITEM,
196+
getString(R.string.home_error_is_battery_optimised),
197+
),
198+
)
199+
} // don't show a success message for this
200+
201+
if (showNotificationPermissionAlert) {
202+
add(
203+
HomeWarningListItem(
204+
ID_NOTIFICATION_PERMISSION_DENIED_LIST_ITEM,
205+
getString(R.string.home_error_notification_permission),
206+
),
207+
)
208+
}
209+
210+
if (showXiaomiAdbSecuritySettingsWarning) {
211+
add(
212+
HomeWarningListItem(
213+
ID_XIAOMI_ADB_SECURITY_SETTINGS_LIST_ITEM,
214+
getString(R.string.expert_mode_usb_debugging_security_settings_description),
215+
),
216+
)
217+
}
218+
219+
if (isLoggingEnabled) {
220+
add(
221+
HomeWarningListItem(
222+
ID_LOGGING_ENABLED_LIST_ITEM,
223+
getString(R.string.home_error_logging_enabled),
224+
),
225+
)
211226
}
212227
}
213228

@@ -687,6 +702,9 @@ class KeyMapListViewModel(
687702

688703
ID_NOTIFICATION_PERMISSION_DENIED_LIST_ITEM ->
689704
showNotificationPermissionAlertDialog()
705+
706+
ID_XIAOMI_ADB_SECURITY_SETTINGS_LIST_ITEM ->
707+
showAlertsUseCase.launchDeveloperOptions()
690708
}
691709
}
692710
}

base/src/main/java/io/github/sds100/keymapper/base/home/ShowHomeScreenAlertsUseCase.kt

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package io.github.sds100.keymapper.base.home
33
import io.github.sds100.keymapper.base.keymaps.PauseKeyMapsUseCase
44
import io.github.sds100.keymapper.data.Keys
55
import io.github.sds100.keymapper.data.repositories.PreferenceRepository
6-
import io.github.sds100.keymapper.system.accessibility.AccessibilityServiceAdapter
6+
import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionManager
7+
import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionState
8+
import io.github.sds100.keymapper.sysbridge.service.SystemBridgeSetupController
79
import io.github.sds100.keymapper.system.permissions.Permission
810
import io.github.sds100.keymapper.system.permissions.PermissionAdapter
911
import javax.inject.Inject
@@ -14,8 +16,9 @@ import kotlinx.coroutines.flow.map
1416
class ShowHomeScreenAlertsUseCaseImpl @Inject constructor(
1517
private val preferences: PreferenceRepository,
1618
private val permissions: PermissionAdapter,
17-
private val accessibilityServiceAdapter: AccessibilityServiceAdapter,
1819
private val pauseKeyMapsUseCase: PauseKeyMapsUseCase,
20+
private val systemBridgeConnectionManager: SystemBridgeConnectionManager,
21+
private val systemBridgeSetupController: SystemBridgeSetupController,
1922
) : ShowHomeScreenAlertsUseCase {
2023
override val hideAlerts: Flow<Boolean> =
2124
preferences.get(Keys.hideHomeScreenAlerts).map { it == true }
@@ -48,6 +51,19 @@ class ShowHomeScreenAlertsUseCaseImpl @Inject constructor(
4851
!isGranted && !neverShow
4952
}
5053

54+
override val showXiaomiAdbSecuritySettingsWarning: Flow<Boolean> =
55+
combine(
56+
systemBridgeConnectionManager.connectionState,
57+
systemBridgeSetupController.xiaomiAdbSecuritySettingsEnabled,
58+
) { connectionState, xiaomiSettingEnabled ->
59+
connectionState is SystemBridgeConnectionState.Connected &&
60+
!xiaomiSettingEnabled
61+
}
62+
63+
override fun launchDeveloperOptions() {
64+
systemBridgeSetupController.launchDeveloperOptions()
65+
}
66+
5167
override fun requestNotificationPermission() {
5268
permissions.request(Permission.POST_NOTIFICATIONS)
5369
}
@@ -70,4 +86,8 @@ interface ShowHomeScreenAlertsUseCase {
7086
val showNotificationPermissionAlert: Flow<Boolean>
7187
fun requestNotificationPermission()
7288
fun neverShowNotificationPermissionAlert()
89+
90+
val showXiaomiAdbSecuritySettingsWarning: Flow<Boolean>
91+
92+
fun launchDeveloperOptions()
7393
}

sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridge.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -486,7 +486,12 @@ class SystemBridge : ISystemBridge.Stub() {
486486
}
487487

488488
override fun injectInputEvent(event: InputEvent?, mode: Int): Boolean {
489-
return inputManager.injectInputEvent(event, mode)
489+
try {
490+
return inputManager.injectInputEvent(event, mode)
491+
} catch (e: SecurityException) {
492+
logCallback?.onLog(Log.WARN, "Failed to inject event due to security exception: $e")
493+
return false
494+
}
490495
}
491496

492497
override fun getEvdevInputDevices(): Array<out EvdevDeviceInfo?> {

sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridgeSetupController.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,8 @@ class SystemBridgeSetupControllerImpl @Inject constructor(
8181
private val isAdbPairedResult: MutableStateFlow<Boolean?> = MutableStateFlow(null)
8282
private var isAdbPairedJob: Job? = null
8383

84-
override val shellHasGrantRuntimePermissions: MutableStateFlow<Boolean> =
85-
MutableStateFlow(getShellHasGrantRuntimePermissions())
84+
override val xiaomiAdbSecuritySettingsEnabled: MutableStateFlow<Boolean> =
85+
MutableStateFlow(getXiaomiAdbSecuritySettingsEnabled())
8686

8787
init {
8888
// Automatically go back to the Key Mapper app when turning on wireless debugging
@@ -365,7 +365,7 @@ class SystemBridgeSetupControllerImpl @Inject constructor(
365365
fun invalidateSettings() {
366366
isDeveloperOptionsEnabled.update { getDeveloperOptionsEnabled() }
367367
isWirelessDebuggingEnabled.update { getWirelessDebuggingEnabled() }
368-
shellHasGrantRuntimePermissions.update { getShellHasGrantRuntimePermissions() }
368+
xiaomiAdbSecuritySettingsEnabled.update { getXiaomiAdbSecuritySettingsEnabled() }
369369
}
370370

371371
private fun getDeveloperOptionsEnabled(): Boolean {
@@ -384,7 +384,7 @@ class SystemBridgeSetupControllerImpl @Inject constructor(
384384
}
385385
}
386386

387-
private fun getShellHasGrantRuntimePermissions(): Boolean {
387+
private fun getXiaomiAdbSecuritySettingsEnabled(): Boolean {
388388
return ctx.packageManager.checkPermission(
389389
"android.permission.GRANT_RUNTIME_PERMISSIONS",
390390
"com.android.shell",
@@ -444,7 +444,7 @@ interface SystemBridgeSetupController {
444444
* the Shell to have permission to grant runtime permissions, such as WRITE_SECURE_SETTINGS
445445
* to Key Mapper.
446446
*/
447-
val shellHasGrantRuntimePermissions: StateFlow<Boolean>
447+
val xiaomiAdbSecuritySettingsEnabled: StateFlow<Boolean>
448448

449449
fun launchDeveloperOptions()
450450

0 commit comments

Comments
 (0)