@@ -19,15 +19,12 @@ import io.github.sds100.keymapper.common.KeyMapperClassProvider
1919import io.github.sds100.keymapper.common.utils.Constants
2020import io.github.sds100.keymapper.common.utils.KMResult
2121import io.github.sds100.keymapper.common.utils.SettingsUtils
22- import io.github.sds100.keymapper.common.utils.Success
2322import io.github.sds100.keymapper.common.utils.isSuccess
2423import io.github.sds100.keymapper.common.utils.onSuccess
2524import io.github.sds100.keymapper.sysbridge.adb.AdbManager
2625import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionManager
2726import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionState
28- import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionState.Connected
2927import io.github.sds100.keymapper.sysbridge.manager.awaitConnected
30- import io.github.sds100.keymapper.sysbridge.manager.isConnected
3128import javax.inject.Inject
3229import javax.inject.Singleton
3330import kotlinx.coroutines.CoroutineScope
@@ -81,8 +78,8 @@ class SystemBridgeSetupControllerImpl @Inject constructor(
8178 private val isAdbPairedResult: MutableStateFlow <Boolean ?> = MutableStateFlow (null )
8279 private var isAdbPairedJob: Job ? = null
8380
84- override val isAdbInputSecurityEnabled : MutableStateFlow <Boolean ? > = MutableStateFlow ( null )
85- private var checkAdbInputSecurityJob : Job ? = null
81+ override val shellHasGrantRuntimePermissions : MutableStateFlow <Boolean > =
82+ MutableStateFlow (getShellHasGrantRuntimePermissions())
8683
8784 init {
8885 // Automatically go back to the Key Mapper app when turning on wireless debugging
@@ -94,7 +91,6 @@ class SystemBridgeSetupControllerImpl @Inject constructor(
9491 // Do not automatically go back to Key Mapper after this step because
9592 // some devices show a dialog that will be auto dismissed resulting in wireless
9693 // ADB being immediately disabled. E.g OnePlus 6T Oxygen OS 11
97- // Note: ADB input security check is handled by monitoring isWirelessDebuggingEnabled flow
9894 }
9995 }
10096
@@ -110,27 +106,6 @@ class SystemBridgeSetupControllerImpl @Inject constructor(
110106 }
111107 }
112108 }
113-
114- // Automatically check ADB input security when SystemBridge is connected
115- if (Build .VERSION .SDK_INT >= Build .VERSION_CODES .R ) {
116- coroutineScope.launch {
117- // Check when SystemBridge becomes connected
118- connectionManager.connectionState.collect { connectionState ->
119- when (connectionState) {
120- is Connected -> {
121- // Delay a bit to ensure SystemBridge is ready
122- kotlinx.coroutines.delay(1000L )
123- checkAdbInputSecurityEnabled()
124- }
125-
126- is SystemBridgeConnectionState .Disconnected -> {
127- // Reset to null when SystemBridge is disconnected
128- isAdbInputSecurityEnabled.value = null
129- }
130- }
131- }
132- }
133- }
134109 }
135110
136111 override fun startWithRoot () {
@@ -396,51 +371,10 @@ class SystemBridgeSetupControllerImpl @Inject constructor(
396371 )
397372 }
398373
399- private fun checkAdbInputSecurityEnabled () {
400- if (! connectionManager.isConnected()) {
401- isAdbInputSecurityEnabled.value = null
402- return
403- }
404-
405- // Only run one check at a time
406- if (checkAdbInputSecurityJob == null || checkAdbInputSecurityJob?.isCompleted == true ) {
407- checkAdbInputSecurityJob?.cancel()
408-
409- checkAdbInputSecurityJob = coroutineScope.launch {
410- try {
411- val result = connectionManager.run { systemBridge ->
412- systemBridge.executeCommand(" getprop persist.security.adbinput" , 5000L )
413- }
414-
415- val isEnabled = when (result) {
416- is Success -> {
417- val stdout = result.value.stdout.trim()
418-
419- when (stdout) {
420- " 1" -> true
421-
422- " 0" -> false
423-
424- // If it is empty or anything else then set the value to null
425- // because what we are expecting does not exist.
426- else -> null
427- }
428- }
429-
430- else -> null
431- }
432- isAdbInputSecurityEnabled.value = isEnabled
433- } catch (_: Exception ) {
434- // If check fails, set to null
435- isAdbInputSecurityEnabled.value = null
436- }
437- }
438- }
439- }
440-
441374 fun invalidateSettings () {
442375 isDeveloperOptionsEnabled.update { getDeveloperOptionsEnabled() }
443376 isWirelessDebuggingEnabled.update { getWirelessDebuggingEnabled() }
377+ shellHasGrantRuntimePermissions.update { getShellHasGrantRuntimePermissions() }
444378 }
445379
446380 private fun getDeveloperOptionsEnabled (): Boolean {
@@ -459,6 +393,13 @@ class SystemBridgeSetupControllerImpl @Inject constructor(
459393 }
460394 }
461395
396+ private fun getShellHasGrantRuntimePermissions (): Boolean {
397+ return ctx.packageManager.checkPermission(
398+ " android.permission.GRANT_RUNTIME_PERMISSIONS" ,
399+ " com.android.shell" ,
400+ ) == PackageManager .PERMISSION_GRANTED
401+ }
402+
462403 private fun getKeyMapperAppTask (): ActivityManager .AppTask ? {
463404 val task = activityManager.appTasks
464405 ?.firstOrNull {
@@ -506,10 +447,14 @@ interface SystemBridgeSetupController {
506447 fun autoStartWithAdb ()
507448
508449 /* *
509- * If this value is null then the option does not exist or can not be checked
510- * because the system bridge is disconnected.
450+ * See issue #1965.
451+ *
452+ * Whether the Shell package has GRANT_RUNTIME_PERMISSIONS permission. On Xiaomi devices, the
453+ * user needs to enable "USB debugging security settings" in Developer Options for
454+ * the Shell to have permission to grant runtime permissions, such as WRITE_SECURE_SETTINGS
455+ * to Key Mapper.
511456 */
512- val isAdbInputSecurityEnabled : StateFlow <Boolean ? >
457+ val shellHasGrantRuntimePermissions : StateFlow <Boolean >
513458
514459 fun launchDeveloperOptions ()
515460
0 commit comments