From 6d200d14acce00a877c0cfa24b1bf4ae9c42a2ab Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Tue, 27 Feb 2024 19:50:29 -0500 Subject: [PATCH 001/458] 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 c710a1690..ce622de5c 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 0a216b326..c0bcee725 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 002/458] 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 84fa227b9..f3e5e9019 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 003/458] 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 072fcaa1b..b81a41ad9 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 004/458] 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 9a2a8559b..ba15e3731 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 b2690b0f4..dc90ab1ff 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 bce68312e..e72b11245 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 005/458] 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 ba15e3731..1a7e50f8f 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 000000000..aaee67852 --- /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 000000000..4fa3aeb0b --- /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 9d8932a6c..040c910fc 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 006/458] 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 e917aec23..0b2ccecd0 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 23c21b90b..e0317942f 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 000000000..8a6ab5456 --- /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 452ed7641..af6cbf455 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 dc90ab1ff..3c218982f 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 8b3ef996d..28ea49d2e 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 4675ab9c9..4cb493217 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 007/458] 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 0b2ccecd0..6cd22ce04 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 e0317942f..9033d3469 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 000000000..65c2aa91d --- /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 000000000..dd7826c2c --- /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 360da4ffc..43fc1ee96 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 3c218982f..57626e2b4 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 28ea49d2e..2d8e5eebf 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 288cebd6b..75b2b2240 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 000000000..a849a27a4 --- /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 6a08da5c4..23f144422 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 64e8ca9ab..1261932ac 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 ba60fd786..4c77bac0d 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 008/458] 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 3d9c47b5c..843c4874b 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 009/458] 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 843c4874b..93d49be40 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 3a20af1a8..8845a588e 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 010/458] 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 8845a588e..588c0735f 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 011/458] 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 93d49be40..ae5a4da8d 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 012/458] 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 65c2aa91d..a29fcf8ee 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 dd7826c2c..6a0383595 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 000000000..86af0076e --- /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 000000000..d311876e5 --- /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 af6cbf455..ca6715d95 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 a849a27a4..64e45eb6a 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 000000000..d3c5cdbde --- /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 1261932ac..f3c2b3c8a 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 013/458] 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 000000000..1883a11b1 --- /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 9033d3469..b0bce1daf 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 8a6ab5456..000000000 --- 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 a29fcf8ee..a62eff4db 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 6a0383595..9f051e88e 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 57626e2b4..33699d7eb 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 75b2b2240..0ebaa813f 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 64e45eb6a..dc3921ed0 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 4cb493217..4675ab9c9 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 014/458] 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 000000000..15e019127 --- /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 1883a11b1..a97867759 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 dc3921ed0..515835278 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 015/458] 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 accbbb52c..b9a854e32 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 016/458] 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 1a7e50f8f..70f6992b6 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 017/458] 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 f3e5e9019..13848b637 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 018/458] 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 2c77fd3f0..24f02358b 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 019/458] 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 0eb433a8e..b7c8cf803 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 020/458] 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 47f44f911..dc09c3e66 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 6cdda0086..503f81633 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 021/458] 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 d2e7caa22..7564cc88e 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 70f6992b6..2b3646838 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 ca6715d95..c7699e0f2 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 87c5ba342..925ed84b5 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 43fc1ee96..8ab09d914 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 7ebac3997..798711735 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 f5bb4d48a..426c54677 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 9bdcbc104..6288b0dfb 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 a35e897b1..dc16e15fb 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 33699d7eb..175e85fad 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 b7c8cf803..b747befe3 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 23f144422..3c9eaf26e 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 e60dfa720..f12c9b4eb 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 78fcd4f53..744acb8bf 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