From 8b5bc04e083c9fd855111faa91f2327c63b1e50e Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Tue, 3 May 2022 21:45:24 -0400 Subject: [PATCH 1/3] Implement renaming in OpenEuiccService --- .../openeuicc/service/OpenEuiccService.kt | 29 +++++-- .../java/com/truphone/lpa/LocalProfileInfo.kt | 11 +-- .../main/java/com/truphone/util/TextUtil.java | 36 --------- .../main/java/com/truphone/util/TextUtil.kt | 78 +++++++++++++++++++ 4 files changed, 100 insertions(+), 54 deletions(-) delete mode 100644 libs/lpad-sm-dp-plus-connector/src/main/java/com/truphone/util/TextUtil.java create mode 100644 libs/lpad-sm-dp-plus-connector/src/main/java/com/truphone/util/TextUtil.kt diff --git a/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt b/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt index 3e4d77d..070c972 100644 --- a/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt +++ b/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt @@ -3,17 +3,22 @@ package im.angry.openeuicc.service import android.service.euicc.* import android.telephony.euicc.DownloadableSubscription import android.telephony.euicc.EuiccInfo -import com.truphone.lpa.LocalProfileAssistant import com.truphone.lpa.LocalProfileInfo +import com.truphone.util.TextUtil import im.angry.openeuicc.OpenEuiccApplication +import im.angry.openeuicc.core.EuiccChannel +import im.angry.openeuicc.util.* class OpenEuiccService : EuiccService() { - private fun findLpa(slotId: Int): LocalProfileAssistant? = - (application as OpenEuiccApplication).euiccChannelManager - .findEuiccChannelBySlotBlocking(slotId)?.lpa + private val openEuiccApplication + get() = application as OpenEuiccApplication + + private fun findChannel(slotId: Int): EuiccChannel? = + openEuiccApplication.euiccChannelManager + .findEuiccChannelBySlotBlocking(slotId) override fun onGetEid(slotId: Int): String? = - findLpa(slotId)?.eid + findChannel(slotId)?.lpa?.eid override fun onGetOtaStatus(slotId: Int): Int { // Not implemented @@ -46,7 +51,7 @@ class OpenEuiccService : EuiccService() { } override fun onGetEuiccProfileInfoList(slotId: Int): GetEuiccProfileInfoListResult? { - val profiles = (findLpa(slotId) ?: return null).profiles.filter { + val profiles = (findChannel(slotId) ?: return null).lpa.profiles.filter { it.profileClass != LocalProfileInfo.Clazz.Testing }.map { EuiccProfileInfo.Builder(it.iccidLittleEndian).apply { @@ -89,8 +94,16 @@ class OpenEuiccService : EuiccService() { TODO("Not yet implemented") } - override fun onUpdateSubscriptionNickname(slotId: Int, iccid: String?, nickname: String?): Int { - TODO("Not yet implemented") + override fun onUpdateSubscriptionNickname(slotId: Int, iccid: String, nickname: String?): Int { + val channel = findChannel(slotId) ?: return RESULT_FIRST_USER + val success = channel.lpa + .setNickname(TextUtil.iccidLittleToBig(iccid), nickname) + openEuiccApplication.subscriptionManager.tryRefreshCachedEuiccInfo(channel.cardId) + return if (success) { + RESULT_OK + } else { + RESULT_FIRST_USER + } } @Deprecated("Deprecated in Java") diff --git a/libs/lpad-sm-dp-plus-connector/src/main/java/com/truphone/lpa/LocalProfileInfo.kt b/libs/lpad-sm-dp-plus-connector/src/main/java/com/truphone/lpa/LocalProfileInfo.kt index 98e9f9e..691d6b2 100644 --- a/libs/lpad-sm-dp-plus-connector/src/main/java/com/truphone/lpa/LocalProfileInfo.kt +++ b/libs/lpad-sm-dp-plus-connector/src/main/java/com/truphone/lpa/LocalProfileInfo.kt @@ -14,8 +14,8 @@ package com.truphone.lpa +import com.truphone.util.TextUtil.iccidBigToLittle import java.lang.IllegalArgumentException -import java.lang.StringBuilder data class LocalProfileInfo( val iccid: String, @@ -56,14 +56,5 @@ data class LocalProfileInfo( "2" -> Clazz.Operational else -> throw IllegalArgumentException("Unknown profile class $clazz") } - - private fun iccidBigToLittle(iccid: String): String { - val builder = StringBuilder() - for (i in 0 until iccid.length / 2) { - builder.append(iccid[i * 2 + 1]) - if (iccid[i * 2] != 'F') builder.append(iccid[i * 2]) - } - return builder.toString() - } } } \ No newline at end of file diff --git a/libs/lpad-sm-dp-plus-connector/src/main/java/com/truphone/util/TextUtil.java b/libs/lpad-sm-dp-plus-connector/src/main/java/com/truphone/util/TextUtil.java deleted file mode 100644 index b78ddf4..0000000 --- a/libs/lpad-sm-dp-plus-connector/src/main/java/com/truphone/util/TextUtil.java +++ /dev/null @@ -1,36 +0,0 @@ -package com.truphone.util; - -public class TextUtil { - private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', - '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; - - /** - * Converts the given byte array to its hex representation. - * - * @param data The byte array to convert. - * @return Hex-encoded data as a string. - * @see #toHexString(byte[], int, int) - */ - public static String toHexString(byte[] data) { - return data == null ? null : toHexString(data, 0, data.length); - } - - /** - * Converts the given byte array slice to its hex representation. - * - * @param data The byte array to convert. - * @param offset Slice start. - * @param length Slice length. - * @return Hex-encoded data as a string. - */ - public static String toHexString(final byte[] data, int offset, int length) { - final char[] result = new char[length << 1]; - length += offset; - for (int i = 0; offset < length; ++offset) { - result[i++] = HEX_DIGITS[(data[offset] >>> 4) & 0x0F]; - result[i++] = HEX_DIGITS[data[offset] & 0x0F]; - } - return new String(result); - } - -} diff --git a/libs/lpad-sm-dp-plus-connector/src/main/java/com/truphone/util/TextUtil.kt b/libs/lpad-sm-dp-plus-connector/src/main/java/com/truphone/util/TextUtil.kt new file mode 100644 index 0000000..9de4d5a --- /dev/null +++ b/libs/lpad-sm-dp-plus-connector/src/main/java/com/truphone/util/TextUtil.kt @@ -0,0 +1,78 @@ +package com.truphone.util + +import java.lang.StringBuilder + +object TextUtil { + private val HEX_DIGITS = charArrayOf( + '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' + ) + + /** + * Converts the given byte array to its hex representation. + * + * @param data The byte array to convert. + * @return Hex-encoded data as a string. + * @see .toHexString + */ + @JvmStatic + fun toHexString(data: ByteArray?): String? { + return if (data == null) null else toHexString(data, 0, data.size) + } + + /** + * Converts the given byte array slice to its hex representation. + * + * @param data The byte array to convert. + * @param offset Slice start. + * @param length Slice length. + * @return Hex-encoded data as a string. + */ + @JvmStatic + fun toHexString(data: ByteArray, offset: Int, length: Int): String { + var offset = offset + var length = length + val result = CharArray(length shl 1) + length += offset + var i = 0 + while (offset < length) { + result[i++] = HEX_DIGITS[data[offset].toInt() ushr 4 and 0x0F] + result[i++] = HEX_DIGITS[data[offset].toInt() and 0x0F] + ++offset + } + return String(result) + } + + /** + * Converts a big-endian representation of ICCID into little-endian + * Big-endian representation is used internally in communication with the SIM. + * + * @param iccid The ICCID to be converted + */ + fun iccidBigToLittle(iccid: String): String { + val builder = StringBuilder() + for (i in 0 until iccid.length / 2) { + builder.append(iccid[i * 2 + 1]) + if (iccid[i * 2] != 'F') builder.append(iccid[i * 2]) + } + return builder.toString() + } + + /** + * Converts a little-endian representation of ICCID into big-endian + * + * @param iccid The ICCID to be converted + */ + fun iccidLittleToBig(iccidLittle: String): String { + val builder = StringBuilder() + for (i in 0 until iccidLittle.length / 2) { + builder.append(iccidLittle[i * 2 + 1]) + builder.append(iccidLittle[i * 2]) + } + if (iccidLittle.length % 2 == 1) { + builder.append('F') + builder.append(iccidLittle[iccidLittle.length - 1]) + } + return builder.toString() + } +} \ No newline at end of file From 864476939fcafe4aee0893ac896bafb69cce5a80 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Tue, 3 May 2022 21:56:46 -0400 Subject: [PATCH 2/3] Implement subscription switching for OpenEuiccService --- .../openeuicc/service/OpenEuiccService.kt | 28 ++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt b/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt index 070c972..7c7642e 100644 --- a/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt +++ b/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt @@ -4,6 +4,7 @@ import android.service.euicc.* import android.telephony.euicc.DownloadableSubscription import android.telephony.euicc.EuiccInfo import com.truphone.lpa.LocalProfileInfo +import com.truphone.lpad.progress.Progress import com.truphone.util.TextUtil import im.angry.openeuicc.OpenEuiccApplication import im.angry.openeuicc.core.EuiccChannel @@ -91,7 +92,32 @@ class OpenEuiccService : EuiccService() { iccid: String?, forceDeactivateSim: Boolean ): Int { - TODO("Not yet implemented") + try { + val channel = findChannel(slotId) ?: return RESULT_FIRST_USER + if (iccid == null) { + // Disable active profile + val activeProfile = channel.lpa.profiles.filter { + it.state == LocalProfileInfo.State.Enabled + }[0] ?: return RESULT_OK + + return if (channel.lpa.disableProfile(activeProfile.iccid, Progress()) == "0") { + RESULT_OK + } else { + RESULT_FIRST_USER + } + } else { + val iccidBig = TextUtil.iccidLittleToBig(iccid) + return if (channel.lpa.enableProfile(iccidBig, Progress()) == "0") { + RESULT_OK + } else { + RESULT_FIRST_USER + } + } + } catch (e: Exception) { + return RESULT_FIRST_USER + } finally { + openEuiccApplication.euiccChannelManager.invalidate() + } } override fun onUpdateSubscriptionNickname(slotId: Int, iccid: String, nickname: String?): Int { From 54f02ff638b3eaf017c00210ec71d7eabb5a9fb3 Mon Sep 17 00:00:00 2001 From: Peter Cai Date: Tue, 3 May 2022 22:05:16 -0400 Subject: [PATCH 3/3] Implement subscription removal for OpenEuiccService --- .../openeuicc/service/OpenEuiccService.kt | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt b/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt index 7c7642e..b651085 100644 --- a/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt +++ b/app/src/main/java/im/angry/openeuicc/service/OpenEuiccService.kt @@ -82,8 +82,28 @@ class OpenEuiccService : EuiccService() { return EuiccInfo("Unknown") // TODO: Can we actually implement this? } - override fun onDeleteSubscription(slotId: Int, iccid: String?): Int { - TODO("Not yet implemented") + override fun onDeleteSubscription(slotId: Int, iccid: String): Int { + try { + val channel = findChannel(slotId) ?: return RESULT_FIRST_USER + val iccidBig = TextUtil.iccidLittleToBig(iccid) + + val profile = channel.lpa.profiles.find { + it.iccid == iccidBig + } ?: return RESULT_FIRST_USER + + if (profile.state == LocalProfileInfo.State.Enabled) { + // Must disable the profile first + return RESULT_FIRST_USER + } + + return if (channel.lpa.deleteProfile(iccidBig, Progress()) == "0") { + RESULT_OK + } else { + RESULT_FIRST_USER + } + } catch (e: Exception) { + return RESULT_FIRST_USER + } } @Deprecated("Deprecated in Java") @@ -96,9 +116,9 @@ class OpenEuiccService : EuiccService() { val channel = findChannel(slotId) ?: return RESULT_FIRST_USER if (iccid == null) { // Disable active profile - val activeProfile = channel.lpa.profiles.filter { + val activeProfile = channel.lpa.profiles.find { it.state == LocalProfileInfo.State.Enabled - }[0] ?: return RESULT_OK + } ?: return RESULT_OK return if (channel.lpa.disableProfile(activeProfile.iccid, Progress()) == "0") { RESULT_OK