From 27627fdf1579b0d89a0fe9561ef6d755fe994593 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sun, 14 Jul 2024 22:03:03 -0400 Subject: [PATCH 01/10] ui: Minor XML fixes `tools:context` is unused; fix RTL --- app-common/src/main/res/layout/activity_main.xml | 3 +-- app-common/src/main/res/layout/fragment_euicc.xml | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/app-common/src/main/res/layout/activity_main.xml b/app-common/src/main/res/layout/activity_main.xml index cb5f22464..a61a368f7 100644 --- a/app-common/src/main/res/layout/activity_main.xml +++ b/app-common/src/main/res/layout/activity_main.xml @@ -3,8 +3,7 @@ xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="match_parent" - tools:context=".ui.MainActivity"> + android:layout_height="match_parent"> Date: Sat, 20 Jul 2024 15:41:08 -0400 Subject: [PATCH 02/10] feat: Copy ICCID to clipboard on long click --- .../im/angry/openeuicc/ui/EuiccManagementFragment.kt | 10 ++++++++++ app-common/src/main/res/values/strings.xml | 1 + 2 files changed, 11 insertions(+) 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 d49fa70ee..3374f07b7 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 @@ -1,6 +1,8 @@ package im.angry.openeuicc.ui import android.annotation.SuppressLint +import android.content.ClipData +import android.content.ClipboardManager import android.content.Intent import android.os.Bundle import android.text.method.PasswordTransformationMethod @@ -293,6 +295,14 @@ open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener, } } + iccid.setOnLongClickListener { + requireContext().getSystemService(ClipboardManager::class.java) + .setPrimaryClip(ClipData.newPlainText("iccid", iccid.text)) + Toast.makeText(requireContext(), R.string.toast_iccid_copied, Toast.LENGTH_SHORT) + .show() + true + } + profileMenu.setOnClickListener { showOptionsMenu() } } diff --git a/app-common/src/main/res/values/strings.xml b/app-common/src/main/res/values/strings.xml index 4ad6071b3..a1a002a9b 100644 --- a/app-common/src/main/res/values/strings.xml +++ b/app-common/src/main/res/values/strings.xml @@ -23,6 +23,7 @@ Cannot switch to new eSIM profile. Nickname cannot be longer than 64 characters + ICCID copied to clipboard Select Slot Select From c84b114de0af0a2436b479cfbb4e2be200b05892 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sat, 20 Jul 2024 16:02:43 -0400 Subject: [PATCH 03/10] README: Update for USB readers --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 17d2cfc31..5616a327a 100644 --- a/README.md +++ b/README.md @@ -8,9 +8,11 @@ There are two variants of this project: - Due to its privilege requirement, OpenEUICC must be placed inside `/system/priv-app` and be signed with the platform certificate. - The preferred way to including OpenEUICC in a system image is to [build it along with AOSP](#building-aosp). - EasyEUICC: Unprivileged version that can run as a user app. - - Due to obvious security requirements, EasyEUICC is only able to access eSIM chips whose [ARF/ARA](https://source.android.com/docs/core/connect/uicc#arf) contains the hash of EasyEUICC's signing certificate. + - This version supports two modes of operation: + 1. Inserted, removable eSIMs: Due to obvious security requirements, EasyEUICC is only able to access eSIM chips whose [ARF/ARA](https://source.android.com/docs/core/connect/uicc#arf) contains the hash of EasyEUICC's signing certificate. + 2. USB CCID Card Readers: Only `T=0` readers that use the standard [USB CCID protocol](https://en.wikipedia.org/wiki/CCID_(protocol)) are supported. In this mode, EasyEUICC can access any eSIM chip loaded in the card reader regardless of their ARF/ARA, as long as they implement the [SGP.22 standard](https://www.gsma.com/solutions-and-impact/technologies/esim/wp-content/uploads/2021/07/SGP.22-v2.3.pdf). - Prebuilt release-mode EasyEUICC apks can be downloaded [here](https://gitea.angry.im/PeterCxy/OpenEUICC/releases) - - For removable eSIM chip vendors: to have your chip supported by official builds of EasyEUICC, include the ARA-M hash `2A2FA878BC7C3354C2CF82935A5945A3EDAE4AFA` + - For removable eSIM chip vendors: to have your chip supported by official builds of EasyEUICC when inserted, include the ARA-M hash `2A2FA878BC7C3354C2CF82935A5945A3EDAE4AFA` Building (Gradle) === From b55df25650bd5b3237da4faf1ced6196f10cdc0d Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sun, 21 Jul 2024 08:38:31 -0400 Subject: [PATCH 04/10] NotificationsActivity: display channel name in toolbar --- .../main/java/im/angry/openeuicc/ui/MainActivity.kt | 2 +- .../im/angry/openeuicc/ui/NotificationsActivity.kt | 11 +++++++++++ app-common/src/main/res/values/strings.xml | 4 +++- 3 files changed, 15 insertions(+), 2 deletions(-) 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 74fe38598..0f504ebbc 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 @@ -146,7 +146,7 @@ open class MainActivity : BaseEuiccAccessActivity(), OpenEuiccContextMarker { // If USB readers exist, add them at the very last // We use a wrapper fragment to handle logic specific to USB readers usbDevice?.let { - pages.add(Page(it.productName ?: "USB") { UsbCcidReaderFragment() }) + pages.add(Page(it.productName ?: getString(R.string.usb)) { UsbCcidReaderFragment() }) } viewPager.visibility = View.VISIBLE 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 9758d18df..59abbc563 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 @@ -20,6 +20,7 @@ import androidx.recyclerview.widget.RecyclerView import androidx.swiperefreshlayout.widget.SwipeRefreshLayout 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 @@ -53,6 +54,16 @@ class NotificationsActivity: BaseEuiccAccessActivity(), OpenEuiccContextMarker { notificationList.adapter = notificationAdapter registerForContextMenu(notificationList) + // This is slightly different from the MainActivity logic + // due to the length (we don't want to display the full USB product name) + val channelTitle = if (euiccChannel.logicalSlotId == EuiccChannelManager.USB_CHANNEL_ID) { + getString(R.string.usb) + } else { + getString(R.string.channel_name_format, euiccChannel.logicalSlotId) + } + + title = getString(R.string.profile_notifications_detailed_format, channelTitle) + swipeRefresh.setOnRefreshListener { refresh() } diff --git a/app-common/src/main/res/values/strings.xml b/app-common/src/main/res/values/strings.xml index a1a002a9b..7647815e2 100644 --- a/app-common/src/main/res/values/strings.xml +++ b/app-common/src/main/res/values/strings.xml @@ -7,6 +7,7 @@ Reload Slots Logical Slot %d + USB Enabled Disabled @@ -48,7 +49,8 @@ Are you sure you want to delete the profile %s? This operation is irreversible. Type \'%s\' here to confirm deletion - Profile Notifications + Notifications + Notifications (%s) Manage Notifications eSIM profiles can send notifications to the carrier when they are downloaded, deleted, enabled, or disabled. The queue of these notifications to be sent is listed here.\n\nIn Settings, you can specify whether to send each type of notification automatically. Note that even if a notification has been sent, it will not be deleted automatically from the record, unless the queue runs out of space.\n\nHere, you can manually send or delete each pending notification. Downloaded From 01fc07fd78026afbf524bd26c85b529c9be1e3fb Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sun, 11 Aug 2024 20:50:57 -0400 Subject: [PATCH 05/10] EuiccManagementFragment: Add nonnull assertion for platform types Fixes build within AOSP 14 source tree. --- .../main/java/im/angry/openeuicc/ui/EuiccManagementFragment.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 3374f07b7..ac0a99f8a 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 @@ -296,7 +296,7 @@ open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener, } iccid.setOnLongClickListener { - requireContext().getSystemService(ClipboardManager::class.java) + requireContext().getSystemService(ClipboardManager::class.java)!! .setPrimaryClip(ClipData.newPlainText("iccid", iccid.text)) Toast.makeText(requireContext(), R.string.toast_iccid_copied, Toast.LENGTH_SHORT) .show() From 44b85ffdea4b208513a78b86d90f2f90c680201a Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Wed, 14 Aug 2024 20:15:47 -0400 Subject: [PATCH 06/10] lpac-jni: Log load bpp error reason --- libs/lpac-jni/src/main/jni/lpac-jni/lpac-download.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/lpac-jni/src/main/jni/lpac-jni/lpac-download.c b/libs/lpac-jni/src/main/jni/lpac-jni/lpac-download.c index f8b27ff0b..91676daf2 100644 --- a/libs/lpac-jni/src/main/jni/lpac-jni/lpac-download.c +++ b/libs/lpac-jni/src/main/jni/lpac-jni/lpac-download.c @@ -114,7 +114,7 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, j (*env)->CallVoidMethod(env, callback, on_state_update, download_state_finalizing); // TODO: Expose error code as Java-side exceptions? ret = es10b_load_bound_profile_package(ctx, &es10b_load_bound_profile_package_result); - syslog(LOG_INFO, "es10b_load_bound_profile_package %d", ret); + syslog(LOG_INFO, "es10b_load_bound_profile_package %d, reason %d", ret, es10b_load_bound_profile_package_result.errorReason); out: euicc_http_cleanup(ctx); From 87ea017b36d7abec34838632dd69d8d0a897dfc4 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Wed, 14 Aug 2024 20:43:54 -0400 Subject: [PATCH 07/10] OmapiApduInterface: Log all APDU exchanges --- .../angry/openeuicc/core/OmapiApduInterface.kt | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/app-common/src/main/java/im/angry/openeuicc/core/OmapiApduInterface.kt b/app-common/src/main/java/im/angry/openeuicc/core/OmapiApduInterface.kt index 227977c90..711658adb 100644 --- a/app-common/src/main/java/im/angry/openeuicc/core/OmapiApduInterface.kt +++ b/app-common/src/main/java/im/angry/openeuicc/core/OmapiApduInterface.kt @@ -3,6 +3,7 @@ package im.angry.openeuicc.core import android.se.omapi.Channel import android.se.omapi.SEService import android.se.omapi.Session +import android.util.Log import im.angry.openeuicc.util.* import net.typeblog.lpac_jni.ApduInterface @@ -10,6 +11,10 @@ class OmapiApduInterface( private val service: SEService, private val port: UiccPortInfoCompat ): ApduInterface { + companion object { + const val TAG = "OmapiApduInterface" + } + private lateinit var session: Session private lateinit var lastChannel: Channel @@ -44,7 +49,17 @@ class OmapiApduInterface( "Unknown channel" } - return lastChannel.transmit(tx) + Log.d(TAG, "OMAPI APDU: ${tx.encodeHex()}") + + try { + return lastChannel.transmit(tx).also { + Log.d(TAG, "OMAPI APDU response: ${it.encodeHex()}") + } + } catch (e: Exception) { + Log.e(TAG, "OMAPI APDU exception") + e.printStackTrace() + throw e + } } } \ No newline at end of file From f073261b603c620f99cc081aba1ed80564591380 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sat, 17 Aug 2024 20:55:03 -0400 Subject: [PATCH 08/10] unpriv: Add Huawei and Honor into the blocklist --- .../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 49811bc55..8424c5507 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 @@ -193,7 +193,7 @@ internal class IsdrChannelAccessCheck(private val context: Context): Compatibili internal class KnownBrokenCheck(private val context: Context): CompatibilityCheck(context) { companion object { - val BROKEN_MANUFACTURERS = arrayOf("xiaomi") + val BROKEN_MANUFACTURERS = arrayOf("xiaomi", "huawei", "honor") } override val title: String From 7c07db0aab03925bb5e41f5c9602b238601b03f9 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Sat, 17 Aug 2024 20:57:25 -0400 Subject: [PATCH 09/10] README: Warn about non-standard external eSIMs --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5616a327a..24f4c9f47 100644 --- a/README.md +++ b/README.md @@ -7,6 +7,7 @@ There are two variants of this project: - OpenEUICC: The full-fledged privileged variant. - Due to its privilege requirement, OpenEUICC must be placed inside `/system/priv-app` and be signed with the platform certificate. - The preferred way to including OpenEUICC in a system image is to [build it along with AOSP](#building-aosp). + - __Note__: When privileged, OpenEUICC supports any eUICC chip that implements the SGP.22 standard, internal or external. However, there is __no guarantee__ that external (removable) eSIMs actually follow the standard. Please __DO NOT__ submit bug reports for non-functioning removable eSIMs. They are __NOT__ officially supported unless they also support / are supported by EasyEUICC, the unprivileged variant. - EasyEUICC: Unprivileged version that can run as a user app. - This version supports two modes of operation: 1. Inserted, removable eSIMs: Due to obvious security requirements, EasyEUICC is only able to access eSIM chips whose [ARF/ARA](https://source.android.com/docs/core/connect/uicc#arf) contains the hash of EasyEUICC's signing certificate. From 8a17e5c0581b5ae9b330831b613176cad2a0df23 Mon Sep 17 00:00:00 2001 From: Harald Welte Date: Thu, 15 Aug 2024 17:14:47 +0200 Subject: [PATCH 10/10] WIP: preference to un-hide test/bootstrap profiles --- .../src/main/java/im/angry/openeuicc/ui/SettingsFragment.kt | 3 +++ .../src/main/java/im/angry/openeuicc/util/PreferenceUtils.kt | 4 ++++ app-common/src/main/res/values/strings.xml | 2 ++ app-common/src/main/res/xml/pref_settings.xml | 5 +++++ 4 files changed, 14 insertions(+) diff --git a/app-common/src/main/java/im/angry/openeuicc/ui/SettingsFragment.kt b/app-common/src/main/java/im/angry/openeuicc/ui/SettingsFragment.kt index d471efab8..754cc5825 100644 --- a/app-common/src/main/java/im/angry/openeuicc/ui/SettingsFragment.kt +++ b/app-common/src/main/java/im/angry/openeuicc/ui/SettingsFragment.kt @@ -44,6 +44,9 @@ class SettingsFragment: PreferenceFragmentCompat() { findPreference("pref_advanced_disable_safeguard_removable_esim") ?.bindBooleanFlow(preferenceRepository.disableSafeguardFlow, PreferenceKeys.DISABLE_SAFEGUARD_REMOVABLE_ESIM) + + findPreference("pref_advanced_operational_profiles_only") + ?.bindBooleanFlow(preferenceRepository.operationalOnlyFlow, PreferenceKeys.OPERATIONAL_PROFILES_ONLY) } private fun CheckBoxPreference.bindBooleanFlow(flow: Flow, key: Preferences.Key) { 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 53153405e..f04b6a2a4 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 @@ -24,6 +24,7 @@ object PreferenceKeys { val NOTIFICATION_DELETE = booleanPreferencesKey("notification_delete") val NOTIFICATION_SWITCH = booleanPreferencesKey("notification_switch") val DISABLE_SAFEGUARD_REMOVABLE_ESIM = booleanPreferencesKey("disable_safeguard_removable_esim") + val OPERATIONAL_PROFILES_ONLY = booleanPreferencesKey("operational_profiles_only") } class PreferenceRepository(context: Context) { @@ -44,6 +45,9 @@ class PreferenceRepository(context: Context) { val disableSafeguardFlow: Flow = dataStore.data.map { it[PreferenceKeys.DISABLE_SAFEGUARD_REMOVABLE_ESIM] ?: false } + val operationalOnlyFlow: Flow = + dataStore.data.map { it[PreferenceKeys.OPERATIONAL_PROFILES_ONLY] ?: true } + suspend fun updatePreference(key: Preferences.Key, value: T) { dataStore.edit { it[key] = value diff --git a/app-common/src/main/res/values/strings.xml b/app-common/src/main/res/values/strings.xml index 7647815e2..d627ed4ad 100644 --- a/app-common/src/main/res/values/strings.xml +++ b/app-common/src/main/res/values/strings.xml @@ -76,6 +76,8 @@ Advanced Disable Safeguards for Removable eSIMs By default, this app prevents you from disabling the active profile on a removable eSIM inserted in the device, because doing so may sometimes render it inaccessible.\nCheck this box to remove this safeguard. + Display only operational profiles + By default, this app lists only operational profiles. You can disable this to also see (and manage) testing and bootstrap profiles. Logs View recent debug logs of the application Info diff --git a/app-common/src/main/res/xml/pref_settings.xml b/app-common/src/main/res/xml/pref_settings.xml index db348fc15..cc3d5b105 100644 --- a/app-common/src/main/res/xml/pref_settings.xml +++ b/app-common/src/main/res/xml/pref_settings.xml @@ -29,6 +29,11 @@ app:iconSpaceReserved="false" app:title="@string/pref_advanced_disable_safeguard_removable_esim" app:summary="@string/pref_advanced_disable_safeguard_removable_esim_desc" /> +