diff --git a/app-common/src/main/java/im/angry/openeuicc/core/DefaultEuiccChannelFactory.kt b/app-common/src/main/java/im/angry/openeuicc/core/DefaultEuiccChannelFactory.kt index 5e87564..446611d 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/DefaultEuiccChannelFactory.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/DefaultEuiccChannelFactory.kt @@ -10,7 +10,11 @@ import im.angry.openeuicc.common.R import im.angry.openeuicc.core.usb.UsbApduInterface import im.angry.openeuicc.core.usb.getIoEndpoints import im.angry.openeuicc.util.* +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.runBlocking import java.lang.IllegalArgumentException +import kotlin.math.max +import kotlin.math.min open class DefaultEuiccChannelFactory(protected val context: Context) : EuiccChannelFactory { private var seService: SEService? = null @@ -33,21 +37,23 @@ open class DefaultEuiccChannelFactory(protected val context: Context) : EuiccCha ensureSEService() Log.i(DefaultEuiccChannelManager.TAG, "Trying OMAPI for physical slot ${port.card.physicalSlotIndex}") + + val verboseLoggingFlow = context.preferenceRepository.verboseLoggingFlow + val ignoreTLSCertificateFlow = context.preferenceRepository.ignoreTLSCertificateFlow + val maxSegmentSizeFlow = context.preferenceRepository.maxSegmentSizeFlow + try { return EuiccChannelImpl( context.getString(R.string.omapi), port, intrinsicChannelName = null, - OmapiApduInterface( - seService!!, - port, - context.preferenceRepository.verboseLoggingFlow - ), - context.preferenceRepository.verboseLoggingFlow, - context.preferenceRepository.ignoreTLSCertificateFlow, + OmapiApduInterface(seService!!, port, verboseLoggingFlow), + verboseLoggingFlow, + ignoreTLSCertificateFlow, ).also { - Log.i(DefaultEuiccChannelManager.TAG, "Is OMAPI channel, setting MSS to 60") - it.lpa.setEs10xMss(60) + val mss = runBlocking { maxSegmentSizeFlow.first() }.toByte() + Log.i(DefaultEuiccChannelManager.TAG, "Is OMAPI channel, setting MSS to $mss") + it.lpa.setEs10xMss(mss) } } catch (e: IllegalArgumentException) { // Failed diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/SettingsFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/SettingsFragment.kt index fab680f..8f8c6b3 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/SettingsFragment.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/SettingsFragment.kt @@ -5,21 +5,26 @@ import android.net.Uri import android.os.Build import android.os.Bundle import android.provider.Settings +import android.text.InputType import android.widget.Toast import androidx.lifecycle.lifecycleScope import androidx.preference.CheckBoxPreference +import androidx.preference.EditTextPreference import androidx.preference.Preference import androidx.preference.PreferenceCategory import androidx.preference.PreferenceFragmentCompat import im.angry.openeuicc.common.R import im.angry.openeuicc.util.* import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch import kotlinx.coroutines.runBlocking open class SettingsFragment: PreferenceFragmentCompat() { - private lateinit var developerPref: PreferenceCategory + private val developerPref by lazy { + requirePreference("pref_developer") + } // Hidden developer options switch private var numClicks = 0 @@ -29,10 +34,8 @@ open class SettingsFragment: PreferenceFragmentCompat() { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { setPreferencesFromResource(R.xml.pref_settings, rootKey) - developerPref = requirePreference("pref_developer") - - // Show / hide developer preference based on whether it is enabled lifecycleScope.launch { + // Show / hide developer preference based on whether it is enabled preferenceRepository.developerOptionsEnabledFlow .onEach { developerPref.isVisible = it } .collect() @@ -57,6 +60,36 @@ open class SettingsFragment: PreferenceFragmentCompat() { intent = Intent(requireContext(), LogsActivity::class.java) } + findPreference("pref_developer_max_segment_size")?.apply { + val mssFlow = preferenceRepository.maxSegmentSizeFlow + + lifecycleScope.launch { + mssFlow.onEach { text = it.toString() }.collect() + } + + setOnPreferenceChangeListener { _, newValue -> + // SGP.22 v2.2.2, 2.5.5 Segmented Bound Profile Package (Page 33 of 268) + // https://www.gsma.com/solutions-and-impact/technologies/esim/wp-content/uploads/2020/06/SGP.22-v2.2.2.pdf#page=33 + // + // Each segment of this list that is up to 255 bytes is transported in one APDU. + // Larger TLVs are sent in blocks of 255 bytes for the first blocks and a last block that MAY be shorter. + text = runBlocking { + val value = newValue as String + when { + value.isEmpty() -> mssFlow.removePreference() + value.toInt() in 32..255 -> mssFlow.updatePreference(value.toInt()) + } + mssFlow.first().toString() + } + false + } + + setOnBindEditTextListener { + it.inputType = InputType.TYPE_CLASS_NUMBER + it.selectAll() + } + } + requirePreference("pref_notifications_download") .bindBooleanFlow(preferenceRepository.notificationDownloadFlow) @@ -136,8 +169,8 @@ open class SettingsFragment: PreferenceFragmentCompat() { } protected fun mergePreferenceOverlay(overlayKey: String, targetKey: String) { - val overlayCat = requirePreference(overlayKey) - val targetCat = requirePreference(targetKey) + val overlayCat = findPreference(overlayKey)!! + val targetCat = findPreference(targetKey)!! val prefs = buildList { for (i in 0.. bindFlow(key: Preferences.Key, defaultValue: T): PreferenceFlowWrapper = PreferenceFlowWrapper(context, key, defaultValue) @@ -66,6 +69,10 @@ class PreferenceFlowWrapper private constructor( context.dataStore.data.map { it[key] ?: defaultValue } ) + suspend fun removePreference() { + context.dataStore.edit { it.remove(key) } + } + suspend fun updatePreference(value: T) { context.dataStore.edit { it[key] = value } } diff --git a/app-common/src/main/res/values/strings.xml b/app-common/src/main/res/values/strings.xml index 6c9fc34..3974a4b 100644 --- a/app-common/src/main/res/values/strings.xml +++ b/app-common/src/main/res/values/strings.xml @@ -164,6 +164,9 @@ Include non-production profiles in the list Ignore SM-DP+ TLS certificate Accept any TLS certificate used by the RSP server + Max Segment Size + Update Max Segment Size + The valid max segment value range is from 32 to 255. Info App Version Source Code diff --git a/app-common/src/main/res/xml/pref_settings.xml b/app-common/src/main/res/xml/pref_settings.xml index bb5bd50..63ef08a 100644 --- a/app-common/src/main/res/xml/pref_settings.xml +++ b/app-common/src/main/res/xml/pref_settings.xml @@ -69,6 +69,13 @@ app:summary="@string/pref_developer_ignore_tls_certificate_desc" app:title="@string/pref_developer_ignore_tls_certificate" /> + +