Skip to content

Commit 12ea212

Browse files
committed
#1999 Use a more reliable method to check whether Shell has GRANT_RUNTIME_PERMISSIONS permission
1 parent e1970e3 commit 12ea212

File tree

5 files changed

+29
-88
lines changed

5 files changed

+29
-88
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
- #2005 NPE in onSaveInstanceState.
99
- #2000 tell the user to tap OS version or build number.
1010
- #1996 Check if Night Shift is supported on a device before activating to prevent the screen going black when activated.
11+
- #1999 Use a more reliable method to check whether Shell has GRANT_RUNTIME_PERMISSIONS permission.
1112

1213
## [4.0.0](https://github.com/sds100/KeyMapper/releases/tag/v4.0.0)
1314

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ private fun LoadedContent(
332332

333333
// Only show auto-start options and warnings when Expert Mode is started
334334
// Show USB debugging security settings warning if disabled
335-
if (state.isAdbInputSecurityEnabled == false) {
335+
if (state.showXiaomiAdbInputSecurityWarning) {
336336
UsbDebuggingSecuritySettingsCard(
337337
modifier = Modifier
338338
.fillMaxWidth()
@@ -970,7 +970,7 @@ private fun PreviewDark() {
970970
isDefaultUsbModeCompatible = true,
971971
autoStartBootChecked = true,
972972
autoStartBootEnabled = true,
973-
isAdbInputSecurityEnabled = null,
973+
showXiaomiAdbInputSecurityWarning = false,
974974
),
975975
),
976976
showInfoCard = false,
@@ -1013,7 +1013,7 @@ private fun PreviewStarted() {
10131013
isDefaultUsbModeCompatible = false,
10141014
autoStartBootChecked = false,
10151015
autoStartBootEnabled = true,
1016-
isAdbInputSecurityEnabled = null,
1016+
showXiaomiAdbInputSecurityWarning = false,
10171017
),
10181018
),
10191019
showInfoCard = false,
@@ -1062,7 +1062,7 @@ private fun PreviewUsbDebuggingSecuritySettingsCard() {
10621062
isDefaultUsbModeCompatible = true,
10631063
autoStartBootChecked = false,
10641064
autoStartBootEnabled = true,
1065-
isAdbInputSecurityEnabled = false,
1065+
showXiaomiAdbInputSecurityWarning = false,
10661066
),
10671067
),
10681068
showInfoCard = false,

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,15 +198,15 @@ class ExpertModeViewModel @Inject constructor(
198198
private fun startedStateFlow(): Flow<ExpertModeState.Started> = combine(
199199
useCase.isAutoStartBootEnabled,
200200
useCase.isAutoStartBootAllowed,
201-
useCase.isAdbInputSecurityEnabled,
202-
) { autoStartBootChecked, autoStartBootEnabled, isAdbInputSecurityEnabled ->
201+
useCase.shellHasGrantRuntimePermissions,
202+
) { autoStartBootChecked, autoStartBootEnabled, shellHasGrantRuntimePermissions ->
203203
ExpertModeState.Started(
204204
isDefaultUsbModeCompatible =
205205
useCase.isCompatibleUsbModeSelected().valueOrNull()
206206
?: false,
207207
autoStartBootChecked = autoStartBootChecked,
208208
autoStartBootEnabled = autoStartBootEnabled,
209-
isAdbInputSecurityEnabled = isAdbInputSecurityEnabled,
209+
showXiaomiAdbInputSecurityWarning = !shellHasGrantRuntimePermissions,
210210
)
211211
}
212212
}
@@ -230,7 +230,7 @@ sealed class ExpertModeState {
230230
val isDefaultUsbModeCompatible: Boolean,
231231
val autoStartBootChecked: Boolean,
232232
val autoStartBootEnabled: Boolean,
233-
val isAdbInputSecurityEnabled: Boolean?,
233+
val showXiaomiAdbInputSecurityWarning: Boolean,
234234
) : ExpertModeState()
235235
}
236236

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

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

189-
@RequiresApi(Build.VERSION_CODES.R)
190-
override val isAdbInputSecurityEnabled: Flow<Boolean?> =
191-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
192-
systemBridgeSetupController.isAdbInputSecurityEnabled
193-
} else {
194-
flowOf(null)
195-
}
189+
override val shellHasGrantRuntimePermissions: Flow<Boolean> =
190+
systemBridgeSetupController.shellHasGrantRuntimePermissions
196191

197192
override fun connectWifiNetwork() {
198193
networkAdapter.connectWifiNetwork()
@@ -355,7 +350,7 @@ interface SystemBridgeSetupUseCase {
355350
fun startSystemBridgeWithAdb()
356351
fun autoStartSystemBridgeWithAdb()
357352

358-
val isAdbInputSecurityEnabled: Flow<Boolean?>
353+
val shellHasGrantRuntimePermissions: Flow<Boolean>
359354

360355
fun isCompatibleUsbModeSelected(): KMResult<Boolean>
361356

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

Lines changed: 17 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,12 @@ import io.github.sds100.keymapper.common.KeyMapperClassProvider
1919
import io.github.sds100.keymapper.common.utils.Constants
2020
import io.github.sds100.keymapper.common.utils.KMResult
2121
import io.github.sds100.keymapper.common.utils.SettingsUtils
22-
import io.github.sds100.keymapper.common.utils.Success
2322
import io.github.sds100.keymapper.common.utils.isSuccess
2423
import io.github.sds100.keymapper.common.utils.onSuccess
2524
import io.github.sds100.keymapper.sysbridge.adb.AdbManager
2625
import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionManager
2726
import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionState
28-
import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionState.Connected
2927
import io.github.sds100.keymapper.sysbridge.manager.awaitConnected
30-
import io.github.sds100.keymapper.sysbridge.manager.isConnected
3128
import javax.inject.Inject
3229
import javax.inject.Singleton
3330
import 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

Comments
 (0)