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..b651085 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,23 @@ 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.lpad.progress.Progress +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 +52,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 { @@ -76,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") @@ -86,11 +112,44 @@ 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.find { + it.state == LocalProfileInfo.State.Enabled + } ?: 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 { - 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