Compare commits
3 commits
f2a98b4fcb
...
54f02ff638
Author | SHA1 | Date | |
---|---|---|---|
54f02ff638 | |||
864476939f | |||
8b5bc04e08 |
4 changed files with 149 additions and 57 deletions
|
@ -3,17 +3,23 @@ package im.angry.openeuicc.service
|
||||||
import android.service.euicc.*
|
import android.service.euicc.*
|
||||||
import android.telephony.euicc.DownloadableSubscription
|
import android.telephony.euicc.DownloadableSubscription
|
||||||
import android.telephony.euicc.EuiccInfo
|
import android.telephony.euicc.EuiccInfo
|
||||||
import com.truphone.lpa.LocalProfileAssistant
|
|
||||||
import com.truphone.lpa.LocalProfileInfo
|
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.OpenEuiccApplication
|
||||||
|
import im.angry.openeuicc.core.EuiccChannel
|
||||||
|
import im.angry.openeuicc.util.*
|
||||||
|
|
||||||
class OpenEuiccService : EuiccService() {
|
class OpenEuiccService : EuiccService() {
|
||||||
private fun findLpa(slotId: Int): LocalProfileAssistant? =
|
private val openEuiccApplication
|
||||||
(application as OpenEuiccApplication).euiccChannelManager
|
get() = application as OpenEuiccApplication
|
||||||
.findEuiccChannelBySlotBlocking(slotId)?.lpa
|
|
||||||
|
private fun findChannel(slotId: Int): EuiccChannel? =
|
||||||
|
openEuiccApplication.euiccChannelManager
|
||||||
|
.findEuiccChannelBySlotBlocking(slotId)
|
||||||
|
|
||||||
override fun onGetEid(slotId: Int): String? =
|
override fun onGetEid(slotId: Int): String? =
|
||||||
findLpa(slotId)?.eid
|
findChannel(slotId)?.lpa?.eid
|
||||||
|
|
||||||
override fun onGetOtaStatus(slotId: Int): Int {
|
override fun onGetOtaStatus(slotId: Int): Int {
|
||||||
// Not implemented
|
// Not implemented
|
||||||
|
@ -46,7 +52,7 @@ class OpenEuiccService : EuiccService() {
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onGetEuiccProfileInfoList(slotId: Int): GetEuiccProfileInfoListResult? {
|
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
|
it.profileClass != LocalProfileInfo.Clazz.Testing
|
||||||
}.map {
|
}.map {
|
||||||
EuiccProfileInfo.Builder(it.iccidLittleEndian).apply {
|
EuiccProfileInfo.Builder(it.iccidLittleEndian).apply {
|
||||||
|
@ -76,8 +82,28 @@ class OpenEuiccService : EuiccService() {
|
||||||
return EuiccInfo("Unknown") // TODO: Can we actually implement this?
|
return EuiccInfo("Unknown") // TODO: Can we actually implement this?
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDeleteSubscription(slotId: Int, iccid: String?): Int {
|
override fun onDeleteSubscription(slotId: Int, iccid: String): Int {
|
||||||
TODO("Not yet implemented")
|
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")
|
@Deprecated("Deprecated in Java")
|
||||||
|
@ -86,11 +112,44 @@ class OpenEuiccService : EuiccService() {
|
||||||
iccid: String?,
|
iccid: String?,
|
||||||
forceDeactivateSim: Boolean
|
forceDeactivateSim: Boolean
|
||||||
): Int {
|
): 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 {
|
override fun onUpdateSubscriptionNickname(slotId: Int, iccid: String, nickname: String?): Int {
|
||||||
TODO("Not yet implemented")
|
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")
|
@Deprecated("Deprecated in Java")
|
||||||
|
|
|
@ -14,8 +14,8 @@
|
||||||
|
|
||||||
package com.truphone.lpa
|
package com.truphone.lpa
|
||||||
|
|
||||||
|
import com.truphone.util.TextUtil.iccidBigToLittle
|
||||||
import java.lang.IllegalArgumentException
|
import java.lang.IllegalArgumentException
|
||||||
import java.lang.StringBuilder
|
|
||||||
|
|
||||||
data class LocalProfileInfo(
|
data class LocalProfileInfo(
|
||||||
val iccid: String,
|
val iccid: String,
|
||||||
|
@ -56,14 +56,5 @@ data class LocalProfileInfo(
|
||||||
"2" -> Clazz.Operational
|
"2" -> Clazz.Operational
|
||||||
else -> throw IllegalArgumentException("Unknown profile class $clazz")
|
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()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -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);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue