Skip to content

Commit

Permalink
feat: adding logic to check usb and adb connection (#2258)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
Dmitry-Borodin authored Jan 16, 2024
1 parent a221c55 commit ec41b6a
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 112 deletions.
2 changes: 2 additions & 0 deletions android/src/main/java/io/parity/signer/domain/FeatureFlags.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ object FeatureFlags {
FeatureOption.SKIP_ROOTED_CHECK_EMULATOR -> false
FeatureOption.EXPORT_SECRET_KEY -> false //unused
FeatureOption.FAIL_DB_VERSION_CHECK -> false
FeatureOption.SKIP_USB_CHECK -> true

}
}
Expand All @@ -24,6 +25,7 @@ enum class FeatureOption {
FAIL_DB_VERSION_CHECK,
SKIP_UNLOCK_FOR_DEVELOPMENT,
SKIP_ROOTED_CHECK_EMULATOR,
SKIP_USB_CHECK,
EXPORT_SECRET_KEY; //unused as sample

fun isEnabled() = FeatureFlags.isEnabled(this)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import android.content.Intent
import android.content.IntentFilter
import android.net.wifi.WifiManager
import android.provider.Settings
import android.util.Log
import io.parity.signer.domain.backend.UniffiInteractor
import io.parity.signer.uniffi.historyAcknowledgeWarnings
import io.parity.signer.uniffi.historyGetWarnings
Expand All @@ -32,17 +33,23 @@ class NetworkExposedStateKeeper(
MutableStateFlow(null)
val bluetoothDisabledState: StateFlow<Boolean?> = _bluetoothDisabledState

private val _usbDisconnected: MutableStateFlow<Boolean?> =
MutableStateFlow(null)
val usbDisconnected: StateFlow<Boolean?> = _usbDisconnected

private val _airGapModeState: MutableStateFlow<NetworkState> =
MutableStateFlow(NetworkState.None)
val airGapModeState: StateFlow<NetworkState> = _airGapModeState

private val isCurentlyBreached: Boolean
get() = airPlaneModeEnabled.value == false || wifiDisabledState.value == false
|| bluetoothDisabledState.value == false || usbDisconnected.value == false

init {
registerAirplaneBroadcastReceiver()
registerWifiBroadcastReceiver()
registerBluetoothBroadcastReceiver()
reactOnAirplaneMode()
reactOnWifiAwareState()
reactOnBluetooth()
registerUsbBroadcastReceiver()
}

/**
Expand All @@ -64,6 +71,7 @@ class NetworkExposedStateKeeper(
}
}
appContext.registerReceiver(receiver, intentFilter)
reactOnAirplaneMode()
}

private fun registerBluetoothBroadcastReceiver() {
Expand All @@ -74,10 +82,23 @@ class NetworkExposedStateKeeper(
}
}
appContext.registerReceiver(receiver, intentFilter)
reactOnBluetooth()
}

private fun updateGeneralAirgap(isBreached: Boolean) {
if (isBreached) {
private fun registerUsbBroadcastReceiver() {
val intentFilter = IntentFilter("android.hardware.usb.action.USB_STATE")
val receiver: BroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
Log.e("TAGG", "usb broadcast")
reactOnUsb(intent)
}
}
val oldIntent = appContext.registerReceiver(receiver, intentFilter)
oldIntent?.let { reactOnUsb(it) }
}

private fun updateGeneralAirgapState() {
if (isCurentlyBreached) {
if (airGapModeState.value != NetworkState.Active) {
_airGapModeState.value = NetworkState.Active
if (appContext.isDbCreatedAndOnboardingPassed()) {
Expand All @@ -100,15 +121,37 @@ class NetworkExposedStateKeeper(
0
) == 0
_airplaneModeEnabled.value = !airplaneModeOff
updateGeneralAirgap(airplaneModeOff)
updateGeneralAirgapState()
}

private fun reactOnBluetooth() {
val bluetooth =
appContext.applicationContext.getSystemService(BluetoothManager::class.java)?.adapter
val btEnabled = bluetooth?.isEnabled == true
_bluetoothDisabledState.value = !btEnabled
updateGeneralAirgap(btEnabled)
updateGeneralAirgapState()
}

private fun reactOnUsb(usbIntent: Intent) {
if (FeatureFlags.isEnabled(FeatureOption.SKIP_USB_CHECK)) {
_usbDisconnected.value = false
updateGeneralAirgapState()
return
}

when (usbIntent.extras?.getBoolean("connected")) {
true -> {
_usbDisconnected.value = false
updateGeneralAirgapState()
}
false -> {
_usbDisconnected.value = true
updateGeneralAirgapState()
}
null -> {
Log.d("USB", "usb action intent doesn't have connection state")
}
}
}

private fun registerWifiBroadcastReceiver() {
Expand All @@ -119,14 +162,15 @@ class NetworkExposedStateKeeper(
}
}
appContext.registerReceiver(receiver, intentFilter)
reactOnWifiAwareState()
}

private fun reactOnWifiAwareState() {
val wifi =
appContext.applicationContext.getSystemService(Context.WIFI_SERVICE) as WifiManager?
val wifiEnabled = wifi?.isWifiEnabled == true
_wifiDisabledState.value = !wifiEnabled
updateGeneralAirgap(wifiEnabled)
updateGeneralAirgapState()
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
package io.parity.signer.screens.initial.eachstartchecks.airgap

import android.content.Context
import android.provider.Settings
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import io.parity.signer.dependencygraph.ServiceLocator
import io.parity.signer.domain.FeatureFlags
import io.parity.signer.domain.FeatureOption
import io.parity.signer.domain.NetworkExposedStateKeeper
import io.parity.signer.domain.NetworkState
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.cancel
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
Expand All @@ -16,47 +22,69 @@ import kotlinx.coroutines.launch

class AirGapViewModel : ViewModel() {

private val networkExposedStateKeeper =
private val appContext = ServiceLocator.appContext
private val networkExposedStateKeeper: NetworkExposedStateKeeper =
ServiceLocator.networkExposedStateKeeper

private val _state = MutableStateFlow<AirGapScreenState>(
AirGapScreenState(
airplaneModeEnabled = false,
wifiDisabled = false,
bluetoothDisabled = false
bluetoothDisabled = false,
isAdbDisabled = false,
isUsbDisconnected = false,
)
)
val state: StateFlow<AirGapScreenState> = _state.asStateFlow()

var scope: CoroutineScope? = null

fun onCableCheckboxClicked() {
_state.update { it.copy(cablesDisconnected = !_state.value.cablesDisconnected) }
private fun isAdbEnabled(context: Context): Boolean {
if (FeatureFlags.isEnabled(FeatureOption.SKIP_USB_CHECK)) return false

return Settings.Global.getInt(context.contentResolver,
Settings.Global.ADB_ENABLED, 0
) == 1;
}

fun init() {
val scope = CoroutineScope(viewModelScope.coroutineContext + Job())
scope.launch {
networkExposedStateKeeper.airPlaneModeEnabled.collect {
_state.value = _state.value.copy(airplaneModeEnabled = (it != false))
networkExposedStateKeeper.airPlaneModeEnabled.collect { newState ->
_state.update {it.copy(airplaneModeEnabled = (newState != false)) }
}
}
scope.launch {
networkExposedStateKeeper.bluetoothDisabledState.collect { newState ->
_state.update { it.copy(bluetoothDisabled = (newState != false)) }
}
}
scope.launch {
networkExposedStateKeeper.wifiDisabledState.collect { newState ->
_state.update { it.copy(wifiDisabled = (newState != false)) }
}
}
scope.launch {
networkExposedStateKeeper.bluetoothDisabledState.collect {
_state.value = _state.value.copy(bluetoothDisabled = (it != false))
networkExposedStateKeeper.usbDisconnected.collect { newState ->
_state.update { it.copy(isUsbDisconnected = (newState != false)) }
}
}
scope.launch {
networkExposedStateKeeper.wifiDisabledState.collect {
_state.value = _state.value.copy(wifiDisabled = (it != false))
while (true) {
val adbEnabled = isAdbEnabled(appContext)
if (_state.value.isAdbDisabled == !adbEnabled) {
//skip it's the same
} else {
_state.update { it.copy(isAdbDisabled = (!adbEnabled)) }
}
delay(1000) //1s
}
}
this.scope = scope
}

fun unInit() {
scope?.cancel()
_state.update { it.copy(cablesDisconnected = false) }
}

fun onConfirmedAirgap() {
Expand Down
Loading

0 comments on commit ec41b6a

Please sign in to comment.