Compare commits

..

No commits in common. "d0b3d54c6662be0f6c7e8668ff7389f825d26956" and "43f247a71bec450fa75856cfdc3ee4d38968e354" have entirely different histories.

19 changed files with 38 additions and 107 deletions

View file

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

View file

@ -184,30 +184,20 @@ open class DefaultEuiccChannelManager(
}
override suspend fun waitForReconnect(physicalSlotId: Int, portId: Int, timeoutMillis: Long) {
if (physicalSlotId == EuiccChannelManager.USB_CHANNEL_ID) {
usbChannel?.close()
usbChannel = null
} else {
// If there is already a valid channel, we close it proactively
// 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()
}
if (physicalSlotId == EuiccChannelManager.USB_CHANNEL_ID) return
// If there is already a valid channel, we close it proactively
// 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) {
while (true) {
try {
val channel = if (physicalSlotId == EuiccChannelManager.USB_CHANNEL_ID) {
// tryOpenUsbEuiccChannel() will always try to reopen the channel, even if
// a USB channel already exists
tryOpenUsbEuiccChannel()
usbChannel!!
} else {
// tryOpenEuiccChannel() will automatically dispose of invalid channels
// and recreate when needed
findEuiccChannelByPort(physicalSlotId, portId)!!
}
// tryOpenEuiccChannel() will automatically dispose of invalid channels
// and recreate when needed
val channel = findEuiccChannelByPort(physicalSlotId, portId)!!
check(channel.valid) { "Invalid channel" }
break
} catch (e: Exception) {

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -41,7 +41,7 @@ class EuiccInfoActivity : BaseEuiccAccessActivity(), OpenEuiccContextMarker {
@StringRes
val titleResId: Int,
val content: String?,
val copiedToastResId: Int? = null,
val copiedToastResId: Int? = null
)
override fun onCreate(savedInstanceState: Bundle?) {
@ -114,7 +114,6 @@ class EuiccInfoActivity : BaseEuiccAccessActivity(), OpenEuiccContextMarker {
)
)
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_globalplatform_version, info?.globalPlatformVersion))
add(Item(R.string.euicc_info_pp_version, info?.ppVersion))
@ -134,13 +133,6 @@ class EuiccInfoActivity : BaseEuiccAccessActivity(), OpenEuiccContextMarker {
}
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 =

View file

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

View file

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

View file

@ -5,21 +5,15 @@ import android.view.View
import android.view.inputmethod.InputMethodManager
import android.widget.Button
import android.widget.ProgressBar
import android.widget.Toast
import androidx.activity.OnBackPressedCallback
import androidx.activity.enableEdgeToEdge
import androidx.core.view.ViewCompat
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.updatePadding
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import im.angry.openeuicc.common.R
import im.angry.openeuicc.core.EuiccChannelManager
import im.angry.openeuicc.ui.BaseEuiccAccessActivity
import im.angry.openeuicc.util.*
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import net.typeblog.lpac_jni.LocalProfileAssistant
class DownloadWizardActivity: BaseEuiccAccessActivity() {
@ -155,39 +149,13 @@ class DownloadWizardActivity: BaseEuiccAccessActivity() {
private fun onNextPressed() {
hideIme()
nextButton.isEnabled = false
progressBar.visibility = View.VISIBLE
progressBar.isIndeterminate = true
lifecycleScope.launch(Dispatchers.Main) {
if (state.selectedLogicalSlot >= 0) {
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)
}
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,6 +22,8 @@
android:layout_height="wrap_content"
android:layout_marginHorizontal="24dp"
android:layout_marginVertical="12dp"
android:maxLines="1"
android:ellipsize="marquee"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/euicc_info_title"

View file

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

View file

@ -3,7 +3,6 @@
<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="unknown">Unknown</string>
<string name="unavailable">Unavailable</string>
<string name="help">Help</string>
<string name="reload">Reload Slots</string>
@ -32,7 +31,6 @@
<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_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_needed">Permission is needed to access the USB smart card reader.</string>
@ -63,7 +61,6 @@
<string name="download_wizard">Download Wizard</string>
<string name="download_wizard_back">Back</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_type">Type:</string>
<string name="download_wizard_slot_type_removable">Removable</string>
@ -124,7 +121,6 @@
<string name="euicc_info_access_mode">Access Mode</string>
<string name="euicc_info_removable">Removable</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_globalplatform_version">GlobalPlatform Version</string>
<string name="euicc_info_sas_accreditation_number">SAS Accreditation Number</string>
@ -134,7 +130,6 @@
<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_unknown">Unknown eSIM CI</string>
<string name="euicc_info_atr" translatable="false">Answer To Reset (ATR)</string>
<string name="yes">Yes</string>
<string name="no">No</string>

View file

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

View file

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

View file

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

View file

@ -266,7 +266,6 @@ void lpac_jni_euiccinfo2_free(struct es10c_ex_euiccinfo2 *info) {
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_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, euiccFirmwareVer, EuiccFirmwareVersion)
LPAC_JNI_STRUCT_GETTER_STRING(struct es10c_ex_euiccinfo2, euiccInfo2, globalplatformVersion, GlobalPlatformVersion)