From a6286ed09765fbc2cd80a5f38c70050cf9cacc50 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sun, 16 Mar 2025 17:32:09 -0400 Subject: [PATCH 1/6] feat: Acquire partial wake lock for all foreground tasks All of our foreground tasks require the CPU to be at least awake to make any progress. We could keep the screen on but we really only need the partial wake lock to make sure progress is made. --- app-common/src/main/AndroidManifest.xml | 1 + .../openeuicc/service/EuiccChannelManagerService.kt | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/app-common/src/main/AndroidManifest.xml b/app-common/src/main/AndroidManifest.xml index 6edaaf1..464ae0f 100644 --- a/app-common/src/main/AndroidManifest.xml +++ b/app-common/src/main/AndroidManifest.xml @@ -7,6 +7,7 @@ + Date: Sun, 16 Mar 2025 17:35:46 -0400 Subject: [PATCH 2/6] We don't need a public wake lock --- .../im/angry/openeuicc/service/EuiccChannelManagerService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/service/EuiccChannelManagerService.kt b/app-common/src/main/java/im/angry/openeuicc/service/EuiccChannelManagerService.kt index a812a47..88c8057 100644 --- a/app-common/src/main/java/im/angry/openeuicc/service/EuiccChannelManagerService.kt +++ b/app-common/src/main/java/im/angry/openeuicc/service/EuiccChannelManagerService.kt @@ -92,7 +92,7 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker { } val euiccChannelManager: EuiccChannelManager by euiccChannelManagerDelegate - val wakeLock: PowerManager.WakeLock by lazy { + private val wakeLock: PowerManager.WakeLock by lazy { (getSystemService(POWER_SERVICE) as PowerManager).run { newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, this::class.simpleName) } From 33d383a3ce8dbe405791c17c6d4e34b67d7c47c8 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sun, 16 Mar 2025 17:54:54 -0400 Subject: [PATCH 3/6] ui: wizard: Keep screen on during the download process --- .../openeuicc/ui/wizard/DownloadWizardActivity.kt | 11 +++++++++++ .../ui/wizard/DownloadWizardProgressFragment.kt | 3 +++ 2 files changed, 14 insertions(+) diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/wizard/DownloadWizardActivity.kt b/app-common/src/main/java/im/angry/openeuicc/ui/wizard/DownloadWizardActivity.kt index a9f868f..9e312d4 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/wizard/DownloadWizardActivity.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/wizard/DownloadWizardActivity.kt @@ -3,6 +3,7 @@ package im.angry.openeuicc.ui.wizard import android.app.assist.AssistContent import android.os.Bundle import android.view.View +import android.view.WindowManager import android.view.inputmethod.InputMethodManager import android.widget.Button import android.widget.ProgressBar @@ -251,6 +252,14 @@ class DownloadWizardActivity: BaseEuiccAccessActivity() { supportFragmentManager.beginTransaction().setCustomAnimations(enterAnim, exitAnim) .replace(R.id.step_fragment_container, nextFrag) .commit() + + // Sync screen on state + if (nextFrag.keepScreenOn) { + window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + } else { + window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON) + } + refreshButtons() } @@ -280,6 +289,8 @@ class DownloadWizardActivity: BaseEuiccAccessActivity() { protected val state: DownloadWizardState get() = (requireActivity() as DownloadWizardActivity).state + open val keepScreenOn = false + abstract val hasNext: Boolean abstract val hasPrev: Boolean abstract fun createNextFragment(): DownloadWizardStepFragment? diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/wizard/DownloadWizardProgressFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/wizard/DownloadWizardProgressFragment.kt index 1b816d4..342a687 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/wizard/DownloadWizardProgressFragment.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/wizard/DownloadWizardProgressFragment.kt @@ -59,6 +59,9 @@ class DownloadWizardProgressFragment : DownloadWizardActivity.DownloadWizardStep private val adapter = ProgressItemAdapter() + // We don't want to turn off the screen during a download + override val keepScreenOn = true + private var isDone = false override val hasNext: Boolean From 6b169c505d623c186d10283a3ec94501b5f4e5f3 Mon Sep 17 00:00:00 2001 From: septs Date: Mon, 17 Mar 2025 00:01:06 +0100 Subject: [PATCH 4/6] fix: crash (priv) (#177) resolves #178 ``` 1741836445.585 10331 13748 13748 E AndroidRuntime: FATAL EXCEPTION: main 1741836445.585 10331 13748 13748 E AndroidRuntime: Process: im.angry.openeuicc, PID: 13748 1741836445.585 10331 13748 13748 E AndroidRuntime: java.lang.RuntimeException: PrivilegedEuiccContextMarker shall only be used on Fragments or UI types that derive from Context 1741836445.585 10331 13748 13748 E AndroidRuntime: at im.angry.openeuicc.util.PrivilegedEuiccContextMarker$DefaultImpls.getPrivilegedEuiccMarkerContext(PrivilegedUtils.kt:18) ``` Reviewed-on: https://gitea.angry.im/PeterCxy/OpenEUICC/pulls/177 Co-authored-by: septs Co-committed-by: septs --- .../src/main/java/im/angry/openeuicc/util/Utils.kt | 3 +++ .../openeuicc/core/PrivilegedEuiccChannelFactory.kt | 7 +++---- .../java/im/angry/openeuicc/util/PrivilegedUtils.kt | 13 +++---------- 3 files changed, 9 insertions(+), 14 deletions(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/util/Utils.kt b/app-common/src/main/java/im/angry/openeuicc/util/Utils.kt index 5a559f9..046657f 100644 --- a/app-common/src/main/java/im/angry/openeuicc/util/Utils.kt +++ b/app-common/src/main/java/im/angry/openeuicc/util/Utils.kt @@ -54,6 +54,9 @@ interface OpenEuiccContextMarker { val appContainer: AppContainer get() = openEuiccApplication.appContainer + val preferenceRepository: PreferenceRepository + get() = appContainer.preferenceRepository + val telephonyManager: TelephonyManager get() = appContainer.telephonyManager } diff --git a/app/src/main/java/im/angry/openeuicc/core/PrivilegedEuiccChannelFactory.kt b/app/src/main/java/im/angry/openeuicc/core/PrivilegedEuiccChannelFactory.kt index 6dccda9..d9bceda 100644 --- a/app/src/main/java/im/angry/openeuicc/core/PrivilegedEuiccChannelFactory.kt +++ b/app/src/main/java/im/angry/openeuicc/core/PrivilegedEuiccChannelFactory.kt @@ -10,9 +10,8 @@ import java.lang.IllegalArgumentException class PrivilegedEuiccChannelFactory(context: Context) : DefaultEuiccChannelFactory(context), PrivilegedEuiccContextMarker { - private val tm by lazy { - (context.applicationContext as OpenEuiccApplication).appContainer.telephonyManager - } + override val openEuiccMarkerContext: Context + get() = context @Suppress("NAME_SHADOWING") override suspend fun tryOpenEuiccChannel(port: UiccPortInfoCompat): EuiccChannel? { @@ -35,7 +34,7 @@ class PrivilegedEuiccChannelFactory(context: Context) : DefaultEuiccChannelFacto intrinsicChannelName = null, TelephonyManagerApduInterface( port, - tm, + telephonyManager, context.preferenceRepository.verboseLoggingFlow ), context.preferenceRepository.verboseLoggingFlow, diff --git a/app/src/main/java/im/angry/openeuicc/util/PrivilegedUtils.kt b/app/src/main/java/im/angry/openeuicc/util/PrivilegedUtils.kt index 21c8002..6418581 100644 --- a/app/src/main/java/im/angry/openeuicc/util/PrivilegedUtils.kt +++ b/app/src/main/java/im/angry/openeuicc/util/PrivilegedUtils.kt @@ -10,16 +10,9 @@ import java.util.concurrent.Executors import kotlin.coroutines.resume import kotlin.coroutines.suspendCoroutine -interface PrivilegedEuiccContextMarker { - val privilegedEuiccMarkerContext: Context - get() = when (this) { - is Context -> this - is Fragment -> requireContext() - else -> throw RuntimeException("PrivilegedEuiccContextMarker shall only be used on Fragments or UI types that derive from Context") - } - - val preferenceRepository: PrivilegedPreferenceRepository - get() = privilegedEuiccMarkerContext.preferenceRepository as PrivilegedPreferenceRepository +interface PrivilegedEuiccContextMarker : OpenEuiccContextMarker { + override val preferenceRepository: PrivilegedPreferenceRepository + get() = appContainer.preferenceRepository as PrivilegedPreferenceRepository } suspend fun Context.bindServiceSuspended(intent: Intent, flags: Int): Pair Unit> = From e08f8beb45007d6b70fa3a649815f16e760c1a6d Mon Sep 17 00:00:00 2001 From: septs Date: Mon, 17 Mar 2025 00:01:22 +0100 Subject: [PATCH 5/6] feat: add iQOO stk launch support (#179) ![image](/attachments/b2aac119-c488-41e6-a39f-eab8559cd63b) Reviewed-on: https://gitea.angry.im/PeterCxy/OpenEUICC/pulls/179 Co-authored-by: septs Co-committed-by: septs --- app-unpriv/src/main/res/values/sim_toolkit.xml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app-unpriv/src/main/res/values/sim_toolkit.xml b/app-unpriv/src/main/res/values/sim_toolkit.xml index 1f16271..8db9955 100644 --- a/app-unpriv/src/main/res/values/sim_toolkit.xml +++ b/app-unpriv/src/main/res/values/sim_toolkit.xml @@ -5,12 +5,14 @@ com.android.stk/.StkMainHide com.android.stk/.StkListActivity com.android.stk/.StkLauncherListActivity + com.android.stk/.StkSelectionActivity com.android.stk/.StkMain1 com.android.stk/.PrimaryStkMain com.android.stk/.StkLauncherActivity com.android.stk/.StkLauncherActivity_Chn + com.android.stk/.StkLauncherActivity1 com.android.stk/.StkLauncherActivityI com.android.stk/.OppoStkLauncherActivity1 com.android.stk/.OplusStkLauncherActivity1 From dc6b3a4810bd05b25bd5433993cece12a79c5945 Mon Sep 17 00:00:00 2001 From: septs Date: Sun, 16 Mar 2025 21:04:28 -0400 Subject: [PATCH 6/6] feat: support disabling refresh after switch in settings peter: Reworked strings and i18 translations. Also removed the ad-hoc function in favor of a lambda. --- .../service/EuiccChannelManagerService.kt | 37 +++++++++++-------- .../im/angry/openeuicc/ui/SettingsFragment.kt | 3 ++ .../angry/openeuicc/util/PreferenceUtils.kt | 13 +++---- app-common/src/main/res/values-ja/strings.xml | 2 + .../src/main/res/values-zh-rCN/strings.xml | 2 + .../src/main/res/values-zh-rTW/strings.xml | 2 + app-common/src/main/res/values/strings.xml | 2 + app-common/src/main/res/xml/pref_settings.xml | 6 +++ 8 files changed, 44 insertions(+), 23 deletions(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/service/EuiccChannelManagerService.kt b/app-common/src/main/java/im/angry/openeuicc/service/EuiccChannelManagerService.kt index 88c8057..2a24488 100644 --- a/app-common/src/main/java/im/angry/openeuicc/service/EuiccChannelManagerService.kt +++ b/app-common/src/main/java/im/angry/openeuicc/service/EuiccChannelManagerService.kt @@ -12,6 +12,7 @@ import androidx.core.app.NotificationManagerCompat import androidx.lifecycle.LifecycleService import androidx.lifecycle.lifecycleScope import im.angry.openeuicc.common.R +import im.angry.openeuicc.core.EuiccChannel import im.angry.openeuicc.core.EuiccChannelManager import im.angry.openeuicc.util.* import kotlinx.coroutines.Dispatchers @@ -456,30 +457,34 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker { iccid: String, enable: Boolean, // Enable or disable the profile indicated in iccid reconnectTimeoutMillis: Long = 0 // 0 = do not wait for reconnect - ): ForegroundTaskSubscriberFlow = + ) = launchForegroundTask( getString(R.string.task_profile_switch), getString(R.string.task_profile_switch_failure), R.drawable.ic_task_switch ) { euiccChannelManager.beginTrackedOperation(slotId, portId) { - val (res, refreshed) = euiccChannelManager.withEuiccChannel( - slotId, - portId - ) { channel -> - if (!channel.lpa.switchProfile(iccid, enable, refresh = true)) { - // Sometimes, we *can* enable or disable the profile, but we cannot - // send the refresh command to the modem because the profile somehow - // makes the modem "busy". In this case, we can still switch by setting - // refresh to false, but then the switch cannot take effect until the - // user resets the modem manually by toggling airplane mode or rebooting. - Pair(channel.lpa.switchProfile(iccid, enable, refresh = false), false) - } else { - Pair(true, true) + val (response, refreshed) = + euiccChannelManager.withEuiccChannel(slotId, portId) { channel -> + val refresh = preferenceRepository.refreshAfterSwitchFlow.first() + val response = channel.lpa.switchProfile(iccid, enable, refresh) + if (response || !refresh) { + Pair(response, refresh) + } else { + // refresh failed, but refresh was requested + // Sometimes, we *can* enable or disable the profile, but we cannot + // send the refresh command to the modem because the profile somehow + // makes the modem "busy". In this case, we can still switch by setting + // refresh to false, but then the switch cannot take effect until the + // user resets the modem manually by toggling airplane mode or rebooting. + Pair( + channel.lpa.switchProfile(iccid, enable, refresh = false), + false + ) + } } - } - if (!res) { + if (!response) { throw RuntimeException("Could not switch profile") } 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 b085286..d137e90 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 @@ -78,6 +78,9 @@ open class SettingsFragment: PreferenceFragmentCompat() { requirePreference("pref_developer_ignore_tls_certificate") .bindBooleanFlow(preferenceRepository.ignoreTLSCertificateFlow) + requirePreference("pref_developer_refresh_after_switch") + .bindBooleanFlow(preferenceRepository.refreshAfterSwitchFlow) + requirePreference("pref_developer_euicc_memory_reset") .bindBooleanFlow(preferenceRepository.euiccMemoryResetFlow) } diff --git a/app-common/src/main/java/im/angry/openeuicc/util/PreferenceUtils.kt b/app-common/src/main/java/im/angry/openeuicc/util/PreferenceUtils.kt index 34d1cfd..c69c5e4 100644 --- a/app-common/src/main/java/im/angry/openeuicc/util/PreferenceUtils.kt +++ b/app-common/src/main/java/im/angry/openeuicc/util/PreferenceUtils.kt @@ -31,6 +31,7 @@ internal object PreferenceKeys { // ---- Developer Options ---- val DEVELOPER_OPTIONS_ENABLED = booleanPreferencesKey("developer_options_enabled") + val REFRESH_AFTER_SWITCH = booleanPreferencesKey("refresh_after_switch") val UNFILTERED_PROFILE_LIST = booleanPreferencesKey("unfiltered_profile_list") val IGNORE_TLS_CERTIFICATE = booleanPreferencesKey("ignore_tls_certificate") val EUICC_MEMORY_RESET = booleanPreferencesKey("euicc_memory_reset") @@ -48,6 +49,7 @@ open class PreferenceRepository(private val context: Context) { val verboseLoggingFlow = bindFlow(PreferenceKeys.VERBOSE_LOGGING, false) // ---- Developer Options ---- + val refreshAfterSwitchFlow = bindFlow(PreferenceKeys.REFRESH_AFTER_SWITCH, true) val developerOptionsEnabledFlow = bindFlow(PreferenceKeys.DEVELOPER_OPTIONS_ENABLED, false) val unfilteredProfileListFlow = bindFlow(PreferenceKeys.UNFILTERED_PROFILE_LIST, false) val ignoreTLSCertificateFlow = bindFlow(PreferenceKeys.IGNORE_TLS_CERTIFICATE, false) @@ -60,15 +62,12 @@ open class PreferenceRepository(private val context: Context) { class PreferenceFlowWrapper private constructor( private val context: Context, private val key: Preferences.Key, - inner: Flow + inner: Flow, ) : Flow by inner { - internal constructor(context: Context, key: Preferences.Key, defaultValue: T) : this( - context, - key, - context.dataStore.data.map { it[key] ?: defaultValue } - ) + internal constructor(context: Context, key: Preferences.Key, defaultValue: T) : + this(context, key, context.dataStore.data.map { it[key] ?: defaultValue }) suspend fun updatePreference(value: T) { context.dataStore.edit { it[key] = value } } -} \ No newline at end of file +} diff --git a/app-common/src/main/res/values-ja/strings.xml b/app-common/src/main/res/values-ja/strings.xml index 644e564..35fd400 100644 --- a/app-common/src/main/res/values-ja/strings.xml +++ b/app-common/src/main/res/values-ja/strings.xml @@ -143,6 +143,7 @@ ログ アプリの最新デバッグログを表示します 開発者オプション + プロファイルを切り替えた後にモデムに更新コマンドを送信するかどうか。クラッシュが発生する場合は、これを無効にしてみてください。 フィルタリングされていないプロファイル一覧を表示 非運用のプロファイルも含めます SM-DP+ TLS 証明書を無視する @@ -162,4 +163,5 @@ 消去する eUICC の消去を可能にする この操作は、デフォルトでは非表示になっている危険な操作です。代わりに、すべての構成ファイルを手動で削除することもできます。 + モデムに更新コマンドを送信 diff --git a/app-common/src/main/res/values-zh-rCN/strings.xml b/app-common/src/main/res/values-zh-rCN/strings.xml index 789b532..2e069bd 100644 --- a/app-common/src/main/res/values-zh-rCN/strings.xml +++ b/app-common/src/main/res/values-zh-rCN/strings.xml @@ -145,6 +145,7 @@ 语言 选择 App 语言 开发者选项 + 切换配置文件后是否向基带发送刷新命令。如果发现崩溃,请尝试禁用此功能。 显示未经过滤的配置文件列表 在配置文件列表中包括非生产环境的配置文件 无视 SM-DP+ 的 TLS 证书 @@ -162,4 +163,5 @@ 擦除 允许擦除 eUICC 此操作是默认隐藏的危险操作。作为替代方案,您可以手动删除所有配置文件。 + 向基带发送刷新命令 \ No newline at end of file diff --git a/app-common/src/main/res/values-zh-rTW/strings.xml b/app-common/src/main/res/values-zh-rTW/strings.xml index 53be571..729893c 100644 --- a/app-common/src/main/res/values-zh-rTW/strings.xml +++ b/app-common/src/main/res/values-zh-rTW/strings.xml @@ -145,6 +145,7 @@ 語言 選擇 App 語言 開發人員選項 + 切換設定檔後是否向基帶發送刷新命令。如果發現崩潰,請嘗試停用此功能。 顯示未經過濾的設定檔列表 在設定檔列表中包括非生產環境的設定檔 忽略 SM-DP+ 的 TLS 證書 @@ -162,4 +163,5 @@ 擦除 允許擦除 eUICC 此操作是預設隱藏的危險操作。作為替代方案,您可以手動刪除所有設定檔。 + 向基帶發送刷新命令 \ No newline at end of file diff --git a/app-common/src/main/res/values/strings.xml b/app-common/src/main/res/values/strings.xml index 8146f54..a366b88 100644 --- a/app-common/src/main/res/values/strings.xml +++ b/app-common/src/main/res/values/strings.xml @@ -181,6 +181,8 @@ Logs View recent debug logs of the application Developer Options + Send refresh command to modem + Whether to send a refresh command to the modem after switching profiles. Try disabling this if you see crashes. Show unfiltered profile list Include non-production profiles in the list Ignore SM-DP+ TLS certificate diff --git a/app-common/src/main/res/xml/pref_settings.xml b/app-common/src/main/res/xml/pref_settings.xml index 7d25118..ce700f5 100644 --- a/app-common/src/main/res/xml/pref_settings.xml +++ b/app-common/src/main/res/xml/pref_settings.xml @@ -57,6 +57,12 @@ app:title="@string/pref_developer" app:iconSpaceReserved="false"> + +