Compare commits

...

11 commits

Author SHA1 Message Date
d0b3d54c66 ui: Allow multi-line strings in EuiccInfoActivity 2024-12-20 19:33:43 -05:00
3a860601a3 ui: Optimize ATR strings 2024-12-20 19:32:58 -05:00
6b4723daee refactor: ATR should not be the concern of lpac-jni
...instead, use a separate interface to represent channel types that do
support reading ATR.
2024-12-20 19:30:33 -05:00
3ef78a23db feat: atr in euiccinfo activity
commit 0fbec512ab7dd8be207bb771129a29eb5f9434a8
Author: septs <github@septs.pw>
Date:   Wed Dec 18 21:27:53 2024 +0800

    feat: atr in euiccinfo activity
2024-12-20 19:06:23 -05:00
31d595a6b1 ui: Don't reset profile name while resuming the rename fragment 2024-12-20 19:04:31 -05:00
e7ef370e46 feat: sgp.22 version in euicc info activity (#130)
Co-authored-by: Peter Cai <peter@typeblog.net>
Reviewed-on: PeterCxy/OpenEUICC#130
Co-authored-by: septs <github@septs.pw>
Co-committed-by: septs <github@septs.pw>
2024-12-20 22:56:31 +01:00
653a7b32ee ui: wizard: Prevent clicking next multiple times 2024-12-18 21:13:08 -05:00
0f8749ee04 ui: wizard: Verify the EuiccChannel is still valid every time next is pressed 2024-12-18 21:11:15 -05:00
c0a6917645 Remove unused isForegroundTaskRunning
idk what this was used for
2024-12-18 20:57:37 -05:00
6e3176668a core: Reconnect to USB readers after switching profiles as well
Apparently some reader + card combination results in the need to
re-establish the ISD-R even though this is not a modem.

It doesn't hurt to run waitForReconnect() anyway :)
2024-12-18 20:44:08 -05:00
66bee041a0 ui: wizard: IMEI input type should be numberPassword 2024-12-18 20:05:27 -05:00
19 changed files with 107 additions and 38 deletions

View file

@ -0,0 +1,5 @@
package im.angry.openeuicc.core
interface ApduInterfaceAtrProvider {
val atr: ByteArray?
}

View file

@ -184,20 +184,30 @@ open class DefaultEuiccChannelManager(
} }
override suspend fun waitForReconnect(physicalSlotId: Int, portId: Int, timeoutMillis: Long) { override suspend fun waitForReconnect(physicalSlotId: Int, portId: Int, timeoutMillis: Long) {
if (physicalSlotId == EuiccChannelManager.USB_CHANNEL_ID) return if (physicalSlotId == EuiccChannelManager.USB_CHANNEL_ID) {
usbChannel?.close()
// If there is already a valid channel, we close it proactively usbChannel = null
// Sometimes the current channel can linger on for a bit even after it should have become invalid } else {
channelCache.find { it.slotId == physicalSlotId && it.portId == portId }?.apply { // If there is already a valid channel, we close it proactively
if (valid) close() // Sometimes the current channel can linger on for a bit even after it should have become invalid
channelCache.find { it.slotId == physicalSlotId && it.portId == portId }?.apply {
if (valid) close()
}
} }
withTimeout(timeoutMillis) { withTimeout(timeoutMillis) {
while (true) { while (true) {
try { try {
// tryOpenEuiccChannel() will automatically dispose of invalid channels val channel = if (physicalSlotId == EuiccChannelManager.USB_CHANNEL_ID) {
// and recreate when needed // tryOpenUsbEuiccChannel() will always try to reopen the channel, even if
val channel = findEuiccChannelByPort(physicalSlotId, portId)!! // a USB channel already exists
tryOpenUsbEuiccChannel()
usbChannel!!
} else {
// tryOpenEuiccChannel() will automatically dispose of invalid channels
// and recreate when needed
findEuiccChannelByPort(physicalSlotId, portId)!!
}
check(channel.valid) { "Invalid channel" } check(channel.valid) { "Invalid channel" }
break break
} catch (e: Exception) { } catch (e: Exception) {

View file

@ -16,6 +16,11 @@ interface EuiccChannel {
val valid: Boolean val valid: Boolean
/**
* Answer to Reset (ATR) value of the underlying interface, if any
*/
val atr: ByteArray?
/** /**
* Intrinsic name of this channel. For device-internal SIM slots, * Intrinsic name of this channel. For device-internal SIM slots,
* this should be null; for USB readers, this should be the name of * this should be null; for USB readers, this should be the name of

View file

@ -11,7 +11,7 @@ class EuiccChannelImpl(
override val type: String, override val type: String,
override val port: UiccPortInfoCompat, override val port: UiccPortInfoCompat,
override val intrinsicChannelName: String?, override val intrinsicChannelName: String?,
apduInterface: ApduInterface, private val apduInterface: ApduInterface,
verboseLoggingFlow: Flow<Boolean>, verboseLoggingFlow: Flow<Boolean>,
ignoreTLSCertificateFlow: Flow<Boolean> ignoreTLSCertificateFlow: Flow<Boolean>
) : EuiccChannel { ) : EuiccChannel {
@ -22,6 +22,9 @@ class EuiccChannelImpl(
override val lpa: LocalProfileAssistant = override val lpa: LocalProfileAssistant =
LocalProfileAssistantImpl(apduInterface, HttpInterfaceImpl(verboseLoggingFlow, ignoreTLSCertificateFlow)) LocalProfileAssistantImpl(apduInterface, HttpInterfaceImpl(verboseLoggingFlow, ignoreTLSCertificateFlow))
override val atr: ByteArray?
get() = (apduInterface as? ApduInterfaceAtrProvider)?.atr
override val valid: Boolean override val valid: Boolean
get() = lpa.valid get() = lpa.valid

View file

@ -33,6 +33,8 @@ class EuiccChannelWrapper(orig: EuiccChannel) : EuiccChannel {
get() = channel.valid get() = channel.valid
override val intrinsicChannelName: String? override val intrinsicChannelName: String?
get() = channel.intrinsicChannelName get() = channel.intrinsicChannelName
override val atr: ByteArray?
get() = channel.atr
override fun close() = channel.close() override fun close() = channel.close()

View file

@ -15,7 +15,7 @@ class OmapiApduInterface(
private val service: SEService, private val service: SEService,
private val port: UiccPortInfoCompat, private val port: UiccPortInfoCompat,
private val verboseLoggingFlow: Flow<Boolean> private val verboseLoggingFlow: Flow<Boolean>
): ApduInterface { ): ApduInterface, ApduInterfaceAtrProvider {
companion object { companion object {
const val TAG = "OmapiApduInterface" const val TAG = "OmapiApduInterface"
} }
@ -26,6 +26,9 @@ class OmapiApduInterface(
override val valid: Boolean override val valid: Boolean
get() = service.isConnected && (this::session.isInitialized && !session.isClosed) get() = service.isConnected && (this::session.isInitialized && !session.isClosed)
override val atr: ByteArray?
get() = session.atr
override fun connect() { override fun connect() {
session = service.getUiccReaderCompat(port.logicalSlotIndex + 1).openSession() session = service.getUiccReaderCompat(port.logicalSlotIndex + 1).openSession()
} }

View file

@ -3,6 +3,7 @@ package im.angry.openeuicc.core.usb
import android.hardware.usb.UsbDeviceConnection import android.hardware.usb.UsbDeviceConnection
import android.hardware.usb.UsbEndpoint import android.hardware.usb.UsbEndpoint
import android.util.Log import android.util.Log
import im.angry.openeuicc.core.ApduInterfaceAtrProvider
import im.angry.openeuicc.util.* import im.angry.openeuicc.util.*
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import net.typeblog.lpac_jni.ApduInterface import net.typeblog.lpac_jni.ApduInterface
@ -12,7 +13,7 @@ class UsbApduInterface(
private val bulkIn: UsbEndpoint, private val bulkIn: UsbEndpoint,
private val bulkOut: UsbEndpoint, private val bulkOut: UsbEndpoint,
private val verboseLoggingFlow: Flow<Boolean> private val verboseLoggingFlow: Flow<Boolean>
): ApduInterface { ) : ApduInterface, ApduInterfaceAtrProvider {
companion object { companion object {
private const val TAG = "UsbApduInterface" private const val TAG = "UsbApduInterface"
} }
@ -22,6 +23,8 @@ class UsbApduInterface(
private var channelId = -1 private var channelId = -1
override var atr: ByteArray? = null
override fun connect() { override fun connect() {
ccidDescription = UsbCcidDescription.fromRawDescriptors(conn.rawDescriptors)!! ccidDescription = UsbCcidDescription.fromRawDescriptors(conn.rawDescriptors)!!
@ -32,7 +35,9 @@ class UsbApduInterface(
transceiver = UsbCcidTransceiver(conn, bulkIn, bulkOut, ccidDescription, verboseLoggingFlow) transceiver = UsbCcidTransceiver(conn, bulkIn, bulkOut, ccidDescription, verboseLoggingFlow)
try { try {
transceiver.iccPowerOn() // 6.1.1.1 PC_to_RDR_IccPowerOn (Page 20 of 40)
// https://www.usb.org/sites/default/files/DWG_Smart-Card_USB-ICC_ICCD_rev10.pdf
atr = transceiver.iccPowerOn().data
} catch (e: Exception) { } catch (e: Exception) {
e.printStackTrace() e.printStackTrace()
throw e throw e

View file

@ -362,9 +362,6 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
) )
} }
val isForegroundTaskRunning: Boolean
get() = foregroundTaskState.value != ForegroundTaskState.Idle
suspend fun waitForForegroundTask() { suspend fun waitForForegroundTask() {
foregroundTaskState.takeWhile { it != ForegroundTaskState.Idle } foregroundTaskState.takeWhile { it != ForegroundTaskState.Idle }
.collect() .collect()
@ -448,7 +445,7 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
portId: Int, portId: Int,
iccid: String, iccid: String,
enable: Boolean, // Enable or disable the profile indicated in iccid enable: Boolean, // Enable or disable the profile indicated in iccid
reconnectTimeoutMillis: Long = 0 // 0 = do not wait for reconnect, useful for USB readers reconnectTimeoutMillis: Long = 0 // 0 = do not wait for reconnect
): ForegroundTaskSubscriberFlow = ): ForegroundTaskSubscriberFlow =
launchForegroundTask( launchForegroundTask(
getString(R.string.task_profile_switch), getString(R.string.task_profile_switch),

View file

@ -41,7 +41,7 @@ class EuiccInfoActivity : BaseEuiccAccessActivity(), OpenEuiccContextMarker {
@StringRes @StringRes
val titleResId: Int, val titleResId: Int,
val content: String?, val content: String?,
val copiedToastResId: Int? = null val copiedToastResId: Int? = null,
) )
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -114,6 +114,7 @@ class EuiccInfoActivity : BaseEuiccAccessActivity(), OpenEuiccContextMarker {
) )
) )
channel.lpa.euiccInfo2.let { info -> channel.lpa.euiccInfo2.let { info ->
add(Item(R.string.euicc_info_sgp22_version, info?.sgp22Version))
add(Item(R.string.euicc_info_firmware_version, info?.euiccFirmwareVersion)) add(Item(R.string.euicc_info_firmware_version, info?.euiccFirmwareVersion))
add(Item(R.string.euicc_info_globalplatform_version, info?.globalPlatformVersion)) add(Item(R.string.euicc_info_globalplatform_version, info?.globalPlatformVersion))
add(Item(R.string.euicc_info_pp_version, info?.ppVersion)) add(Item(R.string.euicc_info_pp_version, info?.ppVersion))
@ -133,6 +134,13 @@ class EuiccInfoActivity : BaseEuiccAccessActivity(), OpenEuiccContextMarker {
} }
add(Item(R.string.euicc_info_ci_type, getString(resId))) add(Item(R.string.euicc_info_ci_type, getString(resId)))
} }
add(
Item(
R.string.euicc_info_atr,
channel.atr?.encodeHex() ?: getString(R.string.unavailable),
copiedToastResId = R.string.toast_atr_copied,
)
)
} }
private fun formatByBoolean(b: Boolean, res: Pair<Int, Int>): String = private fun formatByBoolean(b: Boolean, res: Pair<Int, Int>): String =

View file

@ -228,11 +228,7 @@ open class EuiccManagementFragment : Fragment(), EuiccProfilesChangedListener,
portId, portId,
iccid, iccid,
enable, enable,
reconnectTimeoutMillis = if (isUsb) { reconnectTimeoutMillis = 30 * 1000
0
} else {
30 * 1000
}
).waitDone() ).waitDone()
when (err) { when (err) {

View file

@ -54,6 +54,7 @@ class ProfileRenameFragment : BaseMaterialDialogFragment(), EuiccChannelFragment
override fun onViewCreated(view: View, savedInstanceState: Bundle?) { override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState) super.onViewCreated(view, savedInstanceState)
profileRenameNewName.editText!!.setText(requireArguments().getString("currentName"))
toolbar.apply { toolbar.apply {
setTitle(R.string.rename) setTitle(R.string.rename)
setNavigationOnClickListener { setNavigationOnClickListener {
@ -66,11 +67,6 @@ class ProfileRenameFragment : BaseMaterialDialogFragment(), EuiccChannelFragment
} }
} }
override fun onStart() {
super.onStart()
profileRenameNewName.editText!!.setText(requireArguments().getString("currentName"))
}
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
setWidthPercent(95) setWidthPercent(95)

View file

@ -5,15 +5,21 @@ import android.view.View
import android.view.inputmethod.InputMethodManager import android.view.inputmethod.InputMethodManager
import android.widget.Button import android.widget.Button
import android.widget.ProgressBar import android.widget.ProgressBar
import android.widget.Toast
import androidx.activity.OnBackPressedCallback import androidx.activity.OnBackPressedCallback
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.core.view.ViewCompat import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import im.angry.openeuicc.common.R import im.angry.openeuicc.common.R
import im.angry.openeuicc.core.EuiccChannelManager
import im.angry.openeuicc.ui.BaseEuiccAccessActivity import im.angry.openeuicc.ui.BaseEuiccAccessActivity
import im.angry.openeuicc.util.* import im.angry.openeuicc.util.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import net.typeblog.lpac_jni.LocalProfileAssistant import net.typeblog.lpac_jni.LocalProfileAssistant
class DownloadWizardActivity: BaseEuiccAccessActivity() { class DownloadWizardActivity: BaseEuiccAccessActivity() {
@ -149,13 +155,39 @@ class DownloadWizardActivity: BaseEuiccAccessActivity() {
private fun onNextPressed() { private fun onNextPressed() {
hideIme() hideIme()
if (currentFragment?.hasNext == true) { nextButton.isEnabled = false
currentFragment?.beforeNext() progressBar.visibility = View.VISIBLE
val nextFrag = currentFragment?.createNextFragment() progressBar.isIndeterminate = true
if (nextFrag == null) {
finish() lifecycleScope.launch(Dispatchers.Main) {
} else { if (state.selectedLogicalSlot >= 0) {
showFragment(nextFrag, R.anim.slide_in_right, R.anim.slide_out_left) try {
// This is run on IO by default
euiccChannelManager.withEuiccChannel(state.selectedLogicalSlot) { channel ->
// Be _very_ sure that the channel we got is valid
if (!channel.valid) throw EuiccChannelManager.EuiccChannelNotFoundException()
}
} catch (e: EuiccChannelManager.EuiccChannelNotFoundException) {
Toast.makeText(
this@DownloadWizardActivity,
R.string.download_wizard_slot_removed,
Toast.LENGTH_LONG
).show()
finish()
}
}
progressBar.visibility = View.GONE
nextButton.isEnabled = true
if (currentFragment?.hasNext == true) {
currentFragment?.beforeNext()
val nextFrag = currentFragment?.createNextFragment()
if (nextFrag == null) {
finish()
} else {
showFragment(nextFrag, R.anim.slide_in_right, R.anim.slide_out_left)
}
} }
} }
} }

View file

@ -22,8 +22,6 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp" android:layout_marginHorizontal="24dp"
android:layout_marginVertical="12dp" android:layout_marginVertical="12dp"
android:maxLines="1"
android:ellipsize="marquee"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/euicc_info_title" app:layout_constraintTop_toBottomOf="@id/euicc_info_title"

View file

@ -82,7 +82,7 @@
android:maxLines="1" android:maxLines="1"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:inputType="textPassword" /> android:inputType="numberPassword" />
</com.google.android.material.textfield.TextInputLayout> </com.google.android.material.textfield.TextInputLayout>

View file

@ -3,6 +3,7 @@
<string name="no_euicc">No removable eUICC card accessible by this app is detected on this device. Insert a compatible card or a USB reader.</string> <string name="no_euicc">No removable eUICC card accessible by this app is detected on this device. Insert a compatible card or a USB reader.</string>
<string name="no_profile">No profiles (yet) on this eSIM.</string> <string name="no_profile">No profiles (yet) on this eSIM.</string>
<string name="unknown">Unknown</string> <string name="unknown">Unknown</string>
<string name="unavailable">Unavailable</string>
<string name="help">Help</string> <string name="help">Help</string>
<string name="reload">Reload Slots</string> <string name="reload">Reload Slots</string>
@ -31,6 +32,7 @@
<string name="toast_profile_delete_confirm_text_mismatched">Confirmation string mismatch</string> <string name="toast_profile_delete_confirm_text_mismatched">Confirmation string mismatch</string>
<string name="toast_iccid_copied">ICCID copied to clipboard</string> <string name="toast_iccid_copied">ICCID copied to clipboard</string>
<string name="toast_eid_copied">EID copied to clipboard</string> <string name="toast_eid_copied">EID copied to clipboard</string>
<string name="toast_atr_copied">ATR copied to clipboard</string>
<string name="usb_permission">Grant USB permission</string> <string name="usb_permission">Grant USB permission</string>
<string name="usb_permission_needed">Permission is needed to access the USB smart card reader.</string> <string name="usb_permission_needed">Permission is needed to access the USB smart card reader.</string>
@ -61,6 +63,7 @@
<string name="download_wizard">Download Wizard</string> <string name="download_wizard">Download Wizard</string>
<string name="download_wizard_back">Back</string> <string name="download_wizard_back">Back</string>
<string name="download_wizard_next">Next</string> <string name="download_wizard_next">Next</string>
<string name="download_wizard_slot_removed">Selected SIM has been removed</string>
<string name="download_wizard_slot_select">Select or confirm the eSIM you would like to download to:</string> <string name="download_wizard_slot_select">Select or confirm the eSIM you would like to download to:</string>
<string name="download_wizard_slot_type">Type:</string> <string name="download_wizard_slot_type">Type:</string>
<string name="download_wizard_slot_type_removable">Removable</string> <string name="download_wizard_slot_type_removable">Removable</string>
@ -121,6 +124,7 @@
<string name="euicc_info_access_mode">Access Mode</string> <string name="euicc_info_access_mode">Access Mode</string>
<string name="euicc_info_removable">Removable</string> <string name="euicc_info_removable">Removable</string>
<string name="euicc_info_eid" translatable="false">EID</string> <string name="euicc_info_eid" translatable="false">EID</string>
<string name="euicc_info_sgp22_version">SGP.22 Version</string>
<string name="euicc_info_firmware_version">eUICC OS Version</string> <string name="euicc_info_firmware_version">eUICC OS Version</string>
<string name="euicc_info_globalplatform_version">GlobalPlatform Version</string> <string name="euicc_info_globalplatform_version">GlobalPlatform Version</string>
<string name="euicc_info_sas_accreditation_number">SAS Accreditation Number</string> <string name="euicc_info_sas_accreditation_number">SAS Accreditation Number</string>
@ -130,6 +134,7 @@
<string name="euicc_info_ci_gsma_live">GSMA Live CI</string> <string name="euicc_info_ci_gsma_live">GSMA Live CI</string>
<string name="euicc_info_ci_gsma_test">GSMA Test CI</string> <string name="euicc_info_ci_gsma_test">GSMA Test CI</string>
<string name="euicc_info_ci_unknown">Unknown eSIM CI</string> <string name="euicc_info_ci_unknown">Unknown eSIM CI</string>
<string name="euicc_info_atr" translatable="false">Answer To Reset (ATR)</string>
<string name="yes">Yes</string> <string name="yes">Yes</string>
<string name="no">No</string> <string name="no">No</string>

View file

@ -2,6 +2,7 @@ package net.typeblog.lpac_jni
/* Corresponds to EuiccInfo2 in SGP.22 */ /* Corresponds to EuiccInfo2 in SGP.22 */
data class EuiccInfo2( data class EuiccInfo2(
val sgp22Version: String,
val profileVersion: String, val profileVersion: String,
val euiccFirmwareVersion: String, val euiccFirmwareVersion: String,
val globalPlatformVersion: String, val globalPlatformVersion: String,

View file

@ -62,6 +62,7 @@ internal object LpacJni {
external fun notificationsFree(head: Long) external fun notificationsFree(head: Long)
// EuiccInfo2 // EuiccInfo2
external fun euiccInfo2Free(info: Long) external fun euiccInfo2Free(info: Long)
external fun euiccInfo2GetSGP22Version(info: Long): String
external fun euiccInfo2GetProfileVersion(info: Long): String external fun euiccInfo2GetProfileVersion(info: Long): String
external fun euiccInfo2GetEuiccFirmwareVersion(info: Long): String external fun euiccInfo2GetEuiccFirmwareVersion(info: Long): String
external fun euiccInfo2GetGlobalPlatformVersion(info: Long): String external fun euiccInfo2GetGlobalPlatformVersion(info: Long): String

View file

@ -171,6 +171,7 @@ class LocalProfileAssistantImpl(
} }
val ret = EuiccInfo2( val ret = EuiccInfo2(
LpacJni.euiccInfo2GetSGP22Version(cInfo),
LpacJni.euiccInfo2GetProfileVersion(cInfo), LpacJni.euiccInfo2GetProfileVersion(cInfo),
LpacJni.euiccInfo2GetEuiccFirmwareVersion(cInfo), LpacJni.euiccInfo2GetEuiccFirmwareVersion(cInfo),
LpacJni.euiccInfo2GetGlobalPlatformVersion(cInfo), LpacJni.euiccInfo2GetGlobalPlatformVersion(cInfo),

View file

@ -266,6 +266,7 @@ void lpac_jni_euiccinfo2_free(struct es10c_ex_euiccinfo2 *info) {
LPAC_JNI_STRUCT_GETTER_NULL_TERM_LIST_NEXT(char*, stringArr) LPAC_JNI_STRUCT_GETTER_NULL_TERM_LIST_NEXT(char*, stringArr)
LPAC_JNI_STRUCT_FREE(struct es10c_ex_euiccinfo2, euiccInfo2, lpac_jni_euiccinfo2_free) LPAC_JNI_STRUCT_FREE(struct es10c_ex_euiccinfo2, euiccInfo2, lpac_jni_euiccinfo2_free)
LPAC_JNI_STRUCT_GETTER_STRING(struct es10c_ex_euiccinfo2, euiccInfo2, svn, SGP22Version)
LPAC_JNI_STRUCT_GETTER_STRING(struct es10c_ex_euiccinfo2, euiccInfo2, profileVersion, ProfileVersion) LPAC_JNI_STRUCT_GETTER_STRING(struct es10c_ex_euiccinfo2, euiccInfo2, profileVersion, ProfileVersion)
LPAC_JNI_STRUCT_GETTER_STRING(struct es10c_ex_euiccinfo2, euiccInfo2, euiccFirmwareVer, EuiccFirmwareVersion) LPAC_JNI_STRUCT_GETTER_STRING(struct es10c_ex_euiccinfo2, euiccInfo2, euiccFirmwareVer, EuiccFirmwareVersion)
LPAC_JNI_STRUCT_GETTER_STRING(struct es10c_ex_euiccinfo2, euiccInfo2, globalplatformVersion, GlobalPlatformVersion) LPAC_JNI_STRUCT_GETTER_STRING(struct es10c_ex_euiccinfo2, euiccInfo2, globalplatformVersion, GlobalPlatformVersion)