Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: adding logic to check usb and adb connection #2258

Merged
merged 16 commits into from
Jan 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading