forked from PeterCxy/OpenEUICC
Compare commits
No commits in common. "8a17e5c0581b5ae9b330831b613176cad2a0df23" and "7b605fd44a570fa3503fe942f09ff9c5241877cc" have entirely different histories.
8a17e5c058
...
7b605fd44a
13 changed files with 10 additions and 65 deletions
|
@ -7,13 +7,10 @@ There are two variants of this project:
|
||||||
- OpenEUICC: The full-fledged privileged variant.
|
- 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.
|
- 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).
|
- 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.
|
- EasyEUICC: Unprivileged version that can run as a user app.
|
||||||
- This version supports two modes of operation:
|
- 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.
|
||||||
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)
|
- 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 when inserted, include the ARA-M hash `2A2FA878BC7C3354C2CF82935A5945A3EDAE4AFA`
|
- For removable eSIM chip vendors: to have your chip supported by official builds of EasyEUICC, include the ARA-M hash `2A2FA878BC7C3354C2CF82935A5945A3EDAE4AFA`
|
||||||
|
|
||||||
Building (Gradle)
|
Building (Gradle)
|
||||||
===
|
===
|
||||||
|
|
|
@ -3,7 +3,6 @@ package im.angry.openeuicc.core
|
||||||
import android.se.omapi.Channel
|
import android.se.omapi.Channel
|
||||||
import android.se.omapi.SEService
|
import android.se.omapi.SEService
|
||||||
import android.se.omapi.Session
|
import android.se.omapi.Session
|
||||||
import android.util.Log
|
|
||||||
import im.angry.openeuicc.util.*
|
import im.angry.openeuicc.util.*
|
||||||
import net.typeblog.lpac_jni.ApduInterface
|
import net.typeblog.lpac_jni.ApduInterface
|
||||||
|
|
||||||
|
@ -11,10 +10,6 @@ class OmapiApduInterface(
|
||||||
private val service: SEService,
|
private val service: SEService,
|
||||||
private val port: UiccPortInfoCompat
|
private val port: UiccPortInfoCompat
|
||||||
): ApduInterface {
|
): ApduInterface {
|
||||||
companion object {
|
|
||||||
const val TAG = "OmapiApduInterface"
|
|
||||||
}
|
|
||||||
|
|
||||||
private lateinit var session: Session
|
private lateinit var session: Session
|
||||||
private lateinit var lastChannel: Channel
|
private lateinit var lastChannel: Channel
|
||||||
|
|
||||||
|
@ -49,17 +44,7 @@ class OmapiApduInterface(
|
||||||
"Unknown channel"
|
"Unknown channel"
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.d(TAG, "OMAPI APDU: ${tx.encodeHex()}")
|
return lastChannel.transmit(tx)
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,8 +1,6 @@
|
||||||
package im.angry.openeuicc.ui
|
package im.angry.openeuicc.ui
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.content.ClipData
|
|
||||||
import android.content.ClipboardManager
|
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.text.method.PasswordTransformationMethod
|
import android.text.method.PasswordTransformationMethod
|
||||||
|
@ -295,14 +293,6 @@ 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() }
|
profileMenu.setOnClickListener { showOptionsMenu() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -146,7 +146,7 @@ open class MainActivity : BaseEuiccAccessActivity(), OpenEuiccContextMarker {
|
||||||
// If USB readers exist, add them at the very last
|
// If USB readers exist, add them at the very last
|
||||||
// We use a wrapper fragment to handle logic specific to USB readers
|
// We use a wrapper fragment to handle logic specific to USB readers
|
||||||
usbDevice?.let {
|
usbDevice?.let {
|
||||||
pages.add(Page(it.productName ?: getString(R.string.usb)) { UsbCcidReaderFragment() })
|
pages.add(Page(it.productName ?: "USB") { UsbCcidReaderFragment() })
|
||||||
}
|
}
|
||||||
viewPager.visibility = View.VISIBLE
|
viewPager.visibility = View.VISIBLE
|
||||||
|
|
||||||
|
|
|
@ -20,7 +20,6 @@ import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
import im.angry.openeuicc.common.R
|
import im.angry.openeuicc.common.R
|
||||||
import im.angry.openeuicc.core.EuiccChannel
|
import im.angry.openeuicc.core.EuiccChannel
|
||||||
import im.angry.openeuicc.core.EuiccChannelManager
|
|
||||||
import im.angry.openeuicc.util.*
|
import im.angry.openeuicc.util.*
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
@ -54,16 +53,6 @@ class NotificationsActivity: BaseEuiccAccessActivity(), OpenEuiccContextMarker {
|
||||||
notificationList.adapter = notificationAdapter
|
notificationList.adapter = notificationAdapter
|
||||||
registerForContextMenu(notificationList)
|
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 {
|
swipeRefresh.setOnRefreshListener {
|
||||||
refresh()
|
refresh()
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,9 +44,6 @@ class SettingsFragment: PreferenceFragmentCompat() {
|
||||||
|
|
||||||
findPreference<CheckBoxPreference>("pref_advanced_disable_safeguard_removable_esim")
|
findPreference<CheckBoxPreference>("pref_advanced_disable_safeguard_removable_esim")
|
||||||
?.bindBooleanFlow(preferenceRepository.disableSafeguardFlow, PreferenceKeys.DISABLE_SAFEGUARD_REMOVABLE_ESIM)
|
?.bindBooleanFlow(preferenceRepository.disableSafeguardFlow, PreferenceKeys.DISABLE_SAFEGUARD_REMOVABLE_ESIM)
|
||||||
|
|
||||||
findPreference<CheckBoxPreference>("pref_advanced_operational_profiles_only")
|
|
||||||
?.bindBooleanFlow(preferenceRepository.operationalOnlyFlow, PreferenceKeys.OPERATIONAL_PROFILES_ONLY)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun CheckBoxPreference.bindBooleanFlow(flow: Flow<Boolean>, key: Preferences.Key<Boolean>) {
|
private fun CheckBoxPreference.bindBooleanFlow(flow: Flow<Boolean>, key: Preferences.Key<Boolean>) {
|
||||||
|
|
|
@ -24,7 +24,6 @@ object PreferenceKeys {
|
||||||
val NOTIFICATION_DELETE = booleanPreferencesKey("notification_delete")
|
val NOTIFICATION_DELETE = booleanPreferencesKey("notification_delete")
|
||||||
val NOTIFICATION_SWITCH = booleanPreferencesKey("notification_switch")
|
val NOTIFICATION_SWITCH = booleanPreferencesKey("notification_switch")
|
||||||
val DISABLE_SAFEGUARD_REMOVABLE_ESIM = booleanPreferencesKey("disable_safeguard_removable_esim")
|
val DISABLE_SAFEGUARD_REMOVABLE_ESIM = booleanPreferencesKey("disable_safeguard_removable_esim")
|
||||||
val OPERATIONAL_PROFILES_ONLY = booleanPreferencesKey("operational_profiles_only")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class PreferenceRepository(context: Context) {
|
class PreferenceRepository(context: Context) {
|
||||||
|
@ -45,9 +44,6 @@ class PreferenceRepository(context: Context) {
|
||||||
val disableSafeguardFlow: Flow<Boolean> =
|
val disableSafeguardFlow: Flow<Boolean> =
|
||||||
dataStore.data.map { it[PreferenceKeys.DISABLE_SAFEGUARD_REMOVABLE_ESIM] ?: false }
|
dataStore.data.map { it[PreferenceKeys.DISABLE_SAFEGUARD_REMOVABLE_ESIM] ?: false }
|
||||||
|
|
||||||
val operationalOnlyFlow: Flow<Boolean> =
|
|
||||||
dataStore.data.map { it[PreferenceKeys.OPERATIONAL_PROFILES_ONLY] ?: true }
|
|
||||||
|
|
||||||
suspend fun <T> updatePreference(key: Preferences.Key<T>, value: T) {
|
suspend fun <T> updatePreference(key: Preferences.Key<T>, value: T) {
|
||||||
dataStore.edit {
|
dataStore.edit {
|
||||||
it[key] = value
|
it[key] = value
|
||||||
|
|
|
@ -3,7 +3,8 @@
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".ui.MainActivity">
|
||||||
|
|
||||||
<com.google.android.material.appbar.MaterialToolbar
|
<com.google.android.material.appbar.MaterialToolbar
|
||||||
android:id="@+id/toolbar"
|
android:id="@+id/toolbar"
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
android:id="@+id/fab"
|
android:id="@+id/fab"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginEnd="16dp"
|
android:layout_marginRight="16dp"
|
||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
android:src="@drawable/ic_add"
|
android:src="@drawable/ic_add"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
<string name="reload">Reload Slots</string>
|
<string name="reload">Reload Slots</string>
|
||||||
|
|
||||||
<string name="channel_name_format">Logical Slot %d</string>
|
<string name="channel_name_format">Logical Slot %d</string>
|
||||||
<string name="usb">USB</string>
|
|
||||||
|
|
||||||
<string name="enabled">Enabled</string>
|
<string name="enabled">Enabled</string>
|
||||||
<string name="disabled">Disabled</string>
|
<string name="disabled">Disabled</string>
|
||||||
|
@ -24,7 +23,6 @@
|
||||||
|
|
||||||
<string name="toast_profile_enable_failed">Cannot switch to new eSIM profile.</string>
|
<string name="toast_profile_enable_failed">Cannot switch to new eSIM profile.</string>
|
||||||
<string name="toast_profile_name_too_long">Nickname cannot be longer than 64 characters</string>
|
<string name="toast_profile_name_too_long">Nickname cannot be longer than 64 characters</string>
|
||||||
<string name="toast_iccid_copied">ICCID copied to clipboard</string>
|
|
||||||
|
|
||||||
<string name="slot_select">Select Slot</string>
|
<string name="slot_select">Select Slot</string>
|
||||||
<string name="slot_select_select">Select</string>
|
<string name="slot_select_select">Select</string>
|
||||||
|
@ -49,8 +47,7 @@
|
||||||
<string name="profile_delete_confirm">Are you sure you want to delete the profile %s? This operation is irreversible.</string>
|
<string name="profile_delete_confirm">Are you sure you want to delete the profile %s? This operation is irreversible.</string>
|
||||||
<string name="profile_delete_confirm_input">Type \'%s\' here to confirm deletion</string>
|
<string name="profile_delete_confirm_input">Type \'%s\' here to confirm deletion</string>
|
||||||
|
|
||||||
<string name="profile_notifications">Notifications</string>
|
<string name="profile_notifications">Profile Notifications</string>
|
||||||
<string name="profile_notifications_detailed_format">Notifications (%s)</string>
|
|
||||||
<string name="profile_notifications_show">Manage Notifications</string>
|
<string name="profile_notifications_show">Manage Notifications</string>
|
||||||
<string name="profile_notifications_help">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.</string>
|
<string name="profile_notifications_help">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.</string>
|
||||||
<string name="profile_notification_operation_download">Downloaded</string>
|
<string name="profile_notification_operation_download">Downloaded</string>
|
||||||
|
@ -76,8 +73,6 @@
|
||||||
<string name="pref_advanced">Advanced</string>
|
<string name="pref_advanced">Advanced</string>
|
||||||
<string name="pref_advanced_disable_safeguard_removable_esim">Disable Safeguards for Removable eSIMs</string>
|
<string name="pref_advanced_disable_safeguard_removable_esim">Disable Safeguards for Removable eSIMs</string>
|
||||||
<string name="pref_advanced_disable_safeguard_removable_esim_desc">By default, this app prevents you from disabling the active profile on a removable eSIM inserted in the device, because doing so may <i>sometimes</i> render it inaccessible.\nCheck this box to <i>remove</i> this safeguard.</string>
|
<string name="pref_advanced_disable_safeguard_removable_esim_desc">By default, this app prevents you from disabling the active profile on a removable eSIM inserted in the device, because doing so may <i>sometimes</i> render it inaccessible.\nCheck this box to <i>remove</i> this safeguard.</string>
|
||||||
<string name="pref_advanced_operational_profiles_only">Display only operational profiles</string>
|
|
||||||
<string name="pref_advanced_operational_profiles_only_desc">By default, this app lists only <i>operational</i> profiles. You can disable this to also see (and manage) <i>testing</i> and <i>bootstrap</i> profiles.</string>
|
|
||||||
<string name="pref_advanced_logs">Logs</string>
|
<string name="pref_advanced_logs">Logs</string>
|
||||||
<string name="pref_advanced_logs_desc">View recent debug logs of the application</string>
|
<string name="pref_advanced_logs_desc">View recent debug logs of the application</string>
|
||||||
<string name="pref_info">Info</string>
|
<string name="pref_info">Info</string>
|
||||||
|
|
|
@ -29,11 +29,6 @@
|
||||||
app:iconSpaceReserved="false"
|
app:iconSpaceReserved="false"
|
||||||
app:title="@string/pref_advanced_disable_safeguard_removable_esim"
|
app:title="@string/pref_advanced_disable_safeguard_removable_esim"
|
||||||
app:summary="@string/pref_advanced_disable_safeguard_removable_esim_desc" />
|
app:summary="@string/pref_advanced_disable_safeguard_removable_esim_desc" />
|
||||||
<CheckBoxPreference
|
|
||||||
app:key="pref_advanced_operational_profiles_only"
|
|
||||||
app:iconSpaceReserved="false"
|
|
||||||
app:title="@string/pref_advanced_operational_profiles_only"
|
|
||||||
app:summary="@string/pref_advanced_operational_profiles_only_desc" />
|
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
app:key="pref_advanced_logs"
|
app:key="pref_advanced_logs"
|
||||||
|
|
|
@ -193,7 +193,7 @@ internal class IsdrChannelAccessCheck(private val context: Context): Compatibili
|
||||||
|
|
||||||
internal class KnownBrokenCheck(private val context: Context): CompatibilityCheck(context) {
|
internal class KnownBrokenCheck(private val context: Context): CompatibilityCheck(context) {
|
||||||
companion object {
|
companion object {
|
||||||
val BROKEN_MANUFACTURERS = arrayOf("xiaomi", "huawei", "honor")
|
val BROKEN_MANUFACTURERS = arrayOf("xiaomi")
|
||||||
}
|
}
|
||||||
|
|
||||||
override val title: String
|
override val title: String
|
||||||
|
|
|
@ -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);
|
(*env)->CallVoidMethod(env, callback, on_state_update, download_state_finalizing);
|
||||||
// TODO: Expose error code as Java-side exceptions?
|
// TODO: Expose error code as Java-side exceptions?
|
||||||
ret = es10b_load_bound_profile_package(ctx, &es10b_load_bound_profile_package_result);
|
ret = es10b_load_bound_profile_package(ctx, &es10b_load_bound_profile_package_result);
|
||||||
syslog(LOG_INFO, "es10b_load_bound_profile_package %d, reason %d", ret, es10b_load_bound_profile_package_result.errorReason);
|
syslog(LOG_INFO, "es10b_load_bound_profile_package %d", ret);
|
||||||
|
|
||||||
out:
|
out:
|
||||||
euicc_http_cleanup(ctx);
|
euicc_http_cleanup(ctx);
|
||||||
|
|
Loading…
Add table
Reference in a new issue