From 284c0664f65cca83ed23763acacd1a28471a385c Mon Sep 17 00:00:00 2001 From: Alexandre Roux D'Anzi Date: Tue, 27 Feb 2024 13:09:08 +0100 Subject: [PATCH 001/461] removing asn1c not existing in project anymore --- libs/lpac-jni/src/main/jni/Android.bp | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/libs/lpac-jni/src/main/jni/Android.bp b/libs/lpac-jni/src/main/jni/Android.bp index c710a16..ce622de 100644 --- a/libs/lpac-jni/src/main/jni/Android.bp +++ b/libs/lpac-jni/src/main/jni/Android.bp @@ -13,16 +13,6 @@ cc_library_static { ], } -cc_library_static { - name: "liblpac-asn1c", - defaults: ["lpac-jni-defaults"], - local_include_dirs: ["lpac/euicc/asn1c"], - cflags: ["-DHAVE_CONFIG_H"], - srcs: [ - "lpac/euicc/asn1c/asn1/*.c", - ], -} - cc_library_static { name: "liblpac-euicc", defaults: ["lpac-jni-defaults"], @@ -30,7 +20,6 @@ cc_library_static { "lpac/euicc/*.c", ], static_libs: [ - "liblpac-asn1c", "liblpac-cjson", ], } @@ -44,7 +33,6 @@ cc_library_shared { ], static_libs: [ "liblpac-euicc", - "liblpac-asn1c", "liblpac-cjson", ], shared_libs: ["liblog"], From 655eb5cf52d05e5b44cd1d1c173b03a62b841ba5 Mon Sep 17 00:00:00 2001 From: Alexandre Roux D'Anzi Date: Tue, 27 Feb 2024 13:09:35 +0100 Subject: [PATCH 002/461] fixing syntax not existing in older kotlin --- .../main/java/im/angry/openeuicc/core/EuiccChannelManager.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt index 23c21b9..313ef21 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt @@ -29,7 +29,7 @@ open class EuiccChannelManager(protected val context: Context) { } protected open val uiccCards: Collection - get() = (0.. Date: Tue, 27 Feb 2024 13:09:54 +0100 Subject: [PATCH 003/461] fixing bad override --- .../main/java/im/angry/openeuicc/ui/NotificationsActivity.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/NotificationsActivity.kt b/app-common/src/main/java/im/angry/openeuicc/ui/NotificationsActivity.kt index 87c5ba3..66929c3 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/NotificationsActivity.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/NotificationsActivity.kt @@ -59,7 +59,7 @@ class NotificationsActivity: AppCompatActivity(), OpenEuiccContextMarker { refresh() } - override fun onCreateOptionsMenu(menu: Menu?): Boolean { + override fun onCreateOptionsMenu(menu: Menu): Boolean { super.onCreateOptionsMenu(menu) menuInflater.inflate(R.menu.activity_notifications, menu) return true From 6d200d14acce00a877c0cfa24b1bf4ae9c42a2ab Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Tue, 27 Feb 2024 19:50:29 -0500 Subject: [PATCH 004/461] nuke asn1c in Android.bp and Android.mk --- libs/lpac-jni/src/main/jni/Android.bp | 12 ------------ libs/lpac-jni/src/main/jni/Android.mk | 12 +----------- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/libs/lpac-jni/src/main/jni/Android.bp b/libs/lpac-jni/src/main/jni/Android.bp index c710a16..ce622de 100644 --- a/libs/lpac-jni/src/main/jni/Android.bp +++ b/libs/lpac-jni/src/main/jni/Android.bp @@ -13,16 +13,6 @@ cc_library_static { ], } -cc_library_static { - name: "liblpac-asn1c", - defaults: ["lpac-jni-defaults"], - local_include_dirs: ["lpac/euicc/asn1c"], - cflags: ["-DHAVE_CONFIG_H"], - srcs: [ - "lpac/euicc/asn1c/asn1/*.c", - ], -} - cc_library_static { name: "liblpac-euicc", defaults: ["lpac-jni-defaults"], @@ -30,7 +20,6 @@ cc_library_static { "lpac/euicc/*.c", ], static_libs: [ - "liblpac-asn1c", "liblpac-cjson", ], } @@ -44,7 +33,6 @@ cc_library_shared { ], static_libs: [ "liblpac-euicc", - "liblpac-asn1c", "liblpac-cjson", ], shared_libs: ["liblog"], diff --git a/libs/lpac-jni/src/main/jni/Android.mk b/libs/lpac-jni/src/main/jni/Android.mk index 0a216b3..c0bcee7 100644 --- a/libs/lpac-jni/src/main/jni/Android.mk +++ b/libs/lpac-jni/src/main/jni/Android.mk @@ -14,20 +14,10 @@ LOCAL_SRC_FILES := \ $(call all-c-files-under, lpac/cjson) include $(BUILD_STATIC_LIBRARY) -include $(CLEAR_VARS) -# libasn1c, the ASN parser component from lpac -LOCAL_MODULE := lpac-asn1c -LOCAL_C_INCLUDES := \ - $(LOCAL_PATH)/lpac/euicc/asn1c -LOCAL_SRC_FILES := \ - $(call all-c-files-under, lpac/euicc/asn1c/asn1) -LOCAL_CFLAGS := -DHAVE_CONFIG_H -include $(BUILD_STATIC_LIBRARY) - include $(CLEAR_VARS) # libeuicc component from lpac, which contains the actual implementation LOCAL_MODULE := lpac-euicc -LOCAL_STATIC_LIBRARIES := lpac-asn1c lpac-cjson +LOCAL_STATIC_LIBRARIES := lpac-cjson LOCAL_C_INCLUDES := \ $(LOCAL_PATH)/lpac LOCAL_SRC_FILES := \ From a1b264362509d0d8bc358101719f8251cca43b5e Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Tue, 27 Feb 2024 20:06:08 -0500 Subject: [PATCH 005/461] lpac-jni: Always add GSMA ROOT CI1 --- .../main/java/net/typeblog/lpac_jni/impl/RootCertificates.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/RootCertificates.kt b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/RootCertificates.kt index 84fa227..f3e5e90 100644 --- a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/RootCertificates.kt +++ b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/RootCertificates.kt @@ -24,8 +24,9 @@ internal fun keyIdToKeystore(keyIds: Array): KeyStore { } } - // If no known certs have been added, add at least the default GSMA CI - if (ret.size() == 0) { + // At the very least, we should always have GSMA ROOT CI1 trusted + // many servers supporting custom roots are served with GSMA ROOT CI1 for TLS + if (!ret.isCertificateEntry(DEFAULT_PKID_GSMA_RSP2_ROOT_CI1)) { getCertificate(DEFAULT_PKID_GSMA_RSP2_ROOT_CI1)?.let { cert -> ret.setCertificateEntry(DEFAULT_PKID_GSMA_RSP2_ROOT_CI1, cert) } From 62e3e41c52ee224f41e63865c22f257e314520c2 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Wed, 28 Feb 2024 19:57:47 -0500 Subject: [PATCH 006/461] lpac-jni: Mark network-related LPA methods as synchronized These methods rely on non-thread-safe internal states within lpac's context. --- .../net/typeblog/lpac_jni/impl/LocalProfileAssistantImpl.kt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/LocalProfileAssistantImpl.kt b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/LocalProfileAssistantImpl.kt index 072fcaa..b81a41a 100644 --- a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/LocalProfileAssistantImpl.kt +++ b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/LocalProfileAssistantImpl.kt @@ -115,6 +115,7 @@ class LocalProfileAssistantImpl( return LpacJni.es10cDeleteProfile(contextHandle, iccid) == 0 } + @Synchronized override fun downloadProfile(smdp: String, matchingId: String?, imei: String?, confirmationCode: String?, callback: ProfileDownloadCallback): Boolean { return LpacJni.downloadProfile(contextHandle, smdp, matchingId, imei, confirmationCode, callback) == 0 @@ -123,6 +124,7 @@ class LocalProfileAssistantImpl( override fun deleteNotification(seqNumber: Long): Boolean = LpacJni.es10bDeleteNotification(contextHandle, seqNumber) == 0 + @Synchronized override fun handleNotification(seqNumber: Long): Boolean = LpacJni.handleNotification(contextHandle, seqNumber).also { Log.d(TAG, "handleNotification $seqNumber = $it") From c0d1c29b7fcd21803e8174fdf522fbda215fd249 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sun, 3 Mar 2024 10:55:35 -0500 Subject: [PATCH 007/461] feat: Show error logs on crash when unprivileged ...however, don't do this in privileged mode because OpenEuiccService is supposed to be background, and we don't want to just randomly show up when things go wrong. --- .../im/angry/openeuicc/ui/LogsActivity.kt | 12 ++-------- .../java/im/angry/openeuicc/util/Utils.kt | 11 +++++++++ app-unpriv/src/main/AndroidManifest.xml | 2 +- .../UnprivilegedOpenEuiccApplication.kt | 23 +++++++++++++++++++ 4 files changed, 37 insertions(+), 11 deletions(-) create mode 100644 app-unpriv/src/main/java/im/angry/openeuicc/UnprivilegedOpenEuiccApplication.kt diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/LogsActivity.kt b/app-common/src/main/java/im/angry/openeuicc/ui/LogsActivity.kt index 9a2a855..ba15e37 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/LogsActivity.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/LogsActivity.kt @@ -8,6 +8,7 @@ import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.lifecycleScope import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import im.angry.openeuicc.common.R +import im.angry.openeuicc.util.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -44,16 +45,7 @@ class LogsActivity : AppCompatActivity() { private suspend fun reload() = withContext(Dispatchers.Main) { swipeRefresh.isRefreshing = true - val logStr = withContext(Dispatchers.IO) { - try { - Runtime.getRuntime().exec("logcat -t 1024").inputStream.readBytes() - .decodeToString() - } catch (_: Exception) { - "" - } - } - - logText.text = logStr + logText.text = intent.extras?.getString("log") ?: readSelfLog() swipeRefresh.isRefreshing = false 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 b2690b0..dc90ab1 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 @@ -8,9 +8,11 @@ import androidx.fragment.app.Fragment import im.angry.openeuicc.OpenEuiccApplication import im.angry.openeuicc.core.EuiccChannel import im.angry.openeuicc.core.EuiccChannelManager +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock +import kotlinx.coroutines.withContext import net.typeblog.lpac_jni.LocalProfileInfo import java.lang.RuntimeException import kotlin.coroutines.resume @@ -26,6 +28,15 @@ val Context.selfAppVersion: String throw RuntimeException(e) } +suspend fun readSelfLog(lines: Int = 2048): String = withContext(Dispatchers.IO) { + try { + Runtime.getRuntime().exec("logcat -t $lines").inputStream.readBytes() + .decodeToString() + } catch (_: Exception) { + "" + } +} + interface OpenEuiccContextMarker { val openEuiccMarkerContext: Context get() = when (this) { diff --git a/app-unpriv/src/main/AndroidManifest.xml b/app-unpriv/src/main/AndroidManifest.xml index bce6831..e72b112 100644 --- a/app-unpriv/src/main/AndroidManifest.xml +++ b/app-unpriv/src/main/AndroidManifest.xml @@ -2,7 +2,7 @@ + Intent(this, LogsActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK) + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + putExtra("log", runBlocking { readSelfLog() }) + startActivity(this) + exitProcess(-1) + } + } + } +} \ No newline at end of file From e48a919335583f1f86d6955a1fc41ce42bb219ee Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sun, 3 Mar 2024 13:22:46 -0500 Subject: [PATCH 008/461] feat: Allow exporting logs as txt --- .../im/angry/openeuicc/ui/LogsActivity.kt | 31 +++++++++++++++++++ .../main/res/drawable/ic_save_as_black.xml | 5 +++ .../src/main/res/menu/activity_logs.xml | 9 ++++++ app-common/src/main/res/values/strings.xml | 3 ++ 4 files changed, 48 insertions(+) create mode 100644 app-common/src/main/res/drawable/ic_save_as_black.xml create mode 100644 app-common/src/main/res/menu/activity_logs.xml diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/LogsActivity.kt b/app-common/src/main/java/im/angry/openeuicc/ui/LogsActivity.kt index ba15e37..1a7e50f 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/LogsActivity.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/LogsActivity.kt @@ -1,9 +1,13 @@ package im.angry.openeuicc.ui +import android.icu.text.SimpleDateFormat import android.os.Bundle +import android.view.Menu +import android.view.MenuItem import android.view.View import android.widget.ScrollView import android.widget.TextView +import androidx.activity.result.contract.ActivityResultContracts import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.lifecycleScope import androidx.swiperefreshlayout.widget.SwipeRefreshLayout @@ -12,12 +16,24 @@ import im.angry.openeuicc.util.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext +import java.io.FileOutputStream +import java.util.Date class LogsActivity : AppCompatActivity() { private lateinit var swipeRefresh: SwipeRefreshLayout private lateinit var scrollView: ScrollView private lateinit var logText: TextView + private val saveLogs = + registerForActivityResult(ActivityResultContracts.CreateDocument("text/plain")) { uri -> + if (uri == null) return@registerForActivityResult + contentResolver.openFileDescriptor(uri, "w")?.use { + FileOutputStream(it.fileDescriptor).use { os -> + os.write(logText.text.toString().encodeToByteArray()) + } + } + } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_logs) @@ -42,6 +58,21 @@ class LogsActivity : AppCompatActivity() { } } + override fun onCreateOptionsMenu(menu: Menu?): Boolean { + menuInflater.inflate(R.menu.activity_logs, menu) + return true + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { + R.id.save -> { + saveLogs.launch(getString(R.string.logs_filename_template, + SimpleDateFormat.getDateTimeInstance().format(Date()) + )) + true + } + else -> super.onOptionsItemSelected(item) + } + private suspend fun reload() = withContext(Dispatchers.Main) { swipeRefresh.isRefreshing = true diff --git a/app-common/src/main/res/drawable/ic_save_as_black.xml b/app-common/src/main/res/drawable/ic_save_as_black.xml new file mode 100644 index 0000000..aaee678 --- /dev/null +++ b/app-common/src/main/res/drawable/ic_save_as_black.xml @@ -0,0 +1,5 @@ + + + diff --git a/app-common/src/main/res/menu/activity_logs.xml b/app-common/src/main/res/menu/activity_logs.xml new file mode 100644 index 0000000..4fa3aeb --- /dev/null +++ b/app-common/src/main/res/menu/activity_logs.xml @@ -0,0 +1,9 @@ + + + + \ 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 9d8932a..040c910 100644 --- a/app-common/src/main/res/values/strings.xml +++ b/app-common/src/main/res/values/strings.xml @@ -48,6 +48,9 @@ Process Delete + Save + Logs at %s + Settings Notifications eSIM profile operations send notifications to the carrier. Fine-tune this behavior as needed here. From 770083523db3cec28184edf8a169a5eafd09e1d8 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sun, 3 Mar 2024 20:29:18 -0500 Subject: [PATCH 009/461] refactor: Extract an interface from EuiccChannelManager Eventually, we would like EuiccChannelManager to become a Service instead of just any random class. --- .../angry/openeuicc/OpenEuiccApplication.kt | 3 +- .../openeuicc/core/EuiccChannelManager.kt | 84 ++++++++++--------- .../openeuicc/core/IEuiccChannelManager.kt | 47 +++++++++++ .../im/angry/openeuicc/ui/MainActivity.kt | 13 +-- .../java/im/angry/openeuicc/util/Utils.kt | 4 +- .../PrivilegedOpenEuiccApplication.kt | 4 +- .../util/PrivilegedTelephonyUtils.kt | 6 +- 7 files changed, 105 insertions(+), 56 deletions(-) create mode 100644 app-common/src/main/java/im/angry/openeuicc/core/IEuiccChannelManager.kt diff --git a/app-common/src/main/java/im/angry/openeuicc/OpenEuiccApplication.kt b/app-common/src/main/java/im/angry/openeuicc/OpenEuiccApplication.kt index e917aec..0b2ccec 100644 --- a/app-common/src/main/java/im/angry/openeuicc/OpenEuiccApplication.kt +++ b/app-common/src/main/java/im/angry/openeuicc/OpenEuiccApplication.kt @@ -5,6 +5,7 @@ import android.telephony.SubscriptionManager import android.telephony.TelephonyManager import com.google.android.material.color.DynamicColors import im.angry.openeuicc.core.EuiccChannelManager +import im.angry.openeuicc.core.IEuiccChannelManager import im.angry.openeuicc.util.PreferenceRepository open class OpenEuiccApplication : Application() { @@ -19,7 +20,7 @@ open class OpenEuiccApplication : Application() { getSystemService(TelephonyManager::class.java)!! } - open val euiccChannelManager: EuiccChannelManager by lazy { + open val euiccChannelManager: IEuiccChannelManager by lazy { EuiccChannelManager(this) } diff --git a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt index 23c21b9..e031794 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt @@ -13,7 +13,7 @@ import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext import java.lang.IllegalArgumentException -open class EuiccChannelManager(protected val context: Context) { +open class EuiccChannelManager(protected val context: Context) : IEuiccChannelManager { companion object { const val TAG = "EuiccChannelManager" } @@ -32,9 +32,9 @@ open class EuiccChannelManager(protected val context: Context) { get() = (0..? = + runBlocking { for (card in uiccCards) { if (card.physicalSlotIndex != physicalSlotId) continue - for (port in card.ports) { - tryOpenEuiccChannel(port)?.let { return@withContext it } + return@runBlocking card.ports.mapNotNull { tryOpenEuiccChannel(it) } + .ifEmpty { null } + } + return@runBlocking null + } + + override fun findEuiccChannelByPortBlocking(physicalSlotId: Int, portId: Int): EuiccChannel? = + runBlocking { + withContext(Dispatchers.IO) { + uiccCards.find { it.physicalSlotIndex == physicalSlotId }?.let { card -> + card.ports.find { it.portIndex == portId }?.let { tryOpenEuiccChannel(it) } } } - - null } - } - fun findAllEuiccChannelsByPhysicalSlotBlocking(physicalSlotId: Int): List? = runBlocking { - for (card in uiccCards) { - if (card.physicalSlotIndex != physicalSlotId) continue - return@runBlocking card.ports.mapNotNull { tryOpenEuiccChannel(it) } - .ifEmpty { null } - } - return@runBlocking null - } - - fun findEuiccChannelByPortBlocking(physicalSlotId: Int, portId: Int): EuiccChannel? = runBlocking { - withContext(Dispatchers.IO) { - uiccCards.find { it.physicalSlotIndex == physicalSlotId }?.let { card -> - card.ports.find { it.portIndex == portId }?.let { tryOpenEuiccChannel(it) } - } - } - } - - suspend fun enumerateEuiccChannels() { + override suspend fun enumerateEuiccChannels() { withContext(Dispatchers.IO) { ensureSEService() for (uiccInfo in uiccCards) { for (port in uiccInfo.ports) { if (tryOpenEuiccChannel(port) != null) { - Log.d(TAG, "Found eUICC on slot ${uiccInfo.physicalSlotIndex} port ${port.portIndex}") + Log.d( + TAG, + "Found eUICC on slot ${uiccInfo.physicalSlotIndex} port ${port.portIndex}" + ) } } } } } - val knownChannels: List + override val knownChannels: List get() = channels.toList() - fun invalidate() { + override fun invalidate() { for (channel in channels) { channel.close() } @@ -161,8 +171,4 @@ open class EuiccChannelManager(protected val context: Context) { seService?.shutdown() seService = null } - - open fun notifyEuiccProfilesChanged(logicalSlotId: Int) { - // No-op for unprivileged - } } \ No newline at end of file diff --git a/app-common/src/main/java/im/angry/openeuicc/core/IEuiccChannelManager.kt b/app-common/src/main/java/im/angry/openeuicc/core/IEuiccChannelManager.kt new file mode 100644 index 0000000..8a6ab54 --- /dev/null +++ b/app-common/src/main/java/im/angry/openeuicc/core/IEuiccChannelManager.kt @@ -0,0 +1,47 @@ +package im.angry.openeuicc.core + +interface IEuiccChannelManager { + val knownChannels: List + + /** + * Scan all possible sources for EuiccChannels and have them cached for future use + */ + suspend fun enumerateEuiccChannels() + + /** + * Returns the EuiccChannel corresponding to a **logical** slot + */ + fun findEuiccChannelBySlotBlocking(logicalSlotId: Int): EuiccChannel? + + /** + * Returns the first EuiccChannel corresponding to a **physical** slot + * If the physical slot supports MEP and has multiple ports, it is undefined + * which of the two channels will be returned. + */ + fun findEuiccChannelByPhysicalSlotBlocking(physicalSlotId: Int): EuiccChannel? + + /** + * Returns all EuiccChannels corresponding to a **physical** slot + * Multiple channels are possible in the case of MEP + */ + fun findAllEuiccChannelsByPhysicalSlotBlocking(physicalSlotId: Int): List? + + /** + * Returns the EuiccChannel corresponding to a **physical** slot and a port ID + */ + fun findEuiccChannelByPortBlocking(physicalSlotId: Int, portId: Int): EuiccChannel? + + /** + * Invalidate all EuiccChannels previously known by this Manager + */ + fun invalidate() + + /** + * If possible, trigger the system to update the cached list of profiles + * This is only expected to be implemented when the application is privileged + * TODO: Remove this from the common interface + */ + fun notifyEuiccProfilesChanged(logicalSlotId: Int) { + // no-op by default + } +} \ No newline at end of file diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/MainActivity.kt b/app-common/src/main/java/im/angry/openeuicc/ui/MainActivity.kt index 452ed76..af6cbf4 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/MainActivity.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/MainActivity.kt @@ -14,7 +14,6 @@ import androidx.appcompat.app.AppCompatActivity 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 import kotlinx.coroutines.launch @@ -25,8 +24,6 @@ open class MainActivity : AppCompatActivity(), OpenEuiccContextMarker { const val TAG = "MainActivity" } - protected lateinit var manager: EuiccChannelManager - private lateinit var spinnerAdapter: ArrayAdapter private lateinit var spinner: Spinner @@ -45,8 +42,6 @@ open class MainActivity : AppCompatActivity(), OpenEuiccContextMarker { tm = telephonyManager - manager = euiccChannelManager - spinnerAdapter = ArrayAdapter(this, R.layout.spinner_item) lifecycleScope.launch { @@ -99,19 +94,19 @@ open class MainActivity : AppCompatActivity(), OpenEuiccContextMarker { private suspend fun init() { withContext(Dispatchers.IO) { - manager.enumerateEuiccChannels() - manager.knownChannels.forEach { + euiccChannelManager.enumerateEuiccChannels() + euiccChannelManager.knownChannels.forEach { Log.d(TAG, "slot ${it.slotId} port ${it.portId}") Log.d(TAG, it.lpa.eID) // Request the system to refresh the list of profiles every time we start // Note that this is currently supposed to be no-op when unprivileged, // but it could change in the future - manager.notifyEuiccProfilesChanged(it.logicalSlotId) + euiccChannelManager.notifyEuiccProfilesChanged(it.logicalSlotId) } } withContext(Dispatchers.Main) { - manager.knownChannels.sortedBy { it.logicalSlotId }.forEach { channel -> + euiccChannelManager.knownChannels.sortedBy { it.logicalSlotId }.forEach { channel -> spinnerAdapter.add(getString(R.string.channel_name_format, channel.logicalSlotId)) fragments.add(createEuiccManagementFragment(channel)) } 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 dc90ab1..3c21898 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 @@ -7,7 +7,7 @@ import android.telephony.TelephonyManager import androidx.fragment.app.Fragment import im.angry.openeuicc.OpenEuiccApplication import im.angry.openeuicc.core.EuiccChannel -import im.angry.openeuicc.core.EuiccChannelManager +import im.angry.openeuicc.core.IEuiccChannelManager import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.sync.Mutex @@ -48,7 +48,7 @@ interface OpenEuiccContextMarker { val openEuiccApplication: OpenEuiccApplication get() = openEuiccMarkerContext.applicationContext as OpenEuiccApplication - val euiccChannelManager: EuiccChannelManager + val euiccChannelManager: IEuiccChannelManager get() = openEuiccApplication.euiccChannelManager val telephonyManager: TelephonyManager diff --git a/app/src/main/java/im/angry/openeuicc/PrivilegedOpenEuiccApplication.kt b/app/src/main/java/im/angry/openeuicc/PrivilegedOpenEuiccApplication.kt index 8b3ef99..28ea49d 100644 --- a/app/src/main/java/im/angry/openeuicc/PrivilegedOpenEuiccApplication.kt +++ b/app/src/main/java/im/angry/openeuicc/PrivilegedOpenEuiccApplication.kt @@ -1,10 +1,10 @@ package im.angry.openeuicc -import im.angry.openeuicc.core.EuiccChannelManager +import im.angry.openeuicc.core.IEuiccChannelManager import im.angry.openeuicc.core.PrivilegedEuiccChannelManager class PrivilegedOpenEuiccApplication: OpenEuiccApplication() { - override val euiccChannelManager: EuiccChannelManager by lazy { + override val euiccChannelManager: IEuiccChannelManager by lazy { PrivilegedEuiccChannelManager(this) } diff --git a/app/src/main/java/im/angry/openeuicc/util/PrivilegedTelephonyUtils.kt b/app/src/main/java/im/angry/openeuicc/util/PrivilegedTelephonyUtils.kt index 4675ab9..4cb4932 100644 --- a/app/src/main/java/im/angry/openeuicc/util/PrivilegedTelephonyUtils.kt +++ b/app/src/main/java/im/angry/openeuicc/util/PrivilegedTelephonyUtils.kt @@ -4,7 +4,7 @@ import android.telephony.SubscriptionManager import android.telephony.TelephonyManager import android.telephony.UiccSlotMapping import im.angry.openeuicc.core.EuiccChannel -import im.angry.openeuicc.core.EuiccChannelManager +import im.angry.openeuicc.core.IEuiccChannelManager import kotlinx.coroutines.runBlocking import java.lang.Exception @@ -14,7 +14,7 @@ val TelephonyManager.supportsDSDS: Boolean val TelephonyManager.dsdsEnabled: Boolean get() = activeModemCount >= 2 -fun TelephonyManager.setDsdsEnabled(euiccManager: EuiccChannelManager, enabled: Boolean) { +fun TelephonyManager.setDsdsEnabled(euiccManager: IEuiccChannelManager, enabled: Boolean) { runBlocking { euiccManager.enumerateEuiccChannels() } @@ -32,7 +32,7 @@ fun TelephonyManager.setDsdsEnabled(euiccManager: EuiccChannelManager, enabled: // Disable eSIM profiles before switching the slot mapping // This ensures that unmapped eSIM ports never have "ghost" profiles enabled fun TelephonyManager.updateSimSlotMapping( - euiccManager: EuiccChannelManager, newMapping: Collection, + euiccManager: IEuiccChannelManager, newMapping: Collection, currentMapping: Collection = simSlotMapping ) { val unmapped = currentMapping.filterNot { mapping -> From 2d1c96023a8669036dc2652e13414bcb4f05a893 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Mon, 4 Mar 2024 17:17:20 -0500 Subject: [PATCH 010/461] refactor: Condense dependency management to a rudimentary dependency injection subpackage --- .../angry/openeuicc/OpenEuiccApplication.kt | 27 +++++-------------- .../openeuicc/core/EuiccChannelManager.kt | 2 +- .../im/angry/openeuicc/di/AppContainer.kt | 13 +++++++++ .../angry/openeuicc/di/DefaultAppContainer.kt | 26 ++++++++++++++++++ .../openeuicc/ui/ProfileDownloadFragment.kt | 2 +- .../java/im/angry/openeuicc/util/Utils.kt | 8 ++++-- .../PrivilegedOpenEuiccApplication.kt | 9 ++++--- .../core/PrivilegedEuiccChannelManager.kt | 2 +- .../openeuicc/di/PrivilegedAppContainer.kt | 11 ++++++++ .../openeuicc/service/OpenEuiccService.kt | 2 +- .../openeuicc/ui/PrivilegedMainActivity.kt | 2 +- .../angry/openeuicc/ui/SlotMappingFragment.kt | 16 +++++------ 12 files changed, 78 insertions(+), 42 deletions(-) create mode 100644 app-common/src/main/java/im/angry/openeuicc/di/AppContainer.kt create mode 100644 app-common/src/main/java/im/angry/openeuicc/di/DefaultAppContainer.kt create mode 100644 app/src/main/java/im/angry/openeuicc/di/PrivilegedAppContainer.kt diff --git a/app-common/src/main/java/im/angry/openeuicc/OpenEuiccApplication.kt b/app-common/src/main/java/im/angry/openeuicc/OpenEuiccApplication.kt index 0b2ccec..6cd22ce 100644 --- a/app-common/src/main/java/im/angry/openeuicc/OpenEuiccApplication.kt +++ b/app-common/src/main/java/im/angry/openeuicc/OpenEuiccApplication.kt @@ -1,34 +1,19 @@ package im.angry.openeuicc import android.app.Application -import android.telephony.SubscriptionManager -import android.telephony.TelephonyManager import com.google.android.material.color.DynamicColors -import im.angry.openeuicc.core.EuiccChannelManager -import im.angry.openeuicc.core.IEuiccChannelManager -import im.angry.openeuicc.util.PreferenceRepository +import im.angry.openeuicc.di.AppContainer +import im.angry.openeuicc.di.DefaultAppContainer open class OpenEuiccApplication : Application() { + open val appContainer: AppContainer by lazy { + DefaultAppContainer(this) + } + override fun onCreate() { super.onCreate() // Observe dynamic colors changes DynamicColors.applyToActivitiesIfAvailable(this) } - - val telephonyManager by lazy { - getSystemService(TelephonyManager::class.java)!! - } - - open val euiccChannelManager: IEuiccChannelManager by lazy { - EuiccChannelManager(this) - } - - val subscriptionManager by lazy { - getSystemService(SubscriptionManager::class.java)!! - } - - val preferenceRepository by lazy { - PreferenceRepository(this) - } } \ No newline at end of file diff --git a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt index e031794..9033d34 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt @@ -25,7 +25,7 @@ open class EuiccChannelManager(protected val context: Context) : IEuiccChannelMa private val lock = Mutex() protected val tm by lazy { - (context.applicationContext as OpenEuiccApplication).telephonyManager + (context.applicationContext as OpenEuiccApplication).appContainer.telephonyManager } protected open val uiccCards: Collection diff --git a/app-common/src/main/java/im/angry/openeuicc/di/AppContainer.kt b/app-common/src/main/java/im/angry/openeuicc/di/AppContainer.kt new file mode 100644 index 0000000..65c2aa9 --- /dev/null +++ b/app-common/src/main/java/im/angry/openeuicc/di/AppContainer.kt @@ -0,0 +1,13 @@ +package im.angry.openeuicc.di + +import android.telephony.SubscriptionManager +import android.telephony.TelephonyManager +import im.angry.openeuicc.core.IEuiccChannelManager +import im.angry.openeuicc.util.* + +interface AppContainer { + val telephonyManager: TelephonyManager + val euiccChannelManager: IEuiccChannelManager + val subscriptionManager: SubscriptionManager + val preferenceRepository: PreferenceRepository +} \ No newline at end of file diff --git a/app-common/src/main/java/im/angry/openeuicc/di/DefaultAppContainer.kt b/app-common/src/main/java/im/angry/openeuicc/di/DefaultAppContainer.kt new file mode 100644 index 0000000..dd7826c --- /dev/null +++ b/app-common/src/main/java/im/angry/openeuicc/di/DefaultAppContainer.kt @@ -0,0 +1,26 @@ +package im.angry.openeuicc.di + +import android.content.Context +import android.telephony.SubscriptionManager +import android.telephony.TelephonyManager +import im.angry.openeuicc.core.EuiccChannelManager +import im.angry.openeuicc.core.IEuiccChannelManager +import im.angry.openeuicc.util.* + +open class DefaultAppContainer(context: Context) : AppContainer { + override val telephonyManager by lazy { + context.getSystemService(TelephonyManager::class.java)!! + } + + override val euiccChannelManager: IEuiccChannelManager by lazy { + EuiccChannelManager(context) + } + + override val subscriptionManager by lazy { + context.getSystemService(SubscriptionManager::class.java)!! + } + + override val preferenceRepository by lazy { + PreferenceRepository(context) + } +} \ No newline at end of file diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDownloadFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDownloadFragment.kt index 360da4f..43fc1ee 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDownloadFragment.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDownloadFragment.kt @@ -121,7 +121,7 @@ class ProfileDownloadFragment : BaseMaterialDialogFragment(), super.onStart() profileDownloadIMEI.editText!!.text = Editable.Factory.getInstance().newEditable( try { - openEuiccApplication.telephonyManager.getImei(channel.logicalSlotId) + telephonyManager.getImei(channel.logicalSlotId) } catch (e: Exception) { "" } 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 3c21898..57626e2 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 @@ -8,6 +8,7 @@ import androidx.fragment.app.Fragment import im.angry.openeuicc.OpenEuiccApplication import im.angry.openeuicc.core.EuiccChannel import im.angry.openeuicc.core.IEuiccChannelManager +import im.angry.openeuicc.di.AppContainer import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.sync.Mutex @@ -48,11 +49,14 @@ interface OpenEuiccContextMarker { val openEuiccApplication: OpenEuiccApplication get() = openEuiccMarkerContext.applicationContext as OpenEuiccApplication + val appContainer: AppContainer + get() = openEuiccApplication.appContainer + val euiccChannelManager: IEuiccChannelManager - get() = openEuiccApplication.euiccChannelManager + get() = appContainer.euiccChannelManager val telephonyManager: TelephonyManager - get() = openEuiccApplication.telephonyManager + get() = appContainer.telephonyManager } val LocalProfileInfo.isEnabled: Boolean diff --git a/app/src/main/java/im/angry/openeuicc/PrivilegedOpenEuiccApplication.kt b/app/src/main/java/im/angry/openeuicc/PrivilegedOpenEuiccApplication.kt index 28ea49d..2d8e5ee 100644 --- a/app/src/main/java/im/angry/openeuicc/PrivilegedOpenEuiccApplication.kt +++ b/app/src/main/java/im/angry/openeuicc/PrivilegedOpenEuiccApplication.kt @@ -1,16 +1,17 @@ package im.angry.openeuicc -import im.angry.openeuicc.core.IEuiccChannelManager import im.angry.openeuicc.core.PrivilegedEuiccChannelManager +import im.angry.openeuicc.di.AppContainer +import im.angry.openeuicc.di.PrivilegedAppContainer class PrivilegedOpenEuiccApplication: OpenEuiccApplication() { - override val euiccChannelManager: IEuiccChannelManager by lazy { - PrivilegedEuiccChannelManager(this) + override val appContainer: AppContainer by lazy { + PrivilegedAppContainer(this) } override fun onCreate() { super.onCreate() - (euiccChannelManager as PrivilegedEuiccChannelManager).closeAllStaleChannels() + (appContainer.euiccChannelManager as PrivilegedEuiccChannelManager).closeAllStaleChannels() } } \ No newline at end of file diff --git a/app/src/main/java/im/angry/openeuicc/core/PrivilegedEuiccChannelManager.kt b/app/src/main/java/im/angry/openeuicc/core/PrivilegedEuiccChannelManager.kt index 288cebd..75b2b22 100644 --- a/app/src/main/java/im/angry/openeuicc/core/PrivilegedEuiccChannelManager.kt +++ b/app/src/main/java/im/angry/openeuicc/core/PrivilegedEuiccChannelManager.kt @@ -48,7 +48,7 @@ class PrivilegedEuiccChannelManager(context: Context): EuiccChannelManager(conte } override fun notifyEuiccProfilesChanged(logicalSlotId: Int) { - (context.applicationContext as OpenEuiccApplication).subscriptionManager.apply { + (context.applicationContext as OpenEuiccApplication).appContainer.subscriptionManager.apply { findEuiccChannelBySlotBlocking(logicalSlotId)?.let { tryRefreshCachedEuiccInfo(it.cardId) } diff --git a/app/src/main/java/im/angry/openeuicc/di/PrivilegedAppContainer.kt b/app/src/main/java/im/angry/openeuicc/di/PrivilegedAppContainer.kt new file mode 100644 index 0000000..a849a27 --- /dev/null +++ b/app/src/main/java/im/angry/openeuicc/di/PrivilegedAppContainer.kt @@ -0,0 +1,11 @@ +package im.angry.openeuicc.di + +import android.content.Context +import im.angry.openeuicc.core.IEuiccChannelManager +import im.angry.openeuicc.core.PrivilegedEuiccChannelManager + +class PrivilegedAppContainer(context: Context) : DefaultAppContainer(context) { + override val euiccChannelManager: IEuiccChannelManager by lazy { + PrivilegedEuiccChannelManager(context) + } +} \ No newline at end of file diff --git a/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt b/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt index 6a08da5..23f1444 100644 --- a/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt +++ b/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt @@ -238,7 +238,7 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker { } val success = channel.lpa .setNickname(iccid, nickname!!) - openEuiccApplication.subscriptionManager.tryRefreshCachedEuiccInfo(channel.cardId) + appContainer.subscriptionManager.tryRefreshCachedEuiccInfo(channel.cardId) return if (success) { RESULT_OK } else { diff --git a/app/src/main/java/im/angry/openeuicc/ui/PrivilegedMainActivity.kt b/app/src/main/java/im/angry/openeuicc/ui/PrivilegedMainActivity.kt index 64e8ca9..1261932 100644 --- a/app/src/main/java/im/angry/openeuicc/ui/PrivilegedMainActivity.kt +++ b/app/src/main/java/im/angry/openeuicc/ui/PrivilegedMainActivity.kt @@ -26,7 +26,7 @@ class PrivilegedMainActivity : MainActivity() { override fun onOptionsItemSelected(item: MenuItem): Boolean = when (item.itemId) { R.id.dsds -> { - tm.setDsdsEnabled(openEuiccApplication.euiccChannelManager, !item.isChecked) + tm.setDsdsEnabled(euiccChannelManager, !item.isChecked) Toast.makeText(this, R.string.toast_dsds_switched, Toast.LENGTH_LONG).show() finish() true diff --git a/app/src/main/java/im/angry/openeuicc/ui/SlotMappingFragment.kt b/app/src/main/java/im/angry/openeuicc/ui/SlotMappingFragment.kt index ba60fd7..4c77bac 100644 --- a/app/src/main/java/im/angry/openeuicc/ui/SlotMappingFragment.kt +++ b/app/src/main/java/im/angry/openeuicc/ui/SlotMappingFragment.kt @@ -32,12 +32,8 @@ class SlotMappingFragment: BaseMaterialDialogFragment(), const val TAG = "SlotMappingFragment" } - private val tm: TelephonyManager by lazy { - (requireContext().applicationContext as OpenEuiccApplication).telephonyManager - } - private val ports: List by lazy { - tm.uiccCardsInfoCompat.flatMap { it.ports } + telephonyManager.uiccCardsInfoCompat.flatMap { it.ports } } private val portsDesc: List by lazy { @@ -81,7 +77,7 @@ class SlotMappingFragment: BaseMaterialDialogFragment(), private fun init() { lifecycleScope.launch(Dispatchers.Main) { val mapping = withContext(Dispatchers.IO) { - tm.simSlotMapping + telephonyManager.simSlotMapping } adapter = SlotMappingAdapter(mapping.toMutableList().apply { @@ -100,14 +96,14 @@ class SlotMappingFragment: BaseMaterialDialogFragment(), withContext(Dispatchers.IO) { // Use the utility method from PrivilegedTelephonyUtils to ensure // unmapped ports have all profiles disabled - tm.updateSimSlotMapping(openEuiccApplication.euiccChannelManager, adapter.mappings) + telephonyManager.updateSimSlotMapping(euiccChannelManager, adapter.mappings) } } catch (e: Exception) { Toast.makeText(requireContext(), R.string.slot_mapping_failure, Toast.LENGTH_LONG).show() return@launch } Toast.makeText(requireContext(), R.string.slot_mapping_completed, Toast.LENGTH_LONG).show() - openEuiccApplication.euiccChannelManager.invalidate() + euiccChannelManager.invalidate() requireActivity().finish() } } @@ -115,7 +111,7 @@ class SlotMappingFragment: BaseMaterialDialogFragment(), private suspend fun buildHelpText() = withContext(Dispatchers.IO) { val nLogicalSlots = adapter.mappings.size - val cards = openEuiccApplication.telephonyManager.uiccCardsInfoCompat + val cards = telephonyManager.uiccCardsInfoCompat val nPhysicalSlots = cards.size var idxMepCard = -1 @@ -129,7 +125,7 @@ class SlotMappingFragment: BaseMaterialDialogFragment(), } val mayEnableDSDS = - openEuiccApplication.telephonyManager.supportsDSDS && !openEuiccApplication.telephonyManager.dsdsEnabled + telephonyManager.supportsDSDS && !telephonyManager.dsdsEnabled val extraText = if (nLogicalSlots == 1 && mayEnableDSDS) { getString(R.string.slot_mapping_help_dsds) From 49af0ffee9719497fa439fcde6fa81738a1bbebc Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Mon, 4 Mar 2024 18:23:53 -0500 Subject: [PATCH 011/461] CompatibilityCheck: Return FAILURE_UNKNOWN when no SIM readers are found --- .../java/im/angry/openeuicc/util/CompatibilityCheck.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/app-unpriv/src/main/java/im/angry/openeuicc/util/CompatibilityCheck.kt b/app-unpriv/src/main/java/im/angry/openeuicc/util/CompatibilityCheck.kt index 3d9c47b..843c487 100644 --- a/app-unpriv/src/main/java/im/angry/openeuicc/util/CompatibilityCheck.kt +++ b/app-unpriv/src/main/java/im/angry/openeuicc/util/CompatibilityCheck.kt @@ -132,7 +132,13 @@ internal class IsdrChannelAccessCheck(private val context: Context): Compatibili override suspend fun doCheck(): State { val seService = connectSEService(context) - val (validSlotIds, result) = seService.readers.filter { it.isSIM }.map { + val readers = seService.readers.filter { it.isSIM } + if (readers.isEmpty()) { + failureDescription = context.getString(R.string.compatibility_check_isdr_channel_desc_unknown) + return State.FAILURE_UNKNOWN + } + + val (validSlotIds, result) = readers.map { try { it.openSession().openLogicalChannel(ISDR_AID)?.close() Pair(it.slotIndex, State.SUCCESS) From a101ae6805340053c9d783b2a886965070bf34fb Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Mon, 4 Mar 2024 18:38:01 -0500 Subject: [PATCH 012/461] CompatibilityCheck: Improve OMAPI connectivity check Stop failing the test if only some slots can be seen. Display a text warning users of that, but don't appear as a failure. --- .../main/java/im/angry/openeuicc/util/CompatibilityCheck.kt | 6 ++++-- app-unpriv/src/main/res/values/strings.xml | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/app-unpriv/src/main/java/im/angry/openeuicc/util/CompatibilityCheck.kt b/app-unpriv/src/main/java/im/angry/openeuicc/util/CompatibilityCheck.kt index 843c487..93d49be 100644 --- a/app-unpriv/src/main/java/im/angry/openeuicc/util/CompatibilityCheck.kt +++ b/app-unpriv/src/main/java/im/angry/openeuicc/util/CompatibilityCheck.kt @@ -47,11 +47,13 @@ abstract class CompatibilityCheck(context: Context) { abstract val title: String protected abstract val defaultDescription: String + protected lateinit var successDescription: String protected lateinit var failureDescription: String val description: String get() = when { (state == State.FAILURE || state == State.FAILURE_UNKNOWN) && this::failureDescription.isInitialized -> failureDescription + state == State.SUCCESS && this::successDescription.isInitialized -> successDescription else -> defaultDescription } @@ -111,9 +113,9 @@ internal class OmapiConnCheck(private val context: Context): CompatibilityCheck( failureDescription = context.getString(R.string.compatibility_check_omapi_connectivity_fail) return State.FAILURE } else if (simReaders.size < tm.activeModemCountCompat) { - failureDescription = context.getString(R.string.compatibility_check_omapi_connectivity_fail_sim_number, + successDescription = context.getString(R.string.compatibility_check_omapi_connectivity_partial_success_sim_number, simReaders.map { it.slotIndex }.joinToString(", ")) - return State.FAILURE + return State.SUCCESS } return State.SUCCESS diff --git a/app-unpriv/src/main/res/values/strings.xml b/app-unpriv/src/main/res/values/strings.xml index 3a20af1..8845a58 100644 --- a/app-unpriv/src/main/res/values/strings.xml +++ b/app-unpriv/src/main/res/values/strings.xml @@ -9,8 +9,8 @@ Your device has no support for accessing SIM cards via OMAPI. OMAPI Connectivity Does your device allow access to Secure Elements on SIM cards via OMAPI? - Unable to detect Secure Element readers for SIM cards via OMAPI. - Only the following SIM slots are accessible via OMAPI: %s. + Unable to detect Secure Element readers for SIM cards via OMAPI. If you have not inserted a SIM in this device, try inserting one and retry this check. + Successfully detected Secure Element access, but only for the following SIM slots: %s. ISD-R Channel Access Does your device support opening an ISD-R (management) channel to eSIMs via OMAPI? Cannot determine whether ISD-R access through OMAPI is supported. You might want to retry with SIM cards inserted (any SIM card will do) if not already. From aef399dad013b53aaaa943cf01a9ab7038e11568 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Mon, 4 Mar 2024 18:39:40 -0500 Subject: [PATCH 013/461] CompatibilityCheck: Explain that the user might want to contact the ROM developer --- app-unpriv/src/main/res/values/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app-unpriv/src/main/res/values/strings.xml b/app-unpriv/src/main/res/values/strings.xml index 8845a58..588c073 100644 --- a/app-unpriv/src/main/res/values/strings.xml +++ b/app-unpriv/src/main/res/values/strings.xml @@ -6,7 +6,7 @@ System Features Whether your device has all the required features for managing removable eUICC cards. For example, basic telephony and OMAPI support. Your device has no telephony features. - Your device has no support for accessing SIM cards via OMAPI. + Your device has no support for accessing SIM cards via OMAPI. If you are using a custom ROM, consider contacting the developer to determine whether it is due to hardware or a missing feature declaration in the OS. OMAPI Connectivity Does your device allow access to Secure Elements on SIM cards via OMAPI? Unable to detect Secure Element readers for SIM cards via OMAPI. If you have not inserted a SIM in this device, try inserting one and retry this check. From 6356601467f768a5957ecc0c9480b875efb753d4 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Mon, 4 Mar 2024 18:43:37 -0500 Subject: [PATCH 014/461] CompatibilityCheck: Make connectivity fail a "Unknown" failure --- .../src/main/java/im/angry/openeuicc/util/CompatibilityCheck.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app-unpriv/src/main/java/im/angry/openeuicc/util/CompatibilityCheck.kt b/app-unpriv/src/main/java/im/angry/openeuicc/util/CompatibilityCheck.kt index 93d49be..ae5a4da 100644 --- a/app-unpriv/src/main/java/im/angry/openeuicc/util/CompatibilityCheck.kt +++ b/app-unpriv/src/main/java/im/angry/openeuicc/util/CompatibilityCheck.kt @@ -111,7 +111,7 @@ internal class OmapiConnCheck(private val context: Context): CompatibilityCheck( val simReaders = seService.readers.filter { it.isSIM } if (simReaders.isEmpty()) { failureDescription = context.getString(R.string.compatibility_check_omapi_connectivity_fail) - return State.FAILURE + return State.FAILURE_UNKNOWN } else if (simReaders.size < tm.activeModemCountCompat) { successDescription = context.getString(R.string.compatibility_check_omapi_connectivity_partial_success_sim_number, simReaders.map { it.slotIndex }.joinToString(", ")) From 4dd14d23f24d6dd7694571970c56cb98499bd85a Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Mon, 4 Mar 2024 18:51:10 -0500 Subject: [PATCH 015/461] refactor: Add UiComponentFactory to manual DI --- .../main/java/im/angry/openeuicc/di/AppContainer.kt | 1 + .../java/im/angry/openeuicc/di/DefaultAppContainer.kt | 4 ++++ .../im/angry/openeuicc/di/DefaultUiComponentFactory.kt | 9 +++++++++ .../java/im/angry/openeuicc/di/UiComponentFactory.kt | 8 ++++++++ .../main/java/im/angry/openeuicc/ui/MainActivity.kt | 6 +----- .../im/angry/openeuicc/di/PrivilegedAppContainer.kt | 4 ++++ .../angry/openeuicc/di/PrivilegedUiComponentFactory.kt | 10 ++++++++++ .../im/angry/openeuicc/ui/PrivilegedMainActivity.kt | 4 ---- 8 files changed, 37 insertions(+), 9 deletions(-) create mode 100644 app-common/src/main/java/im/angry/openeuicc/di/DefaultUiComponentFactory.kt create mode 100644 app-common/src/main/java/im/angry/openeuicc/di/UiComponentFactory.kt create mode 100644 app/src/main/java/im/angry/openeuicc/di/PrivilegedUiComponentFactory.kt diff --git a/app-common/src/main/java/im/angry/openeuicc/di/AppContainer.kt b/app-common/src/main/java/im/angry/openeuicc/di/AppContainer.kt index 65c2aa9..a29fcf8 100644 --- a/app-common/src/main/java/im/angry/openeuicc/di/AppContainer.kt +++ b/app-common/src/main/java/im/angry/openeuicc/di/AppContainer.kt @@ -10,4 +10,5 @@ interface AppContainer { val euiccChannelManager: IEuiccChannelManager val subscriptionManager: SubscriptionManager val preferenceRepository: PreferenceRepository + val uiComponentFactory: UiComponentFactory } \ No newline at end of file diff --git a/app-common/src/main/java/im/angry/openeuicc/di/DefaultAppContainer.kt b/app-common/src/main/java/im/angry/openeuicc/di/DefaultAppContainer.kt index dd7826c..6a03835 100644 --- a/app-common/src/main/java/im/angry/openeuicc/di/DefaultAppContainer.kt +++ b/app-common/src/main/java/im/angry/openeuicc/di/DefaultAppContainer.kt @@ -23,4 +23,8 @@ open class DefaultAppContainer(context: Context) : AppContainer { override val preferenceRepository by lazy { PreferenceRepository(context) } + + override val uiComponentFactory by lazy { + DefaultUiComponentFactory() + } } \ No newline at end of file diff --git a/app-common/src/main/java/im/angry/openeuicc/di/DefaultUiComponentFactory.kt b/app-common/src/main/java/im/angry/openeuicc/di/DefaultUiComponentFactory.kt new file mode 100644 index 0000000..86af007 --- /dev/null +++ b/app-common/src/main/java/im/angry/openeuicc/di/DefaultUiComponentFactory.kt @@ -0,0 +1,9 @@ +package im.angry.openeuicc.di + +import im.angry.openeuicc.core.EuiccChannel +import im.angry.openeuicc.ui.EuiccManagementFragment + +open class DefaultUiComponentFactory : UiComponentFactory { + override fun createEuiccManagementFragment(channel: EuiccChannel): EuiccManagementFragment = + EuiccManagementFragment.newInstance(channel.slotId, channel.portId) +} \ No newline at end of file diff --git a/app-common/src/main/java/im/angry/openeuicc/di/UiComponentFactory.kt b/app-common/src/main/java/im/angry/openeuicc/di/UiComponentFactory.kt new file mode 100644 index 0000000..d311876 --- /dev/null +++ b/app-common/src/main/java/im/angry/openeuicc/di/UiComponentFactory.kt @@ -0,0 +1,8 @@ +package im.angry.openeuicc.di + +import im.angry.openeuicc.core.EuiccChannel +import im.angry.openeuicc.ui.EuiccManagementFragment + +interface UiComponentFactory { + fun createEuiccManagementFragment(channel: EuiccChannel): EuiccManagementFragment +} \ No newline at end of file diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/MainActivity.kt b/app-common/src/main/java/im/angry/openeuicc/ui/MainActivity.kt index af6cbf4..ca6715d 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/MainActivity.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/MainActivity.kt @@ -88,10 +88,6 @@ open class MainActivity : AppCompatActivity(), OpenEuiccContextMarker { else -> super.onOptionsItemSelected(item) } - - protected open fun createEuiccManagementFragment(channel: EuiccChannel): EuiccManagementFragment = - EuiccManagementFragment.newInstance(channel.slotId, channel.portId) - private suspend fun init() { withContext(Dispatchers.IO) { euiccChannelManager.enumerateEuiccChannels() @@ -108,7 +104,7 @@ open class MainActivity : AppCompatActivity(), OpenEuiccContextMarker { withContext(Dispatchers.Main) { euiccChannelManager.knownChannels.sortedBy { it.logicalSlotId }.forEach { channel -> spinnerAdapter.add(getString(R.string.channel_name_format, channel.logicalSlotId)) - fragments.add(createEuiccManagementFragment(channel)) + fragments.add(appContainer.uiComponentFactory.createEuiccManagementFragment(channel)) } if (fragments.isNotEmpty()) { diff --git a/app/src/main/java/im/angry/openeuicc/di/PrivilegedAppContainer.kt b/app/src/main/java/im/angry/openeuicc/di/PrivilegedAppContainer.kt index a849a27..64e45eb 100644 --- a/app/src/main/java/im/angry/openeuicc/di/PrivilegedAppContainer.kt +++ b/app/src/main/java/im/angry/openeuicc/di/PrivilegedAppContainer.kt @@ -8,4 +8,8 @@ class PrivilegedAppContainer(context: Context) : DefaultAppContainer(context) { override val euiccChannelManager: IEuiccChannelManager by lazy { PrivilegedEuiccChannelManager(context) } + + override val uiComponentFactory by lazy { + PrivilegedUiComponentFactory() + } } \ No newline at end of file diff --git a/app/src/main/java/im/angry/openeuicc/di/PrivilegedUiComponentFactory.kt b/app/src/main/java/im/angry/openeuicc/di/PrivilegedUiComponentFactory.kt new file mode 100644 index 0000000..d3c5cdb --- /dev/null +++ b/app/src/main/java/im/angry/openeuicc/di/PrivilegedUiComponentFactory.kt @@ -0,0 +1,10 @@ +package im.angry.openeuicc.di + +import im.angry.openeuicc.core.EuiccChannel +import im.angry.openeuicc.ui.EuiccManagementFragment +import im.angry.openeuicc.ui.PrivilegedEuiccManagementFragment + +class PrivilegedUiComponentFactory : DefaultUiComponentFactory() { + override fun createEuiccManagementFragment(channel: EuiccChannel): EuiccManagementFragment = + PrivilegedEuiccManagementFragment.newInstance(channel.slotId, channel.portId) +} \ No newline at end of file diff --git a/app/src/main/java/im/angry/openeuicc/ui/PrivilegedMainActivity.kt b/app/src/main/java/im/angry/openeuicc/ui/PrivilegedMainActivity.kt index 1261932..f3c2b3c 100644 --- a/app/src/main/java/im/angry/openeuicc/ui/PrivilegedMainActivity.kt +++ b/app/src/main/java/im/angry/openeuicc/ui/PrivilegedMainActivity.kt @@ -4,7 +4,6 @@ import android.view.Menu import android.view.MenuItem import android.widget.Toast import im.angry.openeuicc.R -import im.angry.openeuicc.core.EuiccChannel import im.angry.openeuicc.util.* class PrivilegedMainActivity : MainActivity() { @@ -37,7 +36,4 @@ class PrivilegedMainActivity : MainActivity() { } else -> super.onOptionsItemSelected(item) } - - override fun createEuiccManagementFragment(channel: EuiccChannel): EuiccManagementFragment = - PrivilegedEuiccManagementFragment.newInstance(channel.slotId, channel.portId) } \ No newline at end of file From 7c6b4ebee5cb9dc4690b2336bb8931f63e7820ca Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Mon, 4 Mar 2024 19:06:05 -0500 Subject: [PATCH 016/461] refactor: IEuiccChannelManager -> EuiccChannelManager --- .../core/DefaultEuiccChannelManager.kt | 174 +++++++++++++++ .../openeuicc/core/EuiccChannelManager.kt | 199 ++++-------------- .../openeuicc/core/IEuiccChannelManager.kt | 47 ----- .../im/angry/openeuicc/di/AppContainer.kt | 4 +- .../angry/openeuicc/di/DefaultAppContainer.kt | 6 +- .../java/im/angry/openeuicc/util/Utils.kt | 4 +- .../core/PrivilegedEuiccChannelManager.kt | 2 +- .../openeuicc/di/PrivilegedAppContainer.kt | 4 +- .../util/PrivilegedTelephonyUtils.kt | 6 +- 9 files changed, 223 insertions(+), 223 deletions(-) create mode 100644 app-common/src/main/java/im/angry/openeuicc/core/DefaultEuiccChannelManager.kt delete mode 100644 app-common/src/main/java/im/angry/openeuicc/core/IEuiccChannelManager.kt diff --git a/app-common/src/main/java/im/angry/openeuicc/core/DefaultEuiccChannelManager.kt b/app-common/src/main/java/im/angry/openeuicc/core/DefaultEuiccChannelManager.kt new file mode 100644 index 0000000..1883a11 --- /dev/null +++ b/app-common/src/main/java/im/angry/openeuicc/core/DefaultEuiccChannelManager.kt @@ -0,0 +1,174 @@ +package im.angry.openeuicc.core + +import android.content.Context +import android.se.omapi.SEService +import android.telephony.SubscriptionManager +import android.util.Log +import im.angry.openeuicc.OpenEuiccApplication +import im.angry.openeuicc.util.* +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock +import kotlinx.coroutines.withContext +import java.lang.IllegalArgumentException + +open class DefaultEuiccChannelManager(protected val context: Context) : EuiccChannelManager { + companion object { + const val TAG = "EuiccChannelManager" + } + + private val channels = mutableListOf() + + private var seService: SEService? = null + + private val lock = Mutex() + + protected val tm by lazy { + (context.applicationContext as OpenEuiccApplication).appContainer.telephonyManager + } + + protected open val uiccCards: Collection + get() = (0..? = + runBlocking { + for (card in uiccCards) { + if (card.physicalSlotIndex != physicalSlotId) continue + return@runBlocking card.ports.mapNotNull { tryOpenEuiccChannel(it) } + .ifEmpty { null } + } + return@runBlocking null + } + + override fun findEuiccChannelByPortBlocking(physicalSlotId: Int, portId: Int): EuiccChannel? = + runBlocking { + withContext(Dispatchers.IO) { + uiccCards.find { it.physicalSlotIndex == physicalSlotId }?.let { card -> + card.ports.find { it.portIndex == portId }?.let { tryOpenEuiccChannel(it) } + } + } + } + + override suspend fun enumerateEuiccChannels() { + withContext(Dispatchers.IO) { + ensureSEService() + + for (uiccInfo in uiccCards) { + for (port in uiccInfo.ports) { + if (tryOpenEuiccChannel(port) != null) { + Log.d( + TAG, + "Found eUICC on slot ${uiccInfo.physicalSlotIndex} port ${port.portIndex}" + ) + } + } + } + } + } + + override val knownChannels: List + get() = channels.toList() + + override fun invalidate() { + for (channel in channels) { + channel.close() + } + + channels.clear() + seService?.shutdown() + seService = null + } +} \ No newline at end of file diff --git a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt index 9033d34..b0bce1d 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelManager.kt @@ -1,174 +1,47 @@ package im.angry.openeuicc.core -import android.content.Context -import android.se.omapi.SEService -import android.telephony.SubscriptionManager -import android.util.Log -import im.angry.openeuicc.OpenEuiccApplication -import im.angry.openeuicc.util.* -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.runBlocking -import kotlinx.coroutines.sync.Mutex -import kotlinx.coroutines.sync.withLock -import kotlinx.coroutines.withContext -import java.lang.IllegalArgumentException +interface EuiccChannelManager { + val knownChannels: List -open class EuiccChannelManager(protected val context: Context) : IEuiccChannelManager { - companion object { - const val TAG = "EuiccChannelManager" - } + /** + * Scan all possible sources for EuiccChannels and have them cached for future use + */ + suspend fun enumerateEuiccChannels() - private val channels = mutableListOf() + /** + * Returns the EuiccChannel corresponding to a **logical** slot + */ + fun findEuiccChannelBySlotBlocking(logicalSlotId: Int): EuiccChannel? - private var seService: SEService? = null + /** + * Returns the first EuiccChannel corresponding to a **physical** slot + * If the physical slot supports MEP and has multiple ports, it is undefined + * which of the two channels will be returned. + */ + fun findEuiccChannelByPhysicalSlotBlocking(physicalSlotId: Int): EuiccChannel? - private val lock = Mutex() + /** + * Returns all EuiccChannels corresponding to a **physical** slot + * Multiple channels are possible in the case of MEP + */ + fun findAllEuiccChannelsByPhysicalSlotBlocking(physicalSlotId: Int): List? - protected val tm by lazy { - (context.applicationContext as OpenEuiccApplication).appContainer.telephonyManager - } + /** + * Returns the EuiccChannel corresponding to a **physical** slot and a port ID + */ + fun findEuiccChannelByPortBlocking(physicalSlotId: Int, portId: Int): EuiccChannel? - protected open val uiccCards: Collection - get() = (0..? = - runBlocking { - for (card in uiccCards) { - if (card.physicalSlotIndex != physicalSlotId) continue - return@runBlocking card.ports.mapNotNull { tryOpenEuiccChannel(it) } - .ifEmpty { null } - } - return@runBlocking null - } - - override fun findEuiccChannelByPortBlocking(physicalSlotId: Int, portId: Int): EuiccChannel? = - runBlocking { - withContext(Dispatchers.IO) { - uiccCards.find { it.physicalSlotIndex == physicalSlotId }?.let { card -> - card.ports.find { it.portIndex == portId }?.let { tryOpenEuiccChannel(it) } - } - } - } - - override suspend fun enumerateEuiccChannels() { - withContext(Dispatchers.IO) { - ensureSEService() - - for (uiccInfo in uiccCards) { - for (port in uiccInfo.ports) { - if (tryOpenEuiccChannel(port) != null) { - Log.d( - TAG, - "Found eUICC on slot ${uiccInfo.physicalSlotIndex} port ${port.portIndex}" - ) - } - } - } - } - } - - override val knownChannels: List - get() = channels.toList() - - override fun invalidate() { - for (channel in channels) { - channel.close() - } - - channels.clear() - seService?.shutdown() - seService = null + /** + * If possible, trigger the system to update the cached list of profiles + * This is only expected to be implemented when the application is privileged + * TODO: Remove this from the common interface + */ + fun notifyEuiccProfilesChanged(logicalSlotId: Int) { + // no-op by default } } \ No newline at end of file diff --git a/app-common/src/main/java/im/angry/openeuicc/core/IEuiccChannelManager.kt b/app-common/src/main/java/im/angry/openeuicc/core/IEuiccChannelManager.kt deleted file mode 100644 index 8a6ab54..0000000 --- a/app-common/src/main/java/im/angry/openeuicc/core/IEuiccChannelManager.kt +++ /dev/null @@ -1,47 +0,0 @@ -package im.angry.openeuicc.core - -interface IEuiccChannelManager { - val knownChannels: List - - /** - * Scan all possible sources for EuiccChannels and have them cached for future use - */ - suspend fun enumerateEuiccChannels() - - /** - * Returns the EuiccChannel corresponding to a **logical** slot - */ - fun findEuiccChannelBySlotBlocking(logicalSlotId: Int): EuiccChannel? - - /** - * Returns the first EuiccChannel corresponding to a **physical** slot - * If the physical slot supports MEP and has multiple ports, it is undefined - * which of the two channels will be returned. - */ - fun findEuiccChannelByPhysicalSlotBlocking(physicalSlotId: Int): EuiccChannel? - - /** - * Returns all EuiccChannels corresponding to a **physical** slot - * Multiple channels are possible in the case of MEP - */ - fun findAllEuiccChannelsByPhysicalSlotBlocking(physicalSlotId: Int): List? - - /** - * Returns the EuiccChannel corresponding to a **physical** slot and a port ID - */ - fun findEuiccChannelByPortBlocking(physicalSlotId: Int, portId: Int): EuiccChannel? - - /** - * Invalidate all EuiccChannels previously known by this Manager - */ - fun invalidate() - - /** - * If possible, trigger the system to update the cached list of profiles - * This is only expected to be implemented when the application is privileged - * TODO: Remove this from the common interface - */ - fun notifyEuiccProfilesChanged(logicalSlotId: Int) { - // no-op by default - } -} \ No newline at end of file diff --git a/app-common/src/main/java/im/angry/openeuicc/di/AppContainer.kt b/app-common/src/main/java/im/angry/openeuicc/di/AppContainer.kt index a29fcf8..a62eff4 100644 --- a/app-common/src/main/java/im/angry/openeuicc/di/AppContainer.kt +++ b/app-common/src/main/java/im/angry/openeuicc/di/AppContainer.kt @@ -2,12 +2,12 @@ package im.angry.openeuicc.di import android.telephony.SubscriptionManager import android.telephony.TelephonyManager -import im.angry.openeuicc.core.IEuiccChannelManager +import im.angry.openeuicc.core.EuiccChannelManager import im.angry.openeuicc.util.* interface AppContainer { val telephonyManager: TelephonyManager - val euiccChannelManager: IEuiccChannelManager + val euiccChannelManager: EuiccChannelManager val subscriptionManager: SubscriptionManager val preferenceRepository: PreferenceRepository val uiComponentFactory: UiComponentFactory diff --git a/app-common/src/main/java/im/angry/openeuicc/di/DefaultAppContainer.kt b/app-common/src/main/java/im/angry/openeuicc/di/DefaultAppContainer.kt index 6a03835..9f051e8 100644 --- a/app-common/src/main/java/im/angry/openeuicc/di/DefaultAppContainer.kt +++ b/app-common/src/main/java/im/angry/openeuicc/di/DefaultAppContainer.kt @@ -3,8 +3,8 @@ package im.angry.openeuicc.di import android.content.Context import android.telephony.SubscriptionManager import android.telephony.TelephonyManager +import im.angry.openeuicc.core.DefaultEuiccChannelManager import im.angry.openeuicc.core.EuiccChannelManager -import im.angry.openeuicc.core.IEuiccChannelManager import im.angry.openeuicc.util.* open class DefaultAppContainer(context: Context) : AppContainer { @@ -12,8 +12,8 @@ open class DefaultAppContainer(context: Context) : AppContainer { context.getSystemService(TelephonyManager::class.java)!! } - override val euiccChannelManager: IEuiccChannelManager by lazy { - EuiccChannelManager(context) + override val euiccChannelManager: EuiccChannelManager by lazy { + DefaultEuiccChannelManager(context) } override val subscriptionManager by lazy { 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 57626e2..33699d7 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 @@ -7,7 +7,7 @@ import android.telephony.TelephonyManager import androidx.fragment.app.Fragment import im.angry.openeuicc.OpenEuiccApplication import im.angry.openeuicc.core.EuiccChannel -import im.angry.openeuicc.core.IEuiccChannelManager +import im.angry.openeuicc.core.EuiccChannelManager import im.angry.openeuicc.di.AppContainer import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking @@ -52,7 +52,7 @@ interface OpenEuiccContextMarker { val appContainer: AppContainer get() = openEuiccApplication.appContainer - val euiccChannelManager: IEuiccChannelManager + val euiccChannelManager: EuiccChannelManager get() = appContainer.euiccChannelManager val telephonyManager: TelephonyManager diff --git a/app/src/main/java/im/angry/openeuicc/core/PrivilegedEuiccChannelManager.kt b/app/src/main/java/im/angry/openeuicc/core/PrivilegedEuiccChannelManager.kt index 75b2b22..0ebaa81 100644 --- a/app/src/main/java/im/angry/openeuicc/core/PrivilegedEuiccChannelManager.kt +++ b/app/src/main/java/im/angry/openeuicc/core/PrivilegedEuiccChannelManager.kt @@ -7,7 +7,7 @@ import im.angry.openeuicc.util.* import java.lang.Exception import java.lang.IllegalArgumentException -class PrivilegedEuiccChannelManager(context: Context): EuiccChannelManager(context) { +class PrivilegedEuiccChannelManager(context: Context): DefaultEuiccChannelManager(context) { override val uiccCards: Collection get() = tm.uiccCardsInfoCompat diff --git a/app/src/main/java/im/angry/openeuicc/di/PrivilegedAppContainer.kt b/app/src/main/java/im/angry/openeuicc/di/PrivilegedAppContainer.kt index 64e45eb..dc3921e 100644 --- a/app/src/main/java/im/angry/openeuicc/di/PrivilegedAppContainer.kt +++ b/app/src/main/java/im/angry/openeuicc/di/PrivilegedAppContainer.kt @@ -1,11 +1,11 @@ package im.angry.openeuicc.di import android.content.Context -import im.angry.openeuicc.core.IEuiccChannelManager +import im.angry.openeuicc.core.EuiccChannelManager import im.angry.openeuicc.core.PrivilegedEuiccChannelManager class PrivilegedAppContainer(context: Context) : DefaultAppContainer(context) { - override val euiccChannelManager: IEuiccChannelManager by lazy { + override val euiccChannelManager: EuiccChannelManager by lazy { PrivilegedEuiccChannelManager(context) } diff --git a/app/src/main/java/im/angry/openeuicc/util/PrivilegedTelephonyUtils.kt b/app/src/main/java/im/angry/openeuicc/util/PrivilegedTelephonyUtils.kt index 4cb4932..4675ab9 100644 --- a/app/src/main/java/im/angry/openeuicc/util/PrivilegedTelephonyUtils.kt +++ b/app/src/main/java/im/angry/openeuicc/util/PrivilegedTelephonyUtils.kt @@ -4,7 +4,7 @@ import android.telephony.SubscriptionManager import android.telephony.TelephonyManager import android.telephony.UiccSlotMapping import im.angry.openeuicc.core.EuiccChannel -import im.angry.openeuicc.core.IEuiccChannelManager +import im.angry.openeuicc.core.EuiccChannelManager import kotlinx.coroutines.runBlocking import java.lang.Exception @@ -14,7 +14,7 @@ val TelephonyManager.supportsDSDS: Boolean val TelephonyManager.dsdsEnabled: Boolean get() = activeModemCount >= 2 -fun TelephonyManager.setDsdsEnabled(euiccManager: IEuiccChannelManager, enabled: Boolean) { +fun TelephonyManager.setDsdsEnabled(euiccManager: EuiccChannelManager, enabled: Boolean) { runBlocking { euiccManager.enumerateEuiccChannels() } @@ -32,7 +32,7 @@ fun TelephonyManager.setDsdsEnabled(euiccManager: IEuiccChannelManager, enabled: // Disable eSIM profiles before switching the slot mapping // This ensures that unmapped eSIM ports never have "ghost" profiles enabled fun TelephonyManager.updateSimSlotMapping( - euiccManager: IEuiccChannelManager, newMapping: Collection, + euiccManager: EuiccChannelManager, newMapping: Collection, currentMapping: Collection = simSlotMapping ) { val unmapped = currentMapping.filterNot { mapping -> From 1a69c5294b56912ce71516f3b6c8be4c490d747b Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Mon, 4 Mar 2024 19:30:04 -0500 Subject: [PATCH 017/461] refactor: Use DI techniques for EuiccChannel's as well --- .../core/DefaultEuiccChannelFactory.kt | 43 ++++++++++++ .../core/DefaultEuiccChannelManager.kt | 65 ++++--------------- .../openeuicc/core/EuiccChannelFactory.kt | 16 +++++ .../im/angry/openeuicc/di/AppContainer.kt | 2 + .../angry/openeuicc/di/DefaultAppContainer.kt | 7 +- .../core/PrivilegedEuiccChannelFactory.kt | 43 ++++++++++++ .../core/PrivilegedEuiccChannelManager.kt | 30 ++------- .../openeuicc/di/PrivilegedAppContainer.kt | 7 +- 8 files changed, 133 insertions(+), 80 deletions(-) create mode 100644 app-common/src/main/java/im/angry/openeuicc/core/DefaultEuiccChannelFactory.kt create mode 100644 app-common/src/main/java/im/angry/openeuicc/core/EuiccChannelFactory.kt create mode 100644 app/src/main/java/im/angry/openeuicc/core/PrivilegedEuiccChannelFactory.kt 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 new file mode 100644 index 0000000..15e0191 --- /dev/null +++ b/app-common/src/main/java/im/angry/openeuicc/core/DefaultEuiccChannelFactory.kt @@ -0,0 +1,43 @@ +package im.angry.openeuicc.core + +import android.content.Context +import android.se.omapi.SEService +import android.util.Log +import im.angry.openeuicc.util.* +import java.lang.IllegalArgumentException + +open class DefaultEuiccChannelFactory(protected val context: Context) : EuiccChannelFactory { + private var seService: SEService? = null + + private suspend fun ensureSEService() { + if (seService == null) { + seService = connectSEService(context) + } + } + + override suspend fun tryOpenEuiccChannel(port: UiccPortInfoCompat): EuiccChannel? { + if (port.portIndex != 0) { + Log.w(DefaultEuiccChannelManager.TAG, "OMAPI channel attempted on non-zero portId, this may or may not work.") + } + + ensureSEService() + + Log.i(DefaultEuiccChannelManager.TAG, "Trying OMAPI for physical slot ${port.card.physicalSlotIndex}") + try { + return OmapiChannel(seService!!, port) + } catch (e: IllegalArgumentException) { + // Failed + Log.w( + DefaultEuiccChannelManager.TAG, + "OMAPI APDU interface unavailable for physical slot ${port.card.physicalSlotIndex}." + ) + } + + return null + } + + override fun cleanup() { + seService?.shutdown() + seService = null + } +} \ No newline at end of file diff --git a/app-common/src/main/java/im/angry/openeuicc/core/DefaultEuiccChannelManager.kt b/app-common/src/main/java/im/angry/openeuicc/core/DefaultEuiccChannelManager.kt index 1883a11..a978677 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/DefaultEuiccChannelManager.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/DefaultEuiccChannelManager.kt @@ -1,69 +1,41 @@ package im.angry.openeuicc.core import android.content.Context -import android.se.omapi.SEService import android.telephony.SubscriptionManager import android.util.Log -import im.angry.openeuicc.OpenEuiccApplication +import im.angry.openeuicc.di.AppContainer import im.angry.openeuicc.util.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.sync.Mutex import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext -import java.lang.IllegalArgumentException -open class DefaultEuiccChannelManager(protected val context: Context) : EuiccChannelManager { +open class DefaultEuiccChannelManager( + protected val appContainer: AppContainer, + protected val context: Context +) : EuiccChannelManager { companion object { const val TAG = "EuiccChannelManager" } private val channels = mutableListOf() - private var seService: SEService? = null - private val lock = Mutex() protected val tm by lazy { - (context.applicationContext as OpenEuiccApplication).appContainer.telephonyManager + appContainer.telephonyManager + } + + private val euiccChannelFactory by lazy { + appContainer.euiccChannelFactory } protected open val uiccCards: Collection get() = (0.. get() = tm.uiccCardsInfoCompat - @Suppress("NAME_SHADOWING") - override fun tryOpenEuiccChannelPrivileged(port: UiccPortInfoCompat): EuiccChannel? { - val port = port as RealUiccPortInfoCompat - if (port.card.isRemovable) { - // Attempt unprivileged (OMAPI) before TelephonyManager - // but still try TelephonyManager in case OMAPI is broken - super.tryOpenEuiccChannelUnprivileged(port)?.let { return it } - } - - if (port.card.isEuicc) { - Log.i(TAG, "Trying TelephonyManager for slot ${port.card.physicalSlotIndex} port ${port.portIndex}") - try { - return TelephonyManagerChannel(port, tm) - } catch (e: IllegalArgumentException) { - // Failed - Log.w(TAG, "TelephonyManager APDU interface unavailable for slot ${port.card.physicalSlotIndex} port ${port.portIndex}, falling back") - } - } - return null - } - // Clean up channels left open in TelephonyManager // due to a (potentially) forced restart // This should be called every time the application is restarted @@ -48,7 +26,7 @@ class PrivilegedEuiccChannelManager(context: Context): DefaultEuiccChannelManage } override fun notifyEuiccProfilesChanged(logicalSlotId: Int) { - (context.applicationContext as OpenEuiccApplication).appContainer.subscriptionManager.apply { + appContainer.subscriptionManager.apply { findEuiccChannelBySlotBlocking(logicalSlotId)?.let { tryRefreshCachedEuiccInfo(it.cardId) } diff --git a/app/src/main/java/im/angry/openeuicc/di/PrivilegedAppContainer.kt b/app/src/main/java/im/angry/openeuicc/di/PrivilegedAppContainer.kt index dc3921e..5158352 100644 --- a/app/src/main/java/im/angry/openeuicc/di/PrivilegedAppContainer.kt +++ b/app/src/main/java/im/angry/openeuicc/di/PrivilegedAppContainer.kt @@ -2,14 +2,19 @@ package im.angry.openeuicc.di import android.content.Context import im.angry.openeuicc.core.EuiccChannelManager +import im.angry.openeuicc.core.PrivilegedEuiccChannelFactory import im.angry.openeuicc.core.PrivilegedEuiccChannelManager class PrivilegedAppContainer(context: Context) : DefaultAppContainer(context) { override val euiccChannelManager: EuiccChannelManager by lazy { - PrivilegedEuiccChannelManager(context) + PrivilegedEuiccChannelManager(this, context) } override val uiComponentFactory by lazy { PrivilegedUiComponentFactory() } + + override val euiccChannelFactory by lazy { + PrivilegedEuiccChannelFactory(context) + } } \ No newline at end of file From 6c2b1675bd5f4ae3562d067d3a5d44cc9d507b44 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Mon, 4 Mar 2024 19:35:17 -0500 Subject: [PATCH 018/461] fixup: Infinite loop in PreferenceUtils after adopting DI --- .../src/main/java/im/angry/openeuicc/util/PreferenceUtils.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 accbbb5..b9a854e 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 @@ -14,7 +14,7 @@ import kotlinx.coroutines.flow.map private val Context.dataStore: DataStore by preferencesDataStore(name = "prefs") val Context.preferenceRepository: PreferenceRepository - get() = (applicationContext as OpenEuiccApplication).preferenceRepository + get() = (applicationContext as OpenEuiccApplication).appContainer.preferenceRepository val Fragment.preferenceRepository: PreferenceRepository get() = requireContext().preferenceRepository From 09e19412e31330261dc1c112e1a6dac34ac9aaf6 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Mon, 4 Mar 2024 19:59:10 -0500 Subject: [PATCH 019/461] fix: Show less logs in UI than what we will save ...to avoid the UI getting stuck due to the sheer amount of lines. --- .../main/java/im/angry/openeuicc/ui/LogsActivity.kt | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/LogsActivity.kt b/app-common/src/main/java/im/angry/openeuicc/ui/LogsActivity.kt index 1a7e50f..70f6992 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/LogsActivity.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/LogsActivity.kt @@ -23,13 +23,15 @@ class LogsActivity : AppCompatActivity() { private lateinit var swipeRefresh: SwipeRefreshLayout private lateinit var scrollView: ScrollView private lateinit var logText: TextView + private lateinit var logStr: String private val saveLogs = registerForActivityResult(ActivityResultContracts.CreateDocument("text/plain")) { uri -> if (uri == null) return@registerForActivityResult + if (!this::logStr.isInitialized) return@registerForActivityResult contentResolver.openFileDescriptor(uri, "w")?.use { FileOutputStream(it.fileDescriptor).use { os -> - os.write(logText.text.toString().encodeToByteArray()) + os.write(logStr.encodeToByteArray()) } } } @@ -76,7 +78,12 @@ class LogsActivity : AppCompatActivity() { private suspend fun reload() = withContext(Dispatchers.Main) { swipeRefresh.isRefreshing = true - logText.text = intent.extras?.getString("log") ?: readSelfLog() + logStr = intent.extras?.getString("log") ?: readSelfLog() + + logText.text = withContext(Dispatchers.IO) { + // Limit the UI to display only 256 lines + logStr.lines().takeLast(256).joinToString("\n") + } swipeRefresh.isRefreshing = false From ca0085e14733906ad20474b8246633b91df272b3 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Mon, 4 Mar 2024 20:07:26 -0500 Subject: [PATCH 020/461] fix: Do not crash if a certificate key ID is not known Fixes #17. --- .../main/java/net/typeblog/lpac_jni/impl/RootCertificates.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/RootCertificates.kt b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/RootCertificates.kt index f3e5e90..13848b6 100644 --- a/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/RootCertificates.kt +++ b/libs/lpac-jni/src/main/java/net/typeblog/lpac_jni/impl/RootCertificates.kt @@ -8,7 +8,7 @@ import java.security.cert.CertificateFactory const val DEFAULT_PKID_GSMA_RSP2_ROOT_CI1 = "81370f5125d0b1d408d4c3b232e6d25e795bebfb" private fun getCertificate(keyId: String): Certificate? = - KNOWN_CI_CERTS[keyId]?.toByteArray().let { cert -> + KNOWN_CI_CERTS[keyId]?.toByteArray()?.let { cert -> ByteArrayInputStream(cert).use { stream -> val cf = CertificateFactory.getInstance("X.509") cf.generateCertificate(stream) From 8ee3c53492a75bf3606198c7e579babf59b3acdc Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Tue, 5 Mar 2024 20:07:49 -0500 Subject: [PATCH 021/461] buildSrc: Use HEAD rev count as version code In Actions, we do not always have a checkout of the master branch. This only applies to release builds anyway. For debug builds, we always use the timestamp. --- buildSrc/src/main/kotlin/im/angry/openeuicc/build/Versioning.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildSrc/src/main/kotlin/im/angry/openeuicc/build/Versioning.kt b/buildSrc/src/main/kotlin/im/angry/openeuicc/build/Versioning.kt index 2c77fd3..24f0235 100644 --- a/buildSrc/src/main/kotlin/im/angry/openeuicc/build/Versioning.kt +++ b/buildSrc/src/main/kotlin/im/angry/openeuicc/build/Versioning.kt @@ -12,7 +12,7 @@ val Project.gitVersionCode: Int try { val stdout = ByteArrayOutputStream() exec { - commandLine("git", "rev-list", "--first-parent", "--count", "master") + commandLine("git", "rev-list", "--first-parent", "--count", "HEAD") standardOutput = stdout } stdout.toString("utf-8").trim('\n').toInt() From 124d1690abacfcf728bf7c160798f2721313c2e6 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Tue, 5 Mar 2024 20:12:19 -0500 Subject: [PATCH 022/461] fix: Clear status icon when compat check items are recycled --- .../im/angry/openeuicc/ui/CompatibilityCheckActivity.kt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/app-unpriv/src/main/java/im/angry/openeuicc/ui/CompatibilityCheckActivity.kt b/app-unpriv/src/main/java/im/angry/openeuicc/ui/CompatibilityCheckActivity.kt index 0eb433a..b7c8cf8 100644 --- a/app-unpriv/src/main/java/im/angry/openeuicc/ui/CompatibilityCheckActivity.kt +++ b/app-unpriv/src/main/java/im/angry/openeuicc/ui/CompatibilityCheckActivity.kt @@ -7,6 +7,7 @@ import android.view.View import android.view.ViewGroup import android.widget.TextView import androidx.appcompat.app.AppCompatActivity +import androidx.core.view.children import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.LinearLayoutManager @@ -52,11 +53,16 @@ class CompatibilityCheckActivity: AppCompatActivity() { inner class ViewHolder(private val root: View): RecyclerView.ViewHolder(root) { private val titleView: TextView = root.findViewById(R.id.compatibility_check_title) private val descView: TextView = root.findViewById(R.id.compatibility_check_desc) + private val statusContainer: ViewGroup = root.findViewById(R.id.compatibility_check_status_container) fun bindItem(item: CompatibilityCheck) { titleView.text = item.title descView.text = item.description + statusContainer.children.forEach { + it.visibility = View.GONE + } + when (item.state) { CompatibilityCheck.State.SUCCESS -> { root.findViewById(R.id.compatibility_check_checkmark).visibility = View.VISIBLE From 348395c48da26aae77f4cd87aff3b96dc57fa866 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Tue, 5 Mar 2024 20:14:21 -0500 Subject: [PATCH 023/461] chore: Bump lpac --- libs/lpac-jni/src/main/jni/lpac | 2 +- libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/libs/lpac-jni/src/main/jni/lpac b/libs/lpac-jni/src/main/jni/lpac index 47f44f9..dc09c3e 160000 --- a/libs/lpac-jni/src/main/jni/lpac +++ b/libs/lpac-jni/src/main/jni/lpac @@ -1 +1 @@ -Subproject commit 47f44f911099bffdbdb4854500578ba18ab19d06 +Subproject commit dc09c3e668fc191694e73fede255a7a0a26f4988 diff --git a/libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.c b/libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.c index 6cdda00..503f816 100644 --- a/libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.c +++ b/libs/lpac-jni/src/main/jni/lpac-jni/lpac-jni.c @@ -236,7 +236,7 @@ Java_net_typeblog_lpac_1jni_LpacJni_es10cGetProfilesInfo(JNIEnv *env, jobject th (*env)->DeleteLocalRef(env, jinfo); }); - es10c_profile_info_free_all(info); + es10c_profile_info_list_free_all(info); return ret; } From 3a0d805eb268c42dc53d2014f0b62ec7937b45c8 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Mon, 11 Mar 2024 18:55:32 -0400 Subject: [PATCH 024/461] treewide: Nullability fixes for AOSP 14 r29 Mainly a treewide `findViewById` -> `requireViewById`, with miscellaneous fixes. --- .../openeuicc/ui/EuiccManagementFragment.kt | 16 ++++++++-------- .../java/im/angry/openeuicc/ui/LogsActivity.kt | 8 ++++---- .../java/im/angry/openeuicc/ui/MainActivity.kt | 4 ++-- .../openeuicc/ui/NotificationsActivity.kt | 10 +++++----- .../openeuicc/ui/ProfileDownloadFragment.kt | 14 +++++++------- .../openeuicc/ui/ProfileRenameFragment.kt | 6 +++--- .../im/angry/openeuicc/ui/SettingsActivity.kt | 2 +- .../angry/openeuicc/ui/SlotSelectFragment.kt | 4 ++-- .../LongSummaryPreferenceCategory.kt | 2 +- .../main/java/im/angry/openeuicc/util/Utils.kt | 4 ++-- .../openeuicc/ui/CompatibilityCheckActivity.kt | 18 +++++++++--------- .../openeuicc/service/OpenEuiccService.kt | 6 +++--- .../java/im/angry/openeuicc/ui/LuiActivity.kt | 4 ++-- .../ui/PrivilegedEuiccManagementFragment.kt | 2 +- .../angry/openeuicc/ui/SlotMappingFragment.kt | 10 +++++----- .../util/PrivilegedTelephonyCompat.kt | 6 +++--- 16 files changed, 58 insertions(+), 58 deletions(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt index d2e7caa..7564cc8 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt @@ -58,9 +58,9 @@ open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener, ): View { val view = inflater.inflate(R.layout.fragment_euicc, container, false) - swipeRefresh = view.findViewById(R.id.swipe_refresh) - fab = view.findViewById(R.id.fab) - profileList = view.findViewById(R.id.profile_list) + swipeRefresh = view.requireViewById(R.id.swipe_refresh) + fab = view.requireViewById(R.id.fab) + profileList = view.requireViewById(R.id.profile_list) return view } @@ -191,11 +191,11 @@ open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener, } inner class ProfileViewHolder(private val root: View) : ViewHolder(root) { - private val iccid: TextView = root.findViewById(R.id.iccid) - private val name: TextView = root.findViewById(R.id.name) - private val state: TextView = root.findViewById(R.id.state) - private val provider: TextView = root.findViewById(R.id.provider) - private val profileMenu: ImageButton = root.findViewById(R.id.profile_menu) + private val iccid: TextView = root.requireViewById(R.id.iccid) + private val name: TextView = root.requireViewById(R.id.name) + private val state: TextView = root.requireViewById(R.id.state) + private val provider: TextView = root.requireViewById(R.id.provider) + private val profileMenu: ImageButton = root.requireViewById(R.id.profile_menu) init { iccid.setOnClickListener { diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/LogsActivity.kt b/app-common/src/main/java/im/angry/openeuicc/ui/LogsActivity.kt index 70f6992..2b36468 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/LogsActivity.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/LogsActivity.kt @@ -39,12 +39,12 @@ class LogsActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_logs) - setSupportActionBar(findViewById(R.id.toolbar)) + setSupportActionBar(requireViewById(R.id.toolbar)) supportActionBar!!.setDisplayHomeAsUpEnabled(true) - swipeRefresh = findViewById(R.id.swipe_refresh) - scrollView = findViewById(R.id.scroll_view) - logText = findViewById(R.id.log_text) + swipeRefresh = requireViewById(R.id.swipe_refresh) + scrollView = requireViewById(R.id.scroll_view) + logText = requireViewById(R.id.log_text) swipeRefresh.setOnRefreshListener { lifecycleScope.launch { diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/MainActivity.kt b/app-common/src/main/java/im/angry/openeuicc/ui/MainActivity.kt index ca6715d..c7699e0 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/MainActivity.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/MainActivity.kt @@ -36,9 +36,9 @@ open class MainActivity : AppCompatActivity(), OpenEuiccContextMarker { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) - setSupportActionBar(findViewById(R.id.toolbar)) + setSupportActionBar(requireViewById(R.id.toolbar)) - noEuiccPlaceholder = findViewById(R.id.no_euicc_placeholder) + noEuiccPlaceholder = requireViewById(R.id.no_euicc_placeholder) tm = telephonyManager diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/NotificationsActivity.kt b/app-common/src/main/java/im/angry/openeuicc/ui/NotificationsActivity.kt index 87c5ba3..925ed84 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/NotificationsActivity.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/NotificationsActivity.kt @@ -37,14 +37,14 @@ class NotificationsActivity: AppCompatActivity(), OpenEuiccContextMarker { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_notifications) - setSupportActionBar(findViewById(R.id.toolbar)) + setSupportActionBar(requireViewById(R.id.toolbar)) supportActionBar!!.setDisplayHomeAsUpEnabled(true) euiccChannel = euiccChannelManager .findEuiccChannelBySlotBlocking(intent.getIntExtra("logicalSlotId", 0))!! - swipeRefresh = findViewById(R.id.swipe_refresh) - notificationList = findViewById(R.id.recycler_view) + swipeRefresh = requireViewById(R.id.swipe_refresh) + notificationList = requireViewById(R.id.recycler_view) notificationList.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) @@ -118,8 +118,8 @@ class NotificationsActivity: AppCompatActivity(), OpenEuiccContextMarker { @SuppressLint("ClickableViewAccessibility") inner class NotificationViewHolder(private val root: View): RecyclerView.ViewHolder(root), View.OnCreateContextMenuListener, OnMenuItemClickListener { - private val address: TextView = root.findViewById(R.id.notification_address) - private val profileName: TextView = root.findViewById(R.id.notification_profile_name) + private val address: TextView = root.requireViewById(R.id.notification_address) + private val profileName: TextView = root.requireViewById(R.id.notification_profile_name) private lateinit var notification: LocalProfileNotificationWrapper diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDownloadFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDownloadFragment.kt index 43fc1ee..8ab09d9 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDownloadFragment.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/ProfileDownloadFragment.kt @@ -69,13 +69,13 @@ class ProfileDownloadFragment : BaseMaterialDialogFragment(), ): View { val view = inflater.inflate(R.layout.fragment_profile_download, container, false) - toolbar = view.findViewById(R.id.toolbar) - profileDownloadServer = view.findViewById(R.id.profile_download_server) - profileDownloadCode = view.findViewById(R.id.profile_download_code) - profileDownloadConfirmationCode = view.findViewById(R.id.profile_download_confirmation_code) - profileDownloadIMEI = view.findViewById(R.id.profile_download_imei) - profileDownloadFreeSpace = view.findViewById(R.id.profile_download_free_space) - progress = view.findViewById(R.id.progress) + toolbar = view.requireViewById(R.id.toolbar) + profileDownloadServer = view.requireViewById(R.id.profile_download_server) + profileDownloadCode = view.requireViewById(R.id.profile_download_code) + profileDownloadConfirmationCode = view.requireViewById(R.id.profile_download_confirmation_code) + profileDownloadIMEI = view.requireViewById(R.id.profile_download_imei) + profileDownloadFreeSpace = view.requireViewById(R.id.profile_download_free_space) + progress = view.requireViewById(R.id.progress) toolbar.inflateMenu(R.menu.fragment_profile_download) diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/ProfileRenameFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/ProfileRenameFragment.kt index 7ebac39..7987117 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/ProfileRenameFragment.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/ProfileRenameFragment.kt @@ -46,9 +46,9 @@ class ProfileRenameFragment : BaseMaterialDialogFragment(), EuiccChannelFragment ): View { val view = inflater.inflate(R.layout.fragment_profile_rename, container, false) - toolbar = view.findViewById(R.id.toolbar) - profileRenameNewName = view.findViewById(R.id.profile_rename_new_name) - progress = view.findViewById(R.id.progress) + toolbar = view.requireViewById(R.id.toolbar) + profileRenameNewName = view.requireViewById(R.id.profile_rename_new_name) + progress = view.requireViewById(R.id.progress) toolbar.inflateMenu(R.menu.fragment_profile_rename) diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/SettingsActivity.kt b/app-common/src/main/java/im/angry/openeuicc/ui/SettingsActivity.kt index f5bb4d4..426c546 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/SettingsActivity.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/SettingsActivity.kt @@ -9,7 +9,7 @@ class SettingsActivity: AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_settings) - setSupportActionBar(findViewById(R.id.toolbar)) + setSupportActionBar(requireViewById(R.id.toolbar)) supportActionBar!!.setDisplayHomeAsUpEnabled(true) supportFragmentManager.beginTransaction() .replace(R.id.settings_container, SettingsFragment()) diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/SlotSelectFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/SlotSelectFragment.kt index 9bdcbc1..6288b0d 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/SlotSelectFragment.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/SlotSelectFragment.kt @@ -39,13 +39,13 @@ class SlotSelectFragment : BaseMaterialDialogFragment(), OpenEuiccContextMarker ): View? { val view = inflater.inflate(R.layout.fragment_slot_select, container, false) - toolbar = view.findViewById(R.id.toolbar) + toolbar = view.requireViewById(R.id.toolbar) toolbar.setTitle(R.string.slot_select) toolbar.inflateMenu(R.menu.fragment_slot_select) val adapter = ArrayAdapter(inflater.context, R.layout.spinner_item) - spinner = view.findViewById(R.id.spinner) + spinner = view.requireViewById(R.id.spinner) spinner.adapter = adapter channels.forEach { channel -> diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/preference/LongSummaryPreferenceCategory.kt b/app-common/src/main/java/im/angry/openeuicc/ui/preference/LongSummaryPreferenceCategory.kt index a35e897..dc16e15 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/preference/LongSummaryPreferenceCategory.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/preference/LongSummaryPreferenceCategory.kt @@ -18,4 +18,4 @@ class LongSummaryPreferenceCategory: PreferenceCategory { summaryText.isSingleLine = false summaryText.maxLines = 10 } -} \ No newline at end of file +} 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 33699d7..175e85f 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 @@ -24,7 +24,7 @@ val Context.selfAppVersion: String get() = try { val pInfo = packageManager.getPackageInfo(packageName, 0) - pInfo.versionName + pInfo.versionName!! } catch (e: PackageManager.NameNotFoundException) { throw RuntimeException(e) } @@ -91,4 +91,4 @@ suspend fun connectSEService(context: Context): SEService = suspendCoroutine { c } } } -} \ No newline at end of file +} diff --git a/app-unpriv/src/main/java/im/angry/openeuicc/ui/CompatibilityCheckActivity.kt b/app-unpriv/src/main/java/im/angry/openeuicc/ui/CompatibilityCheckActivity.kt index b7c8cf8..b747bef 100644 --- a/app-unpriv/src/main/java/im/angry/openeuicc/ui/CompatibilityCheckActivity.kt +++ b/app-unpriv/src/main/java/im/angry/openeuicc/ui/CompatibilityCheckActivity.kt @@ -24,10 +24,10 @@ class CompatibilityCheckActivity: AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_compatibility_check) - setSupportActionBar(findViewById(R.id.toolbar)) + setSupportActionBar(requireViewById(R.id.toolbar)) supportActionBar!!.setDisplayHomeAsUpEnabled(true) - compatibilityCheckList = findViewById(R.id.recycler_view) + compatibilityCheckList = requireViewById(R.id.recycler_view) compatibilityCheckList.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) compatibilityCheckList.addItemDecoration(DividerItemDecoration(this, LinearLayoutManager.VERTICAL)) @@ -51,9 +51,9 @@ class CompatibilityCheckActivity: AppCompatActivity() { } inner class ViewHolder(private val root: View): RecyclerView.ViewHolder(root) { - private val titleView: TextView = root.findViewById(R.id.compatibility_check_title) - private val descView: TextView = root.findViewById(R.id.compatibility_check_desc) - private val statusContainer: ViewGroup = root.findViewById(R.id.compatibility_check_status_container) + private val titleView: TextView = root.requireViewById(R.id.compatibility_check_title) + private val descView: TextView = root.requireViewById(R.id.compatibility_check_desc) + private val statusContainer: ViewGroup = root.requireViewById(R.id.compatibility_check_status_container) fun bindItem(item: CompatibilityCheck) { titleView.text = item.title @@ -65,16 +65,16 @@ class CompatibilityCheckActivity: AppCompatActivity() { when (item.state) { CompatibilityCheck.State.SUCCESS -> { - root.findViewById(R.id.compatibility_check_checkmark).visibility = View.VISIBLE + root.requireViewById(R.id.compatibility_check_checkmark).visibility = View.VISIBLE } CompatibilityCheck.State.FAILURE -> { - root.findViewById(R.id.compatibility_check_error).visibility = View.VISIBLE + root.requireViewById(R.id.compatibility_check_error).visibility = View.VISIBLE } CompatibilityCheck.State.FAILURE_UNKNOWN -> { - root.findViewById(R.id.compatibility_check_unknown).visibility = View.VISIBLE + root.requireViewById(R.id.compatibility_check_unknown).visibility = View.VISIBLE } else -> { - root.findViewById(R.id.compatibility_check_progress_bar).visibility = View.VISIBLE + root.requireViewById(R.id.compatibility_check_progress_bar).visibility = View.VISIBLE } } } diff --git a/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt b/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt index 23f1444..3c9eaf2 100644 --- a/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt +++ b/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt @@ -104,9 +104,9 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker { return GetDefaultDownloadableSubscriptionListResult(RESULT_OK, arrayOf()) } - override fun onGetEuiccProfileInfoList(slotId: Int): GetEuiccProfileInfoListResult? { + override fun onGetEuiccProfileInfoList(slotId: Int): GetEuiccProfileInfoListResult { Log.i(TAG, "onGetEuiccProfileInfoList slotId=$slotId") - val channel = findChannel(slotId) ?: return null + val channel = findChannel(slotId)!! val profiles = channel.lpa.profiles.operational.map { EuiccProfileInfo.Builder(it.iccid).apply { setProfileName(it.name) @@ -256,4 +256,4 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker { // No-op -- we do not care return RESULT_FIRST_USER } -} \ No newline at end of file +} diff --git a/app/src/main/java/im/angry/openeuicc/ui/LuiActivity.kt b/app/src/main/java/im/angry/openeuicc/ui/LuiActivity.kt index e60dfa7..f12c9b4 100644 --- a/app/src/main/java/im/angry/openeuicc/ui/LuiActivity.kt +++ b/app/src/main/java/im/angry/openeuicc/ui/LuiActivity.kt @@ -10,10 +10,10 @@ class LuiActivity : AppCompatActivity() { super.onStart() setContentView(R.layout.activity_lui) - findViewById(R.id.lui_skip).setOnClickListener { finish() } + requireViewById(R.id.lui_skip).setOnClickListener { finish() } // TODO: Deactivate LuiActivity if there is no eSIM found. // TODO: Support pre-filled download info (from carrier apps); UX - findViewById(R.id.lui_download).setOnClickListener { + requireViewById(R.id.lui_download).setOnClickListener { startActivity(Intent(this, DirectProfileDownloadActivity::class.java)) } } diff --git a/app/src/main/java/im/angry/openeuicc/ui/PrivilegedEuiccManagementFragment.kt b/app/src/main/java/im/angry/openeuicc/ui/PrivilegedEuiccManagementFragment.kt index 78fcd4f..744acb8 100644 --- a/app/src/main/java/im/angry/openeuicc/ui/PrivilegedEuiccManagementFragment.kt +++ b/app/src/main/java/im/angry/openeuicc/ui/PrivilegedEuiccManagementFragment.kt @@ -17,7 +17,7 @@ class PrivilegedEuiccManagementFragment: EuiccManagementFragment() { override suspend fun onCreateFooterViews(parent: ViewGroup): List = if (channel.isMEP) { val view = layoutInflater.inflate(R.layout.footer_mep, parent, false) - view.findViewById