WIP: feat: customizable max segment size [NOT MERGE] #124

Closed
septs wants to merge 5 commits from septs:customizable-max-segment-size into master
5 changed files with 71 additions and 15 deletions

View file

@ -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

View file

@ -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<PreferenceCategory>("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<EditTextPreference>("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<CheckBoxPreference>("pref_notifications_download")
.bindBooleanFlow(preferenceRepository.notificationDownloadFlow)
@ -136,8 +169,8 @@ open class SettingsFragment: PreferenceFragmentCompat() {
}
protected fun mergePreferenceOverlay(overlayKey: String, targetKey: String) {
val overlayCat = requirePreference<PreferenceCategory>(overlayKey)
val targetCat = requirePreference<PreferenceCategory>(targetKey)
val overlayCat = findPreference<PreferenceCategory>(overlayKey)!!
val targetCat = findPreference<PreferenceCategory>(targetKey)!!
val prefs = buildList {
for (i in 0..<overlayCat.preferenceCount) {

View file

@ -5,6 +5,7 @@ import androidx.datastore.core.DataStore
import androidx.datastore.preferences.core.Preferences
import androidx.datastore.preferences.core.booleanPreferencesKey
import androidx.datastore.preferences.core.edit
import androidx.datastore.preferences.core.intPreferencesKey
import androidx.datastore.preferences.preferencesDataStore
import androidx.fragment.app.Fragment
import im.angry.openeuicc.OpenEuiccApplication
@ -33,6 +34,7 @@ internal object PreferenceKeys {
val DEVELOPER_OPTIONS_ENABLED = booleanPreferencesKey("developer_options_enabled")
val UNFILTERED_PROFILE_LIST = booleanPreferencesKey("unfiltered_profile_list")
val IGNORE_TLS_CERTIFICATE = booleanPreferencesKey("ignore_tls_certificate")
val MAX_SEGMENT_SIZE = intPreferencesKey("max_segment_size")
}
class PreferenceRepository(private val context: Context) {
@ -50,6 +52,7 @@ class PreferenceRepository(private val context: Context) {
val developerOptionsEnabledFlow = bindFlow(PreferenceKeys.DEVELOPER_OPTIONS_ENABLED, false)
val unfilteredProfileListFlow = bindFlow(PreferenceKeys.UNFILTERED_PROFILE_LIST, false)
val ignoreTLSCertificateFlow = bindFlow(PreferenceKeys.IGNORE_TLS_CERTIFICATE, false)
val maxSegmentSizeFlow = bindFlow(PreferenceKeys.MAX_SEGMENT_SIZE, 60)
private fun <T> bindFlow(key: Preferences.Key<T>, defaultValue: T): PreferenceFlowWrapper<T> =
PreferenceFlowWrapper(context, key, defaultValue)
@ -66,6 +69,10 @@ class PreferenceFlowWrapper<T> 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 }
}

View file

@ -164,6 +164,9 @@
<string name="pref_developer_unfiltered_profile_list_desc">Include non-production profiles in the list</string>
<string name="pref_developer_ignore_tls_certificate">Ignore SM-DP+ TLS certificate</string>
<string name="pref_developer_ignore_tls_certificate_desc">Accept any TLS certificate used by the RSP server</string>
<string name="pref_developer_mss">Max Segment Size</string>
<string name="pref_developer_mss_desc">Update Max Segment Size</string>
<string name="pref_developer_mss_dialog_message">The valid max segment value range is from 32 to 255.</string>
<string name="pref_info">Info</string>
<string name="pref_info_app_version">App Version</string>
<string name="pref_info_source_code">Source Code</string>

View file

@ -69,6 +69,13 @@
app:summary="@string/pref_developer_ignore_tls_certificate_desc"
app:title="@string/pref_developer_ignore_tls_certificate" />
<EditTextPreference
app:dialogMessage="@string/pref_developer_mss_dialog_message"
app:iconSpaceReserved="false"
app:key="pref_developer_max_segment_size"
app:summary="@string/pref_developer_mss_desc"
app:title="@string/pref_developer_mss" />
</PreferenceCategory>
<PreferenceCategory