Skip to content

Commit

Permalink
Merge branch 'master' into android-disable-biometric
Browse files Browse the repository at this point in the history
  • Loading branch information
prybalko authored Jan 16, 2024
2 parents c21c962 + ec41b6a commit 4c4558b
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 4c4558b

Please sign in to comment.