Skip to content

Commit ec41b6a

Browse files
feat: adding logic to check usb and adb connection (#2258)
* adding logic to check usb and adb connection * removed old cable logic and work in progress in updating usb and adb elements * added runtime adb check while screen is shown * fixed issue with wrong general state checking * airgap viewmodel part adapted to usb and adb control * remove extra log statement * usb check implemented * enabled feature flag for usb debug builds * updated description for usb * updated description for usb2
1 parent a221c55 commit ec41b6a

File tree

7 files changed

+118
-112
lines changed

7 files changed

+118
-112
lines changed

android/src/main/java/io/parity/signer/domain/FeatureFlags.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ object FeatureFlags {
1212
FeatureOption.SKIP_ROOTED_CHECK_EMULATOR -> false
1313
FeatureOption.EXPORT_SECRET_KEY -> false //unused
1414
FeatureOption.FAIL_DB_VERSION_CHECK -> false
15+
FeatureOption.SKIP_USB_CHECK -> true
1516

1617
}
1718
}
@@ -24,6 +25,7 @@ enum class FeatureOption {
2425
FAIL_DB_VERSION_CHECK,
2526
SKIP_UNLOCK_FOR_DEVELOPMENT,
2627
SKIP_ROOTED_CHECK_EMULATOR,
28+
SKIP_USB_CHECK,
2729
EXPORT_SECRET_KEY; //unused as sample
2830

2931
fun isEnabled() = FeatureFlags.isEnabled(this)

android/src/main/java/io/parity/signer/domain/NetworkExposedStateKeeper.kt

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import android.content.Intent
88
import android.content.IntentFilter
99
import android.net.wifi.WifiManager
1010
import android.provider.Settings
11+
import android.util.Log
1112
import io.parity.signer.domain.backend.UniffiInteractor
1213
import io.parity.signer.uniffi.historyAcknowledgeWarnings
1314
import io.parity.signer.uniffi.historyGetWarnings
@@ -32,17 +33,23 @@ class NetworkExposedStateKeeper(
3233
MutableStateFlow(null)
3334
val bluetoothDisabledState: StateFlow<Boolean?> = _bluetoothDisabledState
3435

36+
private val _usbDisconnected: MutableStateFlow<Boolean?> =
37+
MutableStateFlow(null)
38+
val usbDisconnected: StateFlow<Boolean?> = _usbDisconnected
39+
3540
private val _airGapModeState: MutableStateFlow<NetworkState> =
3641
MutableStateFlow(NetworkState.None)
3742
val airGapModeState: StateFlow<NetworkState> = _airGapModeState
3843

44+
private val isCurentlyBreached: Boolean
45+
get() = airPlaneModeEnabled.value == false || wifiDisabledState.value == false
46+
|| bluetoothDisabledState.value == false || usbDisconnected.value == false
47+
3948
init {
4049
registerAirplaneBroadcastReceiver()
4150
registerWifiBroadcastReceiver()
4251
registerBluetoothBroadcastReceiver()
43-
reactOnAirplaneMode()
44-
reactOnWifiAwareState()
45-
reactOnBluetooth()
52+
registerUsbBroadcastReceiver()
4653
}
4754

4855
/**
@@ -64,6 +71,7 @@ class NetworkExposedStateKeeper(
6471
}
6572
}
6673
appContext.registerReceiver(receiver, intentFilter)
74+
reactOnAirplaneMode()
6775
}
6876

6977
private fun registerBluetoothBroadcastReceiver() {
@@ -74,10 +82,23 @@ class NetworkExposedStateKeeper(
7482
}
7583
}
7684
appContext.registerReceiver(receiver, intentFilter)
85+
reactOnBluetooth()
7786
}
7887

79-
private fun updateGeneralAirgap(isBreached: Boolean) {
80-
if (isBreached) {
88+
private fun registerUsbBroadcastReceiver() {
89+
val intentFilter = IntentFilter("android.hardware.usb.action.USB_STATE")
90+
val receiver: BroadcastReceiver = object : BroadcastReceiver() {
91+
override fun onReceive(context: Context, intent: Intent) {
92+
Log.e("TAGG", "usb broadcast")
93+
reactOnUsb(intent)
94+
}
95+
}
96+
val oldIntent = appContext.registerReceiver(receiver, intentFilter)
97+
oldIntent?.let { reactOnUsb(it) }
98+
}
99+
100+
private fun updateGeneralAirgapState() {
101+
if (isCurentlyBreached) {
81102
if (airGapModeState.value != NetworkState.Active) {
82103
_airGapModeState.value = NetworkState.Active
83104
if (appContext.isDbCreatedAndOnboardingPassed()) {
@@ -100,15 +121,37 @@ class NetworkExposedStateKeeper(
100121
0
101122
) == 0
102123
_airplaneModeEnabled.value = !airplaneModeOff
103-
updateGeneralAirgap(airplaneModeOff)
124+
updateGeneralAirgapState()
104125
}
105126

106127
private fun reactOnBluetooth() {
107128
val bluetooth =
108129
appContext.applicationContext.getSystemService(BluetoothManager::class.java)?.adapter
109130
val btEnabled = bluetooth?.isEnabled == true
110131
_bluetoothDisabledState.value = !btEnabled
111-
updateGeneralAirgap(btEnabled)
132+
updateGeneralAirgapState()
133+
}
134+
135+
private fun reactOnUsb(usbIntent: Intent) {
136+
if (FeatureFlags.isEnabled(FeatureOption.SKIP_USB_CHECK)) {
137+
_usbDisconnected.value = false
138+
updateGeneralAirgapState()
139+
return
140+
}
141+
142+
when (usbIntent.extras?.getBoolean("connected")) {
143+
true -> {
144+
_usbDisconnected.value = false
145+
updateGeneralAirgapState()
146+
}
147+
false -> {
148+
_usbDisconnected.value = true
149+
updateGeneralAirgapState()
150+
}
151+
null -> {
152+
Log.d("USB", "usb action intent doesn't have connection state")
153+
}
154+
}
112155
}
113156

114157
private fun registerWifiBroadcastReceiver() {
@@ -119,14 +162,15 @@ class NetworkExposedStateKeeper(
119162
}
120163
}
121164
appContext.registerReceiver(receiver, intentFilter)
165+
reactOnWifiAwareState()
122166
}
123167

124168
private fun reactOnWifiAwareState() {
125169
val wifi =
126170
appContext.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager?
127171
val wifiEnabled = wifi?.isWifiEnabled == true
128172
_wifiDisabledState.value = !wifiEnabled
129-
updateGeneralAirgap(wifiEnabled)
173+
updateGeneralAirgapState()
130174
}
131175

132176
/**

android/src/main/java/io/parity/signer/screens/initial/eachstartchecks/airgap/AirGapViewModel.kt

Lines changed: 39 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
11
package io.parity.signer.screens.initial.eachstartchecks.airgap
22

3+
import android.content.Context
4+
import android.provider.Settings
35
import androidx.lifecycle.ViewModel
46
import androidx.lifecycle.viewModelScope
57
import io.parity.signer.dependencygraph.ServiceLocator
8+
import io.parity.signer.domain.FeatureFlags
9+
import io.parity.signer.domain.FeatureOption
10+
import io.parity.signer.domain.NetworkExposedStateKeeper
611
import io.parity.signer.domain.NetworkState
712
import kotlinx.coroutines.CoroutineScope
813
import kotlinx.coroutines.Job
914
import kotlinx.coroutines.cancel
15+
import kotlinx.coroutines.delay
1016
import kotlinx.coroutines.flow.MutableStateFlow
1117
import kotlinx.coroutines.flow.StateFlow
1218
import kotlinx.coroutines.flow.asStateFlow
@@ -16,47 +22,69 @@ import kotlinx.coroutines.launch
1622

1723
class AirGapViewModel : ViewModel() {
1824

19-
private val networkExposedStateKeeper =
25+
private val appContext = ServiceLocator.appContext
26+
private val networkExposedStateKeeper: NetworkExposedStateKeeper =
2027
ServiceLocator.networkExposedStateKeeper
2128

2229
private val _state = MutableStateFlow<AirGapScreenState>(
2330
AirGapScreenState(
2431
airplaneModeEnabled = false,
2532
wifiDisabled = false,
26-
bluetoothDisabled = false
33+
bluetoothDisabled = false,
34+
isAdbDisabled = false,
35+
isUsbDisconnected = false,
2736
)
2837
)
2938
val state: StateFlow<AirGapScreenState> = _state.asStateFlow()
3039

3140
var scope: CoroutineScope? = null
3241

33-
fun onCableCheckboxClicked() {
34-
_state.update { it.copy(cablesDisconnected = !_state.value.cablesDisconnected) }
42+
private fun isAdbEnabled(context: Context): Boolean {
43+
if (FeatureFlags.isEnabled(FeatureOption.SKIP_USB_CHECK)) return false
44+
45+
return Settings.Global.getInt(context.contentResolver,
46+
Settings.Global.ADB_ENABLED, 0
47+
) == 1;
3548
}
3649

3750
fun init() {
3851
val scope = CoroutineScope(viewModelScope.coroutineContext + Job())
3952
scope.launch {
40-
networkExposedStateKeeper.airPlaneModeEnabled.collect {
41-
_state.value = _state.value.copy(airplaneModeEnabled = (it != false))
53+
networkExposedStateKeeper.airPlaneModeEnabled.collect { newState ->
54+
_state.update {it.copy(airplaneModeEnabled = (newState != false)) }
55+
}
56+
}
57+
scope.launch {
58+
networkExposedStateKeeper.bluetoothDisabledState.collect { newState ->
59+
_state.update { it.copy(bluetoothDisabled = (newState != false)) }
60+
}
61+
}
62+
scope.launch {
63+
networkExposedStateKeeper.wifiDisabledState.collect { newState ->
64+
_state.update { it.copy(wifiDisabled = (newState != false)) }
4265
}
4366
}
4467
scope.launch {
45-
networkExposedStateKeeper.bluetoothDisabledState.collect {
46-
_state.value = _state.value.copy(bluetoothDisabled = (it != false))
68+
networkExposedStateKeeper.usbDisconnected.collect { newState ->
69+
_state.update { it.copy(isUsbDisconnected = (newState != false)) }
4770
}
4871
}
4972
scope.launch {
50-
networkExposedStateKeeper.wifiDisabledState.collect {
51-
_state.value = _state.value.copy(wifiDisabled = (it != false))
73+
while (true) {
74+
val adbEnabled = isAdbEnabled(appContext)
75+
if (_state.value.isAdbDisabled == !adbEnabled) {
76+
//skip it's the same
77+
} else {
78+
_state.update { it.copy(isAdbDisabled = (!adbEnabled)) }
79+
}
80+
delay(1000) //1s
5281
}
5382
}
5483
this.scope = scope
5584
}
5685

5786
fun unInit() {
5887
scope?.cancel()
59-
_state.update { it.copy(cablesDisconnected = false) }
6088
}
6189

6290
fun onConfirmedAirgap() {

0 commit comments

Comments
 (0)