### Instantiating EuiccManagementFragment Source: https://context7.com/estkme-group/openeuicc/llms.txt Instantiate `EuiccManagementFragment` using `newInstance` for basic setup. For custom behavior in flavored builds, extend `EuiccManagementFragment` and override methods like `onCreateFooterViews` or `populatePopupWithProfileActions`. ```kotlin // Instantiate (use factory in practice) val frag = EuiccManagementFragment.newInstance( slotId = 0, portId = 0, seId = EuiccChannel.SecureElementId.DEFAULT ) // Override footer views (e.g., MEP slot mapping notice in privileged build) class PrivilegedEuiccManagementFragment : EuiccManagementFragment() { override suspend fun onCreateFooterViews( parent: ViewGroup, profiles: List ): List { val views = super.onCreateFooterViews(parent, profiles).toMutableList() if (port.card.isMultipleEnabledProfilesSupported) { views.add(layoutInflater.inflate(R.layout.footer_mep, parent, false)) } return views } // Expose disable option on internal eSIM (normally hidden as a safeguard) override fun populatePopupWithProfileActions(popup: PopupMenu, profile: LocalProfileInfo) { super.populatePopupWithProfileActions(popup, profile) popup.menu.findItem(R.id.disable)?.isVisible = true } } ``` -------------------------------- ### Launch DownloadWizardActivity Programmatically or via Deep Link Source: https://context7.com/estkme-group/openeuicc/llms.txt Demonstrates how to launch the DownloadWizardActivity either programmatically with a specific logical slot or via a deep link URI. The wizard guides users through profile download steps. ```kotlin val intent = DownloadWizardActivity.newIntent( context = context, logicalSlotId = 0, seId = EuiccChannel.SecureElementId.DEFAULT ) startActivity(intent) ``` ```kotlin val deepLinkIntent = Intent(Intent.ACTION_VIEW).apply { data = Uri.parse("LPA:1$smdp.example.com$MATCHING-ID$oid$cc-required") } startActivity(deepLinkIntent) ``` -------------------------------- ### Keystore Properties Configuration Source: https://github.com/estkme-group/openeuicc/blob/master/README.md A `keystore.properties` file is required in the root directory for signing the application. Ensure you have a Java-compatible keystore generated first. ```ini storePassword=my-store-password keyPassword=my-password keyAlias=my-key unprivKeyPassword=my-unpriv-password unprivKeyAlias=my-unpriv-key storeFile=/path/to/android/keystore ``` -------------------------------- ### Create and Use UsbCcidContext Source: https://context7.com/estkme-group/openeuicc/llms.txt Initializes a UsbCcidContext for a CCID reader, powers on the card, reads the ATR, and builds an APDU interface for communication. Ensure the USB device and interface are correctly obtained. ```kotlin val usbDevice: UsbDevice = /* from UsbManager.deviceList */ val usbInterface: UsbInterface = usbDevice.interfaces.smartCard!! val ccidCtx: UsbCcidContext? = UsbCcidContext.createFromUsbDevice( context, usbDevice, usbInterface ) if (ccidCtx != null) { ccidCtx.connect() // powers on the card, reads ATR println("ATR: ${ccidCtx.atr?.encodeHex()}") // Use the context to build a UsbApduInterface val apduIface = UsbApduInterface(ccidCtx) apduIface.connect() // sends TERMINAL CAPABILITY val handle = apduIface.logicalChannelOpen( "A0000005591010FFFFFFFF8900000100".decodeHex() ) // ... transmit APDUs ... apduIface.logicalChannelClose(handle) ccidCtx.allowDisconnect = true ccidCtx.disconnect() } ``` -------------------------------- ### Build Unprivileged EasyEUICC Source: https://github.com/estkme-group/openeuicc/blob/master/README.md Use this Gradle command to build the release version of the unprivileged EasyEUICC application. This requires the keystore configuration to be set up correctly. ```shell ./gradlew :app-unpriv:assembleRelease ``` -------------------------------- ### Build Privileged OpenEUICC Source: https://github.com/estkme-group/openeuicc/blob/master/README.md Use this Gradle command to build the release version of the privileged OpenEUICC application. This requires the keystore configuration to be set up correctly. ```shell ./gradlew :app:assembleRelease ``` -------------------------------- ### OpenEuiccService Framework Integration Methods Source: https://context7.com/estkme-group/openeuicc/llms.txt Demonstrates the system integration surface of `OpenEuiccService`, which is called by the Android framework. These methods expose profile management functionalities to carrier apps and system services. ```kotlin // Called by framework to get eID for a slot override fun onGetEid(slotId: Int): String? // → "89049032000000000000000000000000" ``` ```kotlin // Called by framework to list profiles (e.g. for Settings display) override fun onGetEuiccProfileInfoList(slotId: Int): GetEuiccProfileInfoListResult // Returns EuiccProfileInfo[] with state, name, ICCID, provider name, profile class ``` ```kotlin // Called by framework when a carrier app wants to switch subscription override fun onSwitchToSubscriptionWithPort( slotId: Int, portIndex: Int, iccid: String?, // null = disable current profile forceDeactivateSim: Boolean ): Int // RESULT_OK / RESULT_MUST_DEACTIVATE_SIM / RESULT_FIRST_USER ``` ```kotlin // Called by framework to delete a subscription override fun onDeleteSubscription(slotId: Int, iccid: String): Int ``` ```kotlin // Called by framework to rename a subscription override fun onUpdateSubscriptionNickname(slotId: Int, iccid: String, nickname: String?): Int ``` -------------------------------- ### Launch Profile Download Task Source: https://context7.com/estkme-group/openeuicc/llms.txt Initiates a profile download as a foreground Android Service task, displaying a persistent notification. Collects `ForegroundTaskState` updates for progress and completion. Requires binding to `EuiccChannelManagerService`. ```kotlin // Bind to EuiccChannelManagerService, then: val taskFlow: EuiccChannelManagerService.ForegroundTaskSubscriberFlow = service.launchProfileDownloadTask( slotId = 0, portId = 0, seId = EuiccChannel.SecureElementId.DEFAULT, input = ProfileDownloadInput( smdpAddress = "smdp.example.com", matchingId = "AABBCC-DDEEFF", confirmationCode = null, imei = null ) ) // Collect progress taskFlow.collect { state -> when (state) { is EuiccChannelManagerService.ForegroundTaskState.InProgress -> println("Progress: ${state.progress}% context=${state.context}") is EuiccChannelManagerService.ForegroundTaskState.Done -> if (state.error == null) println("Done!") else state.error.printStackTrace() else -> {} } } // Or simply await completion: val error: Throwable? = taskFlow.waitDone() if (error == null) println("Download successful") ``` -------------------------------- ### Manage User Settings with PreferenceRepository Flows Source: https://context7.com/estkme-group/openeuicc/llms.txt Exposes user-configurable settings as reactive Kotlin `Flow`s using Jetpack DataStore. Allows reading settings reactively, writing new values, and resetting to defaults. ```kotlin val prefs: PreferenceRepository = appContainer.preferenceRepository // Read settings as flows (reactive) prefs.verboseLoggingFlow.collect { enabled -> Log.d(TAG, "verbose=$enabled") } prefs.ignoreTLSCertificateFlow.collect { ignore -> /* dev option */ } prefs.refreshAfterSwitchFlow.collect { refresh -> /* send REFRESH after enable/disable */ } prefs.notificationDownloadFlow.collect { notify -> /* send LPA notification post-download */ } prefs.isdrAidListFlow.collect { aidListText -> /* newline-separated AID list with comments */ } ``` ```kotlin // Read once in a coroutine val unfilteredList = prefs.unfilteredProfileListFlow.first() val es10xMss = prefs.es10xMssFlow.first() // default 63 (0x3F) ``` ```kotlin // Write a preference prefs.verboseLoggingFlow.updatePreference(true) prefs.ignoreTLSCertificateFlow.updatePreference(false) prefs.isdrAidListFlow.updatePreference( """ A0000005591010FFFFFFFF8900000100 A0000005591010000000008900000300 """.trimIndent() ) ``` ```kotlin // Remove a preference (resets to default) prefs.verboseLoggingFlow.removePreference() ``` -------------------------------- ### Parse and Serialize Activation Codes with LPAString Source: https://context7.com/estkme-group/openeuicc/llms.txt Parses SGP.22 activation codes from various sources like QR codes or deep links, and serializes them back into the activation code string format. Handles the `lpa:` URI scheme. ```kotlin // Parse from a QR code scan or deep-link URI val lpa = LPAString.parse("LPA:1\$smdp.example.com\$MATCHING-ID\$\$1") println(lpa.address) // "smdp.example.com" println(lpa.matchingId) // "MATCHING-ID" println(lpa.oid) // null println(lpa.confirmationCodeRequired) // true ``` ```kotlin // Parse with LPA: prefix stripped automatically val lpa2 = LPAString.parse("1\$smdp.example.com\$ABC123") println(lpa2.address) // "smdp.example.com" println(lpa2.matchingId) // "ABC123" ``` ```kotlin // Serialize back to activation code string val str = LPAString("smdp.example.com", "MATCHING-ID", null, false).toString() println(str) // "1\$smdp.example.com\$MATCHING-ID" ``` ```kotlin // Use in DownloadWizardActivity deep link val intent = Intent(Intent.ACTION_VIEW, Uri.parse("LPA:1\$smdp.example.com\$MATCHING-ID")) context.startActivity(intent) ``` -------------------------------- ### Create and Open EuiccChannel with DefaultEuiccChannelFactory Source: https://context7.com/estkme-group/openeuicc/llms.txt Use DefaultEuiccChannelFactory to create OMAPI-backed EuiccChannel instances. Supports both standard and USB CCID channels. Remember to call cleanup() to release resources. ```kotlin val factory: EuiccChannelFactory = DefaultEuiccChannelFactory(context) val isdrAid = "A0000005591010FFFFFFFF8900000100".decodeHex() val channel: EuiccChannel? = factory.tryOpenEuiccChannel( port = someUiccPortInfoCompat, isdrAid = isdrAid, seId = EuiccChannel.SecureElementId.DEFAULT ) // USB channel (from an already-opened UsbCcidContext) val usbChannel: EuiccChannel? = factory.tryOpenUsbEuiccChannel( ccidCtx = usbCcidContext, isdrAid = isdrAid, seId = EuiccChannel.SecureElementId.DEFAULT ) // Release SEService / other held resources factory.cleanup() ``` -------------------------------- ### OmapiApduInterface - connect Source: https://context7.com/estkme-group/openeuicc/llms.txt Establishes a connection to the OMAPI SEService. ```APIDOC ## OmapiApduInterface - connect ### Description Establishes a connection to the OMAPI SEService. ### Method `connect` ### Parameters None ### Request Example ```kotlin val apduInterface = OmapiApduInterface( service = seService, port = uiccPortInfoCompat, verboseLoggingFlow = preferenceRepository.verboseLoggingFlow ) apduInterface.connect() ``` ### Response None ``` -------------------------------- ### PreferenceRepository - User Settings Flows Source: https://context7.com/estkme-group/openeuicc/llms.txt Exposes user-configurable settings as reactive Kotlin `Flow`s, allowing for real-time observation and updates. ```APIDOC ## PreferenceRepository ### Flows **Description**: Provides reactive `Flow`s for various user preferences. **Available Flows**: - **verboseLoggingFlow**: (Flow) - Controls verbose logging. - **ignoreTLSCertificateFlow**: (Flow) - Controls ignoring TLS certificates (development option). - **refreshAfterSwitchFlow**: (Flow) - Controls whether to send a REFRESH after profile enable/disable. - **notificationDownloadFlow**: (Flow) - Controls sending LPA notifications post-download. - **isdrAidListFlow**: (Flow) - A newline-separated list of AIDs with comments for ISDR. - **unfilteredProfileListFlow**: (Flow>) - Flow for unfiltered profile list. - **es10xMssFlow**: (Flow) - Flow for ES10x MSS value (default 63). ### updatePreference **Description**: Updates the value of a specific preference. **Parameters**: - **value** (T) - Required - The new value for the preference. ### removePreference **Description**: Removes a preference, resetting it to its default value. **Parameters**: None. ``` -------------------------------- ### EuiccChannelFactory - tryOpenEuiccChannel Source: https://context7.com/estkme-group/openeuicc/llms.txt Creates an OMAPI-backed EuiccChannel for a given port and ISDR AID. This is the primary method for opening a channel in the EasyEUICC build. ```APIDOC ## EuiccChannelFactory - tryOpenEuiccChannel ### Description Creates an OMAPI-backed EuiccChannel for a given port and ISDR AID. This is the primary method for opening a channel in the EasyEUICC build. ### Method `tryOpenEuiccChannel` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```kotlin val factory: EuiccChannelFactory = DefaultEuiccChannelFactory(context) val isdrAid = "A0000005591010FFFFFFFF8900000100".decodeHex() val channel: EuiccChannel? = factory.tryOpenEuiccChannel( port = someUiccPortInfoCompat, isdrAid = isdrAid, seId = EuiccChannel.SecureElementId.DEFAULT ) ``` ### Response #### Success Response (EuiccChannel?) Returns an `EuiccChannel` instance if successful, otherwise null. #### Response Example ```kotlin // channel variable will hold the EuiccChannel instance or null ``` ``` -------------------------------- ### LPA Utility Extensions for Profile and List Management Source: https://context7.com/estkme-group/openeuicc/llms.txt Provides Kotlin extension properties and functions for idiomatic access to LPA (Local Profile Assistant) types. Includes accessors for profile information, filtering lists of profiles, and managing the active profile. ```kotlin val profile: LocalProfileInfo = channel.lpa.profiles.first() println(profile.displayName) println(profile.isEnabled) ``` ```kotlin val profiles: List = channel.lpa.profiles val operationalOnly: List = profiles.operational val active: LocalProfileInfo? = profiles.enabled ``` ```kotlin val state: ProfileDownloadState = ProfileDownloadState.Downloading println(state.downloadProgress) ``` ```kotlin channel.lpa.switchProfile(iccid = "89049032123451234512345", enable = true, refresh = true) ``` ```kotlin channel.lpa.disableActiveProfile(refresh = true) ``` ```kotlin val disabledIccid: String? = channel.lpa.disableActiveProfileKeepIccId(refresh = false) ``` ```kotlin val channels: List = listOf(ch1, ch2) println(channels.hasMultipleChips) ``` -------------------------------- ### Launch Profile Switch Task Source: https://context7.com/estkme-group/openeuicc/llms.txt Enables or disables an eSIM profile as a foreground task, waiting for the SIM slot to reconnect. Handles potential `SwitchingProfilesRefreshException`, `TimeoutCancellationException`, or other errors during the process. ```kotlin val switchFlow = service.launchProfileSwitchTask( slotId = 0, portId = 0, seId = EuiccChannel.SecureElementId.DEFAULT, iccid = "89049032123451234512345", enable = true, reconnectTimeoutMillis = 30_000 ) val error = switchFlow.waitDone() when { error == null -> println("Profile switched successfully") error is EuiccChannelManagerService.SwitchingProfilesRefreshException -> println("Switched but refresh failed; airplane-mode toggle may be needed") error is TimeoutCancellationException -> println("Timed out waiting for modem to come back online") else -> println("Switch failed: $error") } ``` -------------------------------- ### Launch Profile Operations in EuiccChannelManagerService Source: https://context7.com/estkme-group/openeuicc/llms.txt Launches foreground tasks for deleting, renaming, or resetting eUICC profiles. These operations follow the `ForegroundTaskSubscriberFlow` pattern and require waiting for completion. ```kotlin // Delete a profile val deleteFlow = service.launchProfileDeleteTask( slotId = 0, portId = 0, seId = EuiccChannel.SecureElementId.DEFAULT, iccid = "89049032123451234512345" ) deleteFlow.waitDone()?.let { throw it } ``` ```kotlin // Rename a profile val renameFlow = service.launchProfileRenameTask( slotId = 0, portId = 0, seId = EuiccChannel.SecureElementId.DEFAULT, iccid = "89049032123451234512345", name = "Personal eSIM" ) renameFlow.waitDone()?.let { throw it } ``` ```kotlin // Full eUICC memory reset (deletes ALL profiles) val resetFlow = service.launchMemoryReset( slotId = 0, portId = 0, seId = EuiccChannel.SecureElementId.DEFAULT ) resetFlow.waitDone()?.let { throw it } ``` -------------------------------- ### LPAString - Activation Code Parsing and Serialization Source: https://context7.com/estkme-group/openeuicc/llms.txt Provides functionality to parse and serialize SGP.22 activation codes, commonly used for deep-linking and profile download initiation. ```APIDOC ## LPAString ### parse **Description**: Parses an activation code string into an `LPAString` object. **Parameters**: - **activationCode** (String) - Required - The activation code string (e.g., "LPA:1$SM-DP+$" or "1$SM-DP+$"). **Returns**: (LPAString) - An `LPAString` object representing the parsed activation code. ### toString **Description**: Serializes the `LPAString` object back into an activation code string. **Parameters**: None. **Returns**: (String) - The serialized activation code string. ``` -------------------------------- ### EuiccChannelManagerService — launchProfileDownloadTask Source: https://context7.com/estkme-group/openeuicc/llms.txt Runs a profile download as a foreground Android Service task with a persistent progress notification. Returns a `ForegroundTaskSubscriberFlow` that emits `ForegroundTaskState` updates (`InProgress(progress: Int)` / `Done(error: Throwable?)`). Handles notification dispatch automatically after the download completes if the preference is enabled. ```APIDOC ## EuiccChannelManagerService — launchProfileDownloadTask Runs a profile download as a foreground Android Service task with a persistent progress notification. Returns a `ForegroundTaskSubscriberFlow` that emits `ForegroundTaskState` updates (`InProgress(progress: Int)` / `Done(error: Throwable?)`). Handles notification dispatch automatically after the download completes if the preference is enabled. ```kotlin // Bind to EuiccChannelManagerService, then: val taskFlow: EuiccChannelManagerService.ForegroundTaskSubscriberFlow = service.launchProfileDownloadTask( slotId = 0, portId = 0, seId = EuiccChannel.SecureElementId.DEFAULT, input = ProfileDownloadInput( smdpAddress = "smdp.example.com", matchingId = "AABBCC-DDEEFF", confirmationCode = null, imei = null ) ) // Collect progress taskFlow.collect { state -> when (state) { is EuiccChannelManagerService.ForegroundTaskState.InProgress -> println("Progress: ${state.progress}% context=${state.context}") is EuiccChannelManagerService.ForegroundTaskState.Done -> if (state.error == null) println("Done!") else state.error.printStackTrace() else -> {} } } // Or simply await completion: val error: Throwable? = taskFlow.waitDone() if (error == null) println("Download successful") ``` ``` -------------------------------- ### Creating UI Components with UiComponentFactory Source: https://context7.com/estkme-group/openeuicc/llms.txt Use the `UiComponentFactory` to create UI fragments like `EuiccManagementFragment`, `NoEuiccPlaceholderFragment`, or `SettingsFragment`. This allows different build flavors to inject distinct `Fragment` implementations. ```kotlin // Common usage pattern inside MainActivity / tab pager val factory: UiComponentFactory = appContainer.uiComponentFactory // Create the eSIM management fragment for a given slot val mgmtFrag: EuiccManagementFragment = factory.createEuiccManagementFragment( slotId = 0, portId = 0, seId = EuiccChannel.SecureElementId.DEFAULT ) // Show a "no eUICC found" placeholder val noEuiccFrag: Fragment = factory.createNoEuiccPlaceholderFragment() // Show the settings screen val settingsFrag: Fragment = factory.createSettingsFragment() supportFragmentManager.beginTransaction() .replace(R.id.fragment_container, mgmtFrag) .commit() ``` -------------------------------- ### OMAPI APDU Interface for EasyEUICC Source: https://context7.com/estkme-group/openeuicc/llms.txt Implement ApduInterface using Android's Open Mobile API. Automatically retries on checksum errors. Requires a connected SEService and a port. ```kotlin val seService: SEService = /* connected SEService from connectSEService(context) */ val apduInterface = OmapiApduInterface( service = seService, port = uiccPortInfoCompat, // wraps logicalSlotIndex verboseLoggingFlow = preferenceRepository.verboseLoggingFlow ) apduInterface.connect() val handle = apduInterface.logicalChannelOpen( "A0000005591010FFFFFFFF8900000100".decodeHex() ) // Transmit a raw APDU val response: ByteArray = apduInterface.transmit( handle, "80E2910006BF2903830107".decodeHex() ) println("APDU response: ${response.encodeHex()}") apduInterface.logicalChannelClose(handle) apduInterface.disconnect() ``` -------------------------------- ### Clone Submodules Source: https://github.com/estkme-group/openeuicc/blob/master/README.md Ensure all submodules are cloned and updated before building. This command is essential for the build process. ```shell git submodule update --init ``` -------------------------------- ### EuiccChannelFactory - tryOpenUsbEuiccChannel Source: https://context7.com/estkme-group/openeuicc/llms.txt Attempts to open a USB CCID channel by scanning for readers, requesting permissions, and then opening an ISD-R channel. ```APIDOC ## EuiccChannelFactory - tryOpenUsbEuiccChannel ### Description Attempts to open a USB CCID channel by scanning for readers, requesting permissions, and then opening an ISD-R channel. Returns a `Pair` where the second value indicates whether a usable channel was successfully opened. ### Method `tryOpenUsbEuiccChannel` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Request Example ```kotlin lifecycleScope.launch { val (device, canOpen) = euiccChannelManager.tryOpenUsbEuiccChannel() when { device == null -> println("No USB CCID reader found") !canOpen -> { println("Need USB permission for ${device.deviceName}") } else -> { euiccChannelManager.withEuiccChannel( EuiccChannelManager.USB_CHANNEL_ID, 0, EuiccChannel.SecureElementId.DEFAULT ) { channel -> println("USB eID: ${channel.lpa.eID}") } } } } ``` ### Response #### Success Response (Pair) Returns a pair containing the `UsbDevice` if found, and a boolean indicating if a channel could be opened. ``` -------------------------------- ### Interact with LocalProfileAssistantWrapper Source: https://context7.com/estkme-group/openeuicc/llms.txt Accesses properties and performs profile operations (enable, disable, set nickname, delete, download) using a LocalProfileAssistantWrapper obtained within a `withEuiccChannel` callback. Accessing the wrapper after the callback scope exits will result in an IllegalStateException. ```kotlin euiccChannelManager.withEuiccChannel(0, 0, EuiccChannel.SecureElementId.DEFAULT) { channel -> val lpa = channel.lpa // This is a LocalProfileAssistantWrapper // Read properties println("eID: ${lpa.eID}") println("EuiccInfo2: ${lpa.euiccInfo2}") // Profile operations lpa.enableProfile("89049032123451234512345", refresh = true) lpa.disableProfile("89049032123451234512345", refresh = true) lpa.setNickname("89049032123451234512345", "Work SIM") lpa.deleteProfile("89049032123451234512345") // Download lpa.downloadProfile( ProfileDownloadInput("smdp.example.com", "MATCHING-ID", null, null) ) { state -> println("Download progress: $state") true // continue } // After the lambda returns, lpa.invalidateWrapper() is called automatically // Any access to `lpa` after this point throws IllegalStateException } ``` -------------------------------- ### Open USB CCID Channel with DefaultEuiccChannelManager Source: https://context7.com/estkme-group/openeuicc/llms.txt Use DefaultEuiccChannelManager to scan for and open a USB CCID channel. Handles permission requests and provides access to the channel via a callback. Requires a lifecycleScope for coroutine execution. ```kotlin lifecycleScope.launch { val (device, canOpen) = euiccChannelManager.tryOpenUsbEuiccChannel() when { device == null -> println("No USB CCID reader found") !canOpen -> { // Permission dialog was triggered by Android; retry after user grants it println("Need USB permission for ${device.deviceName}") } else -> { // Channel is now open and cached; access it via USB_CHANNEL_ID euiccChannelManager.withEuiccChannel( EuiccChannelManager.USB_CHANNEL_ID, 0, EuiccChannel.SecureElementId.DEFAULT ) { channel -> println("USB eID: ${channel.lpa.eID}") } } } } ``` -------------------------------- ### TelephonyManagerApduInterface - logicalChannelOpen Source: https://context7.com/estkme-group/openeuicc/llms.txt Opens a logical channel to the EUICC using TelephonyManager. ```APIDOC ## TelephonyManagerApduInterface - logicalChannelOpen ### Description Opens a logical channel to the EUICC using TelephonyManager. ### Method `logicalChannelOpen` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - **aid** (ByteArray) - Required - The Application Identifier (AID) of the application to open a channel for. ### Request Example ```kotlin val channelHandle = apduInterface.logicalChannelOpen( "A0000005591010FFFFFFFF8900000100".decodeHex() ) ``` ### Response #### Success Response (Int) Returns a handle (Int) for the opened logical channel. ``` -------------------------------- ### EuiccChannelManagerService — launchProfileSwitchTask Source: https://context7.com/estkme-group/openeuicc/llms.txt Enables or disables an eSIM profile as a foreground task. After the LPA command succeeds, waits for the SIM slot to reconnect within the specified timeout (important for internal eSIMs where a refresh command causes a brief modem reset). ```APIDOC ## EuiccChannelManagerService — launchProfileSwitchTask Enables or disables an eSIM profile as a foreground task. After the LPA command succeeds, waits for the SIM slot to reconnect within the specified timeout (important for internal eSIMs where a refresh command causes a brief modem reset). ```kotlin val switchFlow = service.launchProfileSwitchTask( slotId = 0, portId = 0, seId = EuiccChannel.SecureElementId.DEFAULT, iccid = "89049032123451234512345", enable = true, reconnectTimeoutMillis = 30_000 ) val error = switchFlow.waitDone() when { error == null -> println("Profile switched successfully") error is EuiccChannelManagerService.SwitchingProfilesRefreshException -> println("Switched but refresh failed; airplane-mode toggle may be needed") error is TimeoutCancellationException -> println("Timed out waiting for modem to come back online") else -> println("Switch failed: $error") } ``` ``` -------------------------------- ### Accessing Services via AppContainer Source: https://context7.com/estkme-group/openeuicc/llms.txt Access various services like `EuiccChannelManager`, `PreferenceRepository`, and `TelephonyManager` through the `AppContainer` interface, implemented by `DefaultAppContainer` and custom subclasses. ```kotlin // Access via OpenEuiccContextMarker (Activity, Fragment, Service that implement the marker) val euiccChannelManager: EuiccChannelManager = appContainer.euiccChannelManager val prefs: PreferenceRepository = appContainer.preferenceRepository val tm: TelephonyManager = appContainer.telephonyManager val sm: SubscriptionManager = appContainer.subscriptionManager val factory: UiComponentFactory = appContainer.uiComponentFactory val textProvider: CustomizableTextProvider = appContainer.customizableTextProvider ``` -------------------------------- ### Custom AppContainer Implementations Source: https://context7.com/estkme-group/openeuicc/llms.txt Implement custom `AppContainer` variants like `UnprivilegedAppContainer` for EasyEUICC or `PrivilegedAppContainer` for OpenEUICC by extending `OpenEuiccApplication` and overriding the `appContainer` property. ```kotlin // Custom variant: UnprivilegedAppContainer (EasyEUICC) class UnprivilegedOpenEuiccApplication : OpenEuiccApplication() { override val appContainer: AppContainer by lazy { UnprivilegedAppContainer(this) // plugs in Unprivileged* implementations } } // Custom variant: PrivilegedOpenEuiccApplication (OpenEUICC) class PrivilegedOpenEuiccApplication : OpenEuiccApplication() { override val appContainer: AppContainer by lazy { PrivilegedAppContainer(this) // plugs in TelephonyManager APDU interface, etc. } } ``` -------------------------------- ### UsbCcidContext Source: https://context7.com/estkme-group/openeuicc/llms.txt Manages the USB device connection and card power-on sequence for a CCID smart card reader. It wraps the raw UsbDeviceConnection, claims the interface, sends the IccPowerOn command to obtain the ATR, and negotiates parameters. A single UsbCcidContext may be reused to try multiple ISD-R AIDs without reopening the USB connection. ```APIDOC ## UsbCcidContext Manages the USB device connection and card power-on sequence for a CCID smart card reader. Wraps the raw `UsbDeviceConnection`, claims the interface, sends the `IccPowerOn` command to obtain the ATR, and negotiates parameters. A single `UsbCcidContext` may be reused to try multiple ISD-R AIDs without reopening the USB connection. ```kotlin val usbDevice: UsbDevice = /* from UsbManager.deviceList */ val usbInterface: UsbInterface = usbDevice.interfaces.smartCard!! val ccidCtx: UsbCcidContext? = UsbCcidContext.createFromUsbDevice( context, usbDevice, usbInterface ) if (ccidCtx != null) { ccidCtx.connect() // powers on the card, reads ATR println("ATR: ${ccidCtx.atr?.encodeHex()}") // Use the context to build a UsbApduInterface val apduIface = UsbApduInterface(ccidCtx) apduIface.connect() // sends TERMINAL CAPABILITY val handle = apduIface.logicalChannelOpen( "A0000005591010FFFFFFFF8900000100".decodeHex() ) // ... transmit APDUs ... apduIface.logicalChannelClose(handle) ccidCtx.allowDisconnect = true ccidCtx.disconnect() } ``` ``` -------------------------------- ### Tracked Operation with Auto Notification Handling Source: https://context7.com/estkme-group/openeuicc/llms.txt Use `beginTrackedOperation` to record notification sequence numbers before an operation and automatically dispatch new notifications afterward. The operation's success determines if notifications are handled. ```kotlin euiccChannelManager.beginTrackedOperation( slotId = 0, portId = 0, seId = EuiccChannel.SecureElementId.DEFAULT ) { // op() returns true → auto-handle new notifications // op() returns false → skip notification handling euiccChannelManager.withEuiccChannel(0, 0, EuiccChannel.SecureElementId.DEFAULT) { channel -> val success = channel.lpa.deleteProfile("89049032123451234512345") success // true → handle any generated notifications } } ``` -------------------------------- ### TelephonyManager APDU Interface for OpenEUICC Source: https://context7.com/estkme-group/openeuicc/llms.txt Privileged-only ApduInterface implementation using TelephonyManager hidden APIs. Used in the 'app' module. Connection is implicit via TelephonyManager. ```kotlin // Privileged context only — requires system app installation val apduInterface = TelephonyManagerApduInterface( port = uiccPortInfoCompat, tm = telephonyManager, verboseLoggingFlow = preferenceRepository.verboseLoggingFlow ) apduInterface.connect() // no-op; connection is implicit via TelephonyManager val channelHandle = apduInterface.logicalChannelOpen( "A0000005591010FFFFFFFF8900000100".decodeHex() ) val response = apduInterface.transmit( channelHandle, "80CA9F7E00".decodeHex() ) println("Response: ${response.encodeHex()}") apduInterface.logicalChannelClose(channelHandle) ``` -------------------------------- ### OmapiApduInterface - logicalChannelOpen Source: https://context7.com/estkme-group/openeuicc/llms.txt Opens a logical channel to the EUICC for APDU communication. ```APIDOC ## OmapiApduInterface - logicalChannelOpen ### Description Opens a logical channel to the EUICC for APDU communication. ### Method `logicalChannelOpen` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body None ### Parameters - **aid** (ByteArray) - Required - The Application Identifier (AID) of the application to open a channel for. ### Request Example ```kotlin val handle = apduInterface.logicalChannelOpen( "A0000005591010FFFFFFFF8900000100".decodeHex() ) ``` ### Response #### Success Response (Int) Returns a handle (Int) for the opened logical channel. ``` -------------------------------- ### EuiccChannelManager Interface Source: https://context7.com/estkme-group/openeuicc/llms.txt The EuiccChannelManager interface handles the lifecycle of EuiccChannel instances, discovering ports, caching channels, and providing safe access through `withEuiccChannel`. It also includes utilities for waiting for device reconnection. ```APIDOC ## EuiccChannelManager Interface `EuiccChannelManager` manages the lifecycle of all open `EuiccChannel` instances. It discovers available eUICC ports (internal and USB), caches open channels, and exposes them through a safe scoped accessor (`withEuiccChannel`). All channels are closed and cache cleared on `invalidate()`. The constant `USB_CHANNEL_ID = 99` is used as the virtual slot ID for USB CCID readers. ```kotlin // Scan all internal eUICC ports as a Flow euiccChannelManager.flowInternalEuiccPorts() .collect { (slotId, portId) -> println("Found internal eUICC on slot=$slotId port=$portId") } // Include USB readers too euiccChannelManager.flowAllOpenEuiccPorts() .collect { (slotId, portId) -> if (slotId == EuiccChannelManager.USB_CHANNEL_ID) println("USB reader at port $portId") else println("Internal slot=$slotId port=$portId") } // Find first available port for a physical slot val portId = euiccChannelManager.findFirstAvailablePort(physicalSlotId = 0) // Access a channel safely (scoped callback, runs on Dispatchers.IO) val eId = euiccChannelManager.withEuiccChannel( physicalSlotId = 0, portId = 0, seId = EuiccChannel.SecureElementId.DEFAULT ) { channel -> channel.lpa.eID } // Wait for SIM to reconnect after a profile switch (timeout 30 s) euiccChannelManager.waitForReconnect(physicalSlotId = 0, portId = 0, timeoutMillis = 30_000) // Invalidate all cached channels euiccChannelManager.invalidate() ``` ``` -------------------------------- ### EuiccChannelManagerService - Profile Management Tasks Source: https://context7.com/estkme-group/openeuicc/llms.txt Launches foreground tasks for deleting, renaming, or resetting eUICC profiles. These methods follow the `ForegroundTaskSubscriberFlow` pattern. ```APIDOC ## EuiccChannelManagerService ### launchProfileDeleteTask **Description**: Launches a foreground task to delete a specific profile from the eUICC. **Parameters**: - **slotId** (Int) - Required - The ID of the eUICC slot. - **portId** (Int) - Required - The ID of the port on the eUICC. - **seId** (EuiccChannel.SecureElementId) - Required - The Secure Element ID. - **iccid** (String) - Required - The ICCID of the profile to delete. ### launchProfileRenameTask **Description**: Launches a foreground task to rename a specific profile on the eUICC. **Parameters**: - **slotId** (Int) - Required - The ID of the eUICC slot. - **portId** (Int) - Required - The ID of the port on the eUICC. - **seId** (EuiccChannel.SecureElementId) - Required - The Secure Element ID. - **iccid** (String) - Required - The ICCID of the profile to rename. - **name** (String) - Required - The new name for the profile. ### launchMemoryReset **Description**: Launches a foreground task to perform a full reset of the eUICC memory, deleting all profiles. **Parameters**: - **slotId** (Int) - Required - The ID of the eUICC slot. - **portId** (Int) - Required - The ID of the port on the eUICC. - **seId** (EuiccChannel.SecureElementId) - Required - The Secure Element ID. ``` -------------------------------- ### TelephonyManagerApduInterface - connect Source: https://context7.com/estkme-group/openeuicc/llms.txt Connects to the TelephonyManager for APDU operations. This is a no-op as connection is implicit. ```APIDOC ## TelephonyManagerApduInterface - connect ### Description Connects to the TelephonyManager for APDU operations. This is a no-op as connection is implicit. ### Method `connect` ### Parameters None ### Request Example ```kotlin val apduInterface = TelephonyManagerApduInterface( port = uiccPortInfoCompat, tm = telephonyManager, verboseLoggingFlow = preferenceRepository.verboseLoggingFlow ) apduInterface.connect() // no-op ``` ### Response None ``` -------------------------------- ### EuiccChannelFactory - cleanup Source: https://context7.com/estkme-group/openeuicc/llms.txt Releases any held resources, such as the SEService connection. ```APIDOC ## EuiccChannelFactory - cleanup ### Description Releases any held resources, such as the SEService connection. ### Method `cleanup` ### Parameters None ### Request Example ```kotlin factory.cleanup() ``` ### Response None ``` -------------------------------- ### OpenEuiccService - Android Framework Integration Source: https://context7.com/estkme-group/openeuicc/llms.txt Methods exposed by `OpenEuiccService` for integration with the Android framework, allowing system services and carrier apps to manage eUICC profiles. ```APIDOC ## OpenEuiccService ### onGetEid **Description**: Called by the Android framework to retrieve the eUICC's Electronic ID (eID) for a given slot. **Parameters**: - **slotId** (Int) - Required - The ID of the eUICC slot. **Returns**: (String?) - The eID of the eUICC, or null if not available. ### onGetEuiccProfileInfoList **Description**: Called by the Android framework to list all available eUICC profiles for a given slot. **Parameters**: - **slotId** (Int) - Required - The ID of the eUICC slot. **Returns**: (GetEuiccProfileInfoListResult) - A result object containing an array of `EuiccProfileInfo`. ### onSwitchToSubscriptionWithPort **Description**: Called by the Android framework when a carrier app requests to switch to a specific subscription (profile) on the eUICC. **Parameters**: - **slotId** (Int) - Required - The ID of the eUICC slot. - **portIndex** (Int) - Required - The index of the port on the eUICC. - **iccid** (String?) - Optional - The ICCID of the profile to switch to. If null, the current profile is deactivated. - **forceDeactivateSim** (Boolean) - Required - Whether to force deactivation of the current SIM. **Returns**: (Int) - Result code indicating success or failure (e.g., RESULT_OK, RESULT_MUST_DEACTIVATE_SIM). ### onDeleteSubscription **Description**: Called by the Android framework to delete a specific subscription (profile) from the eUICC. **Parameters**: - **slotId** (Int) - Required - The ID of the eUICC slot. - **iccid** (String) - Required - The ICCID of the profile to delete. **Returns**: (Int) - Result code indicating success or failure. ### onUpdateSubscriptionNickname **Description**: Called by the Android framework to rename a specific subscription (profile) on the eUICC. **Parameters**: - **slotId** (Int) - Required - The ID of the eUICC slot. - **iccid** (String) - Required - The ICCID of the profile to rename. - **nickname** (String?) - Optional - The new nickname for the profile. ``` -------------------------------- ### Manage eUICC Profiles with EuiccChannel Source: https://context7.com/estkme-group/openeuicc/llms.txt Use the EuiccChannel interface to interact with an eUICC Secure Element. Obtain a channel via EuiccChannelManager and access profile operations through the channel.lpa handle. The channel wrapper is automatically invalidated after the lambda returns. ```kotlin // Every profile operation goes through channel.lpa euiccChannelManager.withEuiccChannel( physicalSlotId = 0, portId = 0, seId = EuiccChannel.SecureElementId.DEFAULT ) { channel: EuiccChannel -> println("eID : ${channel.lpa.eID}") println("Type : ${channel.type}") // "OMAPI" or "USB" println("Valid : ${channel.valid}") println("ATR : ${channel.atr?.encodeHex()}") // List all profiles channel.lpa.profiles.forEach { p -> println("${p.iccid} ${p.displayName} enabled=${p.isEnabled}") } } // The wrapper is invalidated automatically after the lambda returns. ``` -------------------------------- ### LocalProfileAssistantWrapper Source: https://context7.com/estkme-group/openeuicc/llms.txt A scoped, invalidatable proxy over `LocalProfileAssistant` (from `lpac-jni`). Handed to callers inside `withEuiccChannel` callbacks via `EuiccChannelWrapper`. Throws `IllegalStateException` if any property or method is accessed after the outer `withEuiccChannel` scope has exited. ```APIDOC ## LocalProfileAssistantWrapper A scoped, invalidatable proxy over `LocalProfileAssistant` (from `lpac-jni`). Handed to callers inside `withEuiccChannel` callbacks via `EuiccChannelWrapper`. Throws `IllegalStateException` if any property or method is accessed after the outer `withEuiccChannel` scope has exited. ```kotlin euiccChannelManager.withEuiccChannel(0, 0, EuiccChannel.SecureElementId.DEFAULT) { channel -> val lpa = channel.lpa // This is a LocalProfileAssistantWrapper // Read properties println("eID: ${lpa.eID}") println("EuiccInfo2: ${lpa.euiccInfo2}") // Profile operations lpa.enableProfile("89049032123451234512345", refresh = true) lpa.disableProfile("89049032123451234512345", refresh = true) lpa.setNickname("89049032123451234512345", "Work SIM") lpa.deleteProfile("89049032123451234512345") // Download lpa.downloadProfile( ProfileDownloadInput("smdp.example.com", "MATCHING-ID", null, null) ) { state -> println("Download progress: $state") true // continue } // After the lambda returns, lpa.invalidateWrapper() is called automatically // Any access to `lpa` after this point throws IllegalStateException } ``` ``` -------------------------------- ### EuiccChannel Interface Source: https://context7.com/estkme-group/openeuicc/llms.txt The EuiccChannel interface represents an open APDU session to an eUICC Secure Element. It provides access to the LPA handle, raw APDU interface, and ATR bytes, and allows listing and managing profiles. ```APIDOC ## EuiccChannel Interface `EuiccChannel` is the core abstraction for an open APDU session to a single eUICC Secure Element. It exposes the physical/logical slot identity, the `LocalProfileAssistant` (LPA) handle through which all profile operations are performed, the raw `ApduInterface`, and the ATR bytes. Implementations are `EuiccChannelImpl` (created by the factory) and `EuiccChannelWrapper` (a short-lived scoped proxy handed to callbacks). ```kotlin // Every profile operation goes through channel.lpa euiccChannelManager.withEuiccChannel( physicalSlotId = 0, portId = 0, seId = EuiccChannel.SecureElementId.DEFAULT ) { channel: EuiccChannel -> println("eID : ${channel.lpa.eID}") println("Type : ${channel.type}") // "OMAPI" or "USB" println("Valid : ${channel.valid}") println("ATR : ${channel.atr?.encodeHex()}") // List all profiles channel.lpa.profiles.forEach { p -> println("${p.iccid} ${p.displayName} enabled=${p.isEnabled}") } } // The wrapper is invalidated automatically after the lambda returns. ``` ``` -------------------------------- ### SecureElementId Wrapper for eUICC Slots Source: https://context7.com/estkme-group/openeuicc/llms.txt Utilize EuiccChannel.SecureElementId for type-safe handling of Secure Element indices. Use SecureElementId.DEFAULT for single-SE chips or create custom IDs. The type is Parcelable for easy passing through Android Bundles and Intents. ```kotlin // Default SE (single-SE chips) val seId = EuiccChannel.SecureElementId.DEFAULT // Custom SE id created during download flow val customSeId = EuiccChannel.SecureElementId.createFromInt(1) // Passing via Bundle (Fragment arguments) val bundle = Bundle().apply { putParcelable("seId", seId) } val restored: EuiccChannel.SecureElementId = bundle.getParcelable("seId", EuiccChannel.SecureElementId::class.java)!! println(seId == EuiccChannel.SecureElementId.DEFAULT) // true println(customSeId.id) // 1 ```