[Status bar refactor] Add Roaming to the mobile pipeline
Copied the logic exactly as it is from the old pipeline. Roaming is defined as: True if (!carrierNetworkChange && ((isGsm && serviceState.roaming) || (!isGsm && getCdmaEnhancedRoamingIndicatorDisplayNumber != ERI_OFF))) Test: MobileConnectionRepositoryTest Test: DemoMobileConnectionsRepositoryTest Test: MobileIconInteractorTest Test: MobileIconViewModelTest Test: manual in demo mode Bug: 238425913 Change-Id: If8b9b46e44175c9865ad396023f9f5e49b2105a8
This commit is contained in:
@@ -41,6 +41,7 @@ import com.android.systemui.statusbar.pipeline.mobile.data.model.DataConnectionS
|
||||
data class MobileConnectionModel(
|
||||
/** From [ServiceStateListener.onServiceStateChanged] */
|
||||
val isEmergencyOnly: Boolean = false,
|
||||
val isRoaming: Boolean = false,
|
||||
|
||||
/** From [SignalStrengthsListener.onSignalStrengthsChanged] */
|
||||
val isGsm: Boolean = false,
|
||||
|
||||
@@ -50,4 +50,12 @@ interface MobileConnectionRepository {
|
||||
* [SubscriptionManager.getDefaultDataSubscriptionId]
|
||||
*/
|
||||
val isDefaultDataSubscription: StateFlow<Boolean>
|
||||
|
||||
/**
|
||||
* See [TelephonyManager.getCdmaEnhancedRoamingIndicatorDisplayNumber]. This bit only matters if
|
||||
* the connection type is CDMA.
|
||||
*
|
||||
* True if the Enhanced Roaming Indicator (ERI) display number is not [TelephonyManager.ERI_OFF]
|
||||
*/
|
||||
val cdmaRoaming: StateFlow<Boolean>
|
||||
}
|
||||
|
||||
@@ -186,6 +186,7 @@ constructor(
|
||||
connection.dataEnabled.value = true
|
||||
connection.isDefaultDataSubscription.value = state.dataType != null
|
||||
|
||||
connection.cdmaRoaming.value = state.roaming
|
||||
connection.connectionInfo.value = state.toMobileConnectionModel()
|
||||
}
|
||||
|
||||
@@ -229,6 +230,7 @@ constructor(
|
||||
private fun Mobile.toMobileConnectionModel(): MobileConnectionModel {
|
||||
return MobileConnectionModel(
|
||||
isEmergencyOnly = false, // TODO(b/261029387): not yet supported
|
||||
isRoaming = roaming,
|
||||
isGsm = false, // TODO(b/261029387): not yet supported
|
||||
cdmaLevel = level ?: 0,
|
||||
primaryLevel = level ?: 0,
|
||||
@@ -260,4 +262,6 @@ class DemoMobileConnectionRepository(override val subId: Int) : MobileConnection
|
||||
override val dataEnabled = MutableStateFlow(true)
|
||||
|
||||
override val isDefaultDataSubscription = MutableStateFlow(true)
|
||||
|
||||
override val cdmaRoaming = MutableStateFlow(false)
|
||||
}
|
||||
|
||||
@@ -98,6 +98,7 @@ constructor(
|
||||
val inflateStrength = getString("inflate")?.toBoolean()
|
||||
val activity = getString("activity")?.toActivity()
|
||||
val carrierNetworkChange = getString("carriernetworkchange") == "show"
|
||||
val roaming = getString("roam") == "show"
|
||||
|
||||
return Mobile(
|
||||
level = level,
|
||||
@@ -107,6 +108,7 @@ constructor(
|
||||
inflateStrength = inflateStrength,
|
||||
activity = activity,
|
||||
carrierNetworkChange = carrierNetworkChange,
|
||||
roaming = roaming,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,6 +34,7 @@ sealed interface FakeNetworkEventModel {
|
||||
val inflateStrength: Boolean?,
|
||||
@DataActivityType val activity: Int?,
|
||||
val carrierNetworkChange: Boolean,
|
||||
val roaming: Boolean,
|
||||
) : FakeNetworkEventModel
|
||||
|
||||
data class MobileDisabled(
|
||||
|
||||
@@ -27,6 +27,7 @@ import android.telephony.TelephonyCallback
|
||||
import android.telephony.TelephonyDisplayInfo
|
||||
import android.telephony.TelephonyDisplayInfo.OVERRIDE_NETWORK_TYPE_NONE
|
||||
import android.telephony.TelephonyManager
|
||||
import android.telephony.TelephonyManager.ERI_OFF
|
||||
import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
|
||||
import com.android.systemui.common.coroutine.ConflatedCallbackFlow.conflatedCallbackFlow
|
||||
import com.android.systemui.dagger.qualifiers.Application
|
||||
@@ -95,7 +96,11 @@ class MobileConnectionRepositoryImpl(
|
||||
TelephonyCallback.CarrierNetworkListener,
|
||||
TelephonyCallback.DisplayInfoListener {
|
||||
override fun onServiceStateChanged(serviceState: ServiceState) {
|
||||
state = state.copy(isEmergencyOnly = serviceState.isEmergencyOnly)
|
||||
state =
|
||||
state.copy(
|
||||
isEmergencyOnly = serviceState.isEmergencyOnly,
|
||||
isRoaming = serviceState.roaming,
|
||||
)
|
||||
trySend(state)
|
||||
}
|
||||
|
||||
@@ -208,6 +213,11 @@ class MobileConnectionRepositoryImpl(
|
||||
globalMobileDataSettingChangedEvent,
|
||||
)
|
||||
|
||||
override val cdmaRoaming: StateFlow<Boolean> =
|
||||
telephonyPollingEvent
|
||||
.mapLatest { telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber != ERI_OFF }
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
|
||||
|
||||
override val dataEnabled: StateFlow<Boolean> =
|
||||
telephonyPollingEvent
|
||||
.mapLatest { dataConnectionAllowed() }
|
||||
|
||||
@@ -54,6 +54,13 @@ interface MobileIconInteractor {
|
||||
/** True if this line of service is emergency-only */
|
||||
val isEmergencyOnly: StateFlow<Boolean>
|
||||
|
||||
/**
|
||||
* True if this connection is considered roaming. The roaming bit can come from [ServiceState],
|
||||
* or directly from the telephony manager's CDMA ERI number value. Note that we don't consider a
|
||||
* connection to be roaming while carrier network change is active
|
||||
*/
|
||||
val isRoaming: StateFlow<Boolean>
|
||||
|
||||
/** Int describing the connection strength. 0-4 OR 1-5. See [numberOfLevels] */
|
||||
val level: StateFlow<Int>
|
||||
|
||||
@@ -95,6 +102,18 @@ class MobileIconInteractorImpl(
|
||||
.mapLatest { it.isEmergencyOnly }
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
|
||||
|
||||
override val isRoaming: StateFlow<Boolean> =
|
||||
combine(connectionInfo, connectionRepository.cdmaRoaming) { connection, cdmaRoaming ->
|
||||
if (connection.carrierNetworkChangeActive) {
|
||||
false
|
||||
} else if (connection.isGsm) {
|
||||
connection.isRoaming
|
||||
} else {
|
||||
cdmaRoaming
|
||||
}
|
||||
}
|
||||
.stateIn(scope, SharingStarted.WhileSubscribed(), false)
|
||||
|
||||
override val level: StateFlow<Int> =
|
||||
connectionInfo
|
||||
.mapLatest { connection ->
|
||||
|
||||
@@ -21,6 +21,7 @@ import android.view.View.GONE
|
||||
import android.view.View.VISIBLE
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.Space
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
@@ -29,7 +30,6 @@ import com.android.systemui.R
|
||||
import com.android.systemui.common.ui.binder.IconViewBinder
|
||||
import com.android.systemui.lifecycle.repeatWhenAttached
|
||||
import com.android.systemui.statusbar.pipeline.mobile.ui.viewmodel.MobileIconViewModel
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@@ -43,6 +43,8 @@ object MobileIconBinder {
|
||||
val networkTypeView = view.requireViewById<ImageView>(R.id.mobile_type)
|
||||
val iconView = view.requireViewById<ImageView>(R.id.mobile_signal)
|
||||
val mobileDrawable = SignalDrawable(view.context).also { iconView.setImageDrawable(it) }
|
||||
val roamingView = view.requireViewById<ImageView>(R.id.mobile_roaming)
|
||||
val roamingSpace = view.requireViewById<Space>(R.id.mobile_roaming_space)
|
||||
|
||||
view.isVisible = true
|
||||
iconView.isVisible = true
|
||||
@@ -64,12 +66,21 @@ object MobileIconBinder {
|
||||
}
|
||||
}
|
||||
|
||||
// Set the roaming indicator
|
||||
launch {
|
||||
viewModel.roaming.distinctUntilChanged().collect { isRoaming ->
|
||||
roamingView.isVisible = isRoaming
|
||||
roamingSpace.isVisible = isRoaming
|
||||
}
|
||||
}
|
||||
|
||||
// Set the tint
|
||||
launch {
|
||||
viewModel.tint.collect { tint ->
|
||||
val tintList = ColorStateList.valueOf(tint)
|
||||
iconView.imageTintList = tintList
|
||||
networkTypeView.imageTintList = tintList
|
||||
roamingView.imageTintList = tintList
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,5 +87,7 @@ constructor(
|
||||
}
|
||||
}
|
||||
|
||||
val roaming: Flow<Boolean> = iconInteractor.isRoaming
|
||||
|
||||
val tint: Flow<Int> = flowOf(Color.CYAN)
|
||||
}
|
||||
|
||||
@@ -30,6 +30,8 @@ class FakeMobileConnectionRepository(override val subId: Int) : MobileConnection
|
||||
private val _isDefaultDataSubscription = MutableStateFlow(true)
|
||||
override val isDefaultDataSubscription = _isDefaultDataSubscription
|
||||
|
||||
override val cdmaRoaming = MutableStateFlow(false)
|
||||
|
||||
fun setConnectionInfo(model: MobileConnectionModel) {
|
||||
_connectionInfo.value = model
|
||||
}
|
||||
|
||||
@@ -95,6 +95,7 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC
|
||||
inflateStrength = testCase.inflateStrength,
|
||||
activity = testCase.activity,
|
||||
carrierNetworkChange = testCase.carrierNetworkChange,
|
||||
roaming = testCase.roaming,
|
||||
)
|
||||
|
||||
fakeNetworkEventFlow.value = networkModel
|
||||
@@ -116,6 +117,7 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC
|
||||
assertThat(connectionInfo.dataActivityDirection).isEqualTo(model.activity)
|
||||
assertThat(connectionInfo.carrierNetworkChangeActive)
|
||||
.isEqualTo(model.carrierNetworkChange)
|
||||
assertThat(connectionInfo.isRoaming).isEqualTo(model.roaming)
|
||||
|
||||
// TODO(b/261029387): check these once we start handling them
|
||||
assertThat(connectionInfo.isEmergencyOnly).isFalse()
|
||||
@@ -138,6 +140,7 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC
|
||||
val inflateStrength: Boolean,
|
||||
@Annotation.DataActivityType val activity: Int,
|
||||
val carrierNetworkChange: Boolean,
|
||||
val roaming: Boolean,
|
||||
) {
|
||||
override fun toString(): String {
|
||||
return "INPUT(level=$level, " +
|
||||
@@ -146,7 +149,8 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC
|
||||
"carrierId=$carrierId, " +
|
||||
"inflateStrength=$inflateStrength, " +
|
||||
"activity=$activity, " +
|
||||
"carrierNetworkChange=$carrierNetworkChange)"
|
||||
"carrierNetworkChange=$carrierNetworkChange, " +
|
||||
"roaming=$roaming)"
|
||||
}
|
||||
|
||||
// Convenience for iterating test data and creating new cases
|
||||
@@ -158,6 +162,7 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC
|
||||
inflateStrength: Boolean? = null,
|
||||
@Annotation.DataActivityType activity: Int? = null,
|
||||
carrierNetworkChange: Boolean? = null,
|
||||
roaming: Boolean? = null,
|
||||
): TestCase =
|
||||
TestCase(
|
||||
level = level ?: this.level,
|
||||
@@ -166,7 +171,8 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC
|
||||
carrierId = carrierId ?: this.carrierId,
|
||||
inflateStrength = inflateStrength ?: this.inflateStrength,
|
||||
activity = activity ?: this.activity,
|
||||
carrierNetworkChange = carrierNetworkChange ?: this.carrierNetworkChange
|
||||
carrierNetworkChange = carrierNetworkChange ?: this.carrierNetworkChange,
|
||||
roaming = roaming ?: this.roaming,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -193,6 +199,8 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC
|
||||
TelephonyManager.DATA_ACTIVITY_INOUT
|
||||
)
|
||||
private val carrierNetworkChange = booleanList
|
||||
// false first so the base case doesn't have roaming set (more common)
|
||||
private val roaming = listOf(false, true)
|
||||
|
||||
@Parameters(name = "{0}") @JvmStatic fun data() = testData()
|
||||
|
||||
@@ -226,7 +234,8 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC
|
||||
carrierIds.first(),
|
||||
inflateStrength.first(),
|
||||
activity.first(),
|
||||
carrierNetworkChange.first()
|
||||
carrierNetworkChange.first(),
|
||||
roaming.first(),
|
||||
)
|
||||
|
||||
val tail =
|
||||
@@ -237,6 +246,7 @@ internal class DemoMobileConnectionParameterizedTest(private val testCase: TestC
|
||||
inflateStrength.map { baseCase.modifiedBy(inflateStrength = it) },
|
||||
activity.map { baseCase.modifiedBy(activity = it) },
|
||||
carrierNetworkChange.map { baseCase.modifiedBy(carrierNetworkChange = it) },
|
||||
roaming.map { baseCase.modifiedBy(roaming = it) }
|
||||
)
|
||||
.flatten()
|
||||
|
||||
|
||||
@@ -292,6 +292,7 @@ class DemoMobileConnectionsRepositoryTest : SysuiTestCase() {
|
||||
assertThat(connectionInfo.dataActivityDirection).isEqualTo(model.activity)
|
||||
assertThat(connectionInfo.carrierNetworkChangeActive)
|
||||
.isEqualTo(model.carrierNetworkChange)
|
||||
assertThat(connectionInfo.isRoaming).isEqualTo(model.roaming)
|
||||
|
||||
// TODO(b/261029387) check these once we start handling them
|
||||
assertThat(connectionInfo.isEmergencyOnly).isFalse()
|
||||
@@ -313,6 +314,7 @@ fun validMobileEvent(
|
||||
inflateStrength: Boolean? = false,
|
||||
activity: Int? = null,
|
||||
carrierNetworkChange: Boolean = false,
|
||||
roaming: Boolean = false,
|
||||
): FakeNetworkEventModel =
|
||||
FakeNetworkEventModel.Mobile(
|
||||
level = level,
|
||||
@@ -322,4 +324,5 @@ fun validMobileEvent(
|
||||
inflateStrength = inflateStrength,
|
||||
activity = activity,
|
||||
carrierNetworkChange = carrierNetworkChange,
|
||||
roaming = roaming,
|
||||
)
|
||||
|
||||
@@ -32,6 +32,8 @@ import android.telephony.TelephonyManager.DATA_CONNECTING
|
||||
import android.telephony.TelephonyManager.DATA_DISCONNECTED
|
||||
import android.telephony.TelephonyManager.DATA_DISCONNECTING
|
||||
import android.telephony.TelephonyManager.DATA_UNKNOWN
|
||||
import android.telephony.TelephonyManager.ERI_OFF
|
||||
import android.telephony.TelephonyManager.ERI_ON
|
||||
import android.telephony.TelephonyManager.NETWORK_TYPE_LTE
|
||||
import android.telephony.TelephonyManager.NETWORK_TYPE_UNKNOWN
|
||||
import androidx.test.filters.SmallTest
|
||||
@@ -402,6 +404,61 @@ class MobileConnectionRepositoryTest : SysuiTestCase() {
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `roaming - cdma - queries telephony manager`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
var latest: Boolean? = null
|
||||
// Start the telephony collection job so that cdmaRoaming starts updating
|
||||
val telephonyJob = underTest.connectionInfo.launchIn(this)
|
||||
val job = underTest.cdmaRoaming.onEach { latest = it }.launchIn(this)
|
||||
|
||||
val cb = getTelephonyCallbackForType<ServiceStateListener>()
|
||||
|
||||
val serviceState = ServiceState()
|
||||
serviceState.roaming = false
|
||||
|
||||
// CDMA roaming is off, GSM roaming is off
|
||||
whenever(telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber).thenReturn(ERI_OFF)
|
||||
cb.onServiceStateChanged(serviceState)
|
||||
|
||||
assertThat(latest).isFalse()
|
||||
|
||||
// CDMA roaming is off, GSM roaming is on
|
||||
whenever(telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber).thenReturn(ERI_ON)
|
||||
cb.onServiceStateChanged(serviceState)
|
||||
|
||||
assertThat(latest).isTrue()
|
||||
|
||||
telephonyJob.cancel()
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `roaming - gsm - queries service state`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.connectionInfo.onEach { latest = it.isRoaming }.launchIn(this)
|
||||
|
||||
val serviceState = ServiceState()
|
||||
serviceState.roaming = false
|
||||
|
||||
val cb = getTelephonyCallbackForType<ServiceStateListener>()
|
||||
|
||||
// CDMA roaming is off, GSM roaming is off
|
||||
whenever(telephonyManager.cdmaEnhancedRoamingIndicatorDisplayNumber).thenReturn(ERI_OFF)
|
||||
cb.onServiceStateChanged(serviceState)
|
||||
|
||||
assertThat(latest).isFalse()
|
||||
|
||||
// CDMA roaming is off, GSM roaming is on
|
||||
serviceState.roaming = true
|
||||
cb.onServiceStateChanged(serviceState)
|
||||
|
||||
assertThat(latest).isTrue()
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
private fun getTelephonyCallbacks(): List<TelephonyCallback> {
|
||||
val callbackCaptor = argumentCaptor<TelephonyCallback>()
|
||||
Mockito.verify(telephonyManager).registerTelephonyCallback(any(), callbackCaptor.capture())
|
||||
|
||||
@@ -30,6 +30,8 @@ class FakeMobileIconInteractor : MobileIconInteractor {
|
||||
private val _isEmergencyOnly = MutableStateFlow(false)
|
||||
override val isEmergencyOnly = _isEmergencyOnly
|
||||
|
||||
override val isRoaming = MutableStateFlow(false)
|
||||
|
||||
private val _isFailedConnection = MutableStateFlow(false)
|
||||
override val isDefaultConnectionFailed = _isFailedConnection
|
||||
|
||||
|
||||
@@ -298,6 +298,100 @@ class MobileIconInteractorTest : SysuiTestCase() {
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `roaming - is gsm - uses connection model`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.isRoaming.onEach { latest = it }.launchIn(this)
|
||||
|
||||
connectionRepository.cdmaRoaming.value = true
|
||||
connectionRepository.setConnectionInfo(
|
||||
MobileConnectionModel(
|
||||
isGsm = true,
|
||||
isRoaming = false,
|
||||
)
|
||||
)
|
||||
yield()
|
||||
|
||||
assertThat(latest).isFalse()
|
||||
|
||||
connectionRepository.setConnectionInfo(
|
||||
MobileConnectionModel(
|
||||
isGsm = true,
|
||||
isRoaming = true,
|
||||
)
|
||||
)
|
||||
yield()
|
||||
|
||||
assertThat(latest).isTrue()
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `roaming - is cdma - uses cdma roaming bit`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.isRoaming.onEach { latest = it }.launchIn(this)
|
||||
|
||||
connectionRepository.cdmaRoaming.value = false
|
||||
connectionRepository.setConnectionInfo(
|
||||
MobileConnectionModel(
|
||||
isGsm = false,
|
||||
isRoaming = true,
|
||||
)
|
||||
)
|
||||
yield()
|
||||
|
||||
assertThat(latest).isFalse()
|
||||
|
||||
connectionRepository.cdmaRoaming.value = true
|
||||
connectionRepository.setConnectionInfo(
|
||||
MobileConnectionModel(
|
||||
isGsm = false,
|
||||
isRoaming = false,
|
||||
)
|
||||
)
|
||||
yield()
|
||||
|
||||
assertThat(latest).isTrue()
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `roaming - false while carrierNetworkChangeActive`() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.isRoaming.onEach { latest = it }.launchIn(this)
|
||||
|
||||
connectionRepository.cdmaRoaming.value = true
|
||||
connectionRepository.setConnectionInfo(
|
||||
MobileConnectionModel(
|
||||
isGsm = false,
|
||||
isRoaming = true,
|
||||
carrierNetworkChangeActive = true,
|
||||
)
|
||||
)
|
||||
yield()
|
||||
|
||||
assertThat(latest).isFalse()
|
||||
|
||||
connectionRepository.cdmaRoaming.value = true
|
||||
connectionRepository.setConnectionInfo(
|
||||
MobileConnectionModel(
|
||||
isGsm = true,
|
||||
isRoaming = true,
|
||||
carrierNetworkChangeActive = true,
|
||||
)
|
||||
)
|
||||
yield()
|
||||
|
||||
assertThat(latest).isFalse()
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val IMMEDIATE = Dispatchers.Main.immediate
|
||||
|
||||
|
||||
@@ -234,6 +234,22 @@ class MobileIconViewModelTest : SysuiTestCase() {
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun roaming() =
|
||||
runBlocking(IMMEDIATE) {
|
||||
interactor.isRoaming.value = true
|
||||
var latest: Boolean? = null
|
||||
val job = underTest.roaming.onEach { latest = it }.launchIn(this)
|
||||
|
||||
assertThat(latest).isTrue()
|
||||
|
||||
interactor.isRoaming.value = false
|
||||
|
||||
assertThat(latest).isFalse()
|
||||
|
||||
job.cancel()
|
||||
}
|
||||
|
||||
/** Convenience constructor for these tests */
|
||||
private fun defaultSignal(
|
||||
level: Int = 1,
|
||||
|
||||
Reference in New Issue
Block a user