| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- package im.angry.openeuicc.service
- 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 im.angry.openeuicc.OpenEuiccApplication
- import im.angry.openeuicc.core.EuiccChannel
- import im.angry.openeuicc.util.*
- class OpenEuiccService : EuiccService() {
- private val openEuiccApplication
- get() = application as OpenEuiccApplication
- private fun findChannel(slotId: Int): EuiccChannel? =
- openEuiccApplication.euiccChannelManager
- .findEuiccChannelBySlotBlocking(slotId)
- override fun onGetEid(slotId: Int): String? =
- findChannel(slotId)?.lpa?.eid
- // When two eSIM cards are present on one device, the Android settings UI
- // gets confused and sets the incorrect slotId for profiles from one of
- // the cards. This function helps Detect this case and abort early.
- private fun EuiccChannel.profileExists(iccid: String?) =
- lpa.profiles.any { it.iccid == iccid }
- override fun onGetOtaStatus(slotId: Int): Int {
- // Not implemented
- return 5 // EUICC_OTA_STATUS_UNAVAILABLE
- }
- override fun onStartOtaIfNecessary(
- slotId: Int,
- statusChangedCallback: OtaStatusChangedCallback?
- ) {
- // Not implemented
- }
- override fun onGetDownloadableSubscriptionMetadata(
- slotId: Int,
- subscription: DownloadableSubscription?,
- forceDeactivateSim: Boolean
- ): GetDownloadableSubscriptionMetadataResult {
- // Stub: return as-is and do not fetch anything
- // This is incompatible with carrier eSIM apps; should we make it compatible?
- return GetDownloadableSubscriptionMetadataResult(RESULT_OK, subscription)
- }
- override fun onGetDefaultDownloadableSubscriptionList(
- slotId: Int,
- forceDeactivateSim: Boolean
- ): GetDefaultDownloadableSubscriptionListResult {
- // Stub: we do not implement this (as this would require phoning in a central GSMA server)
- return GetDefaultDownloadableSubscriptionListResult(RESULT_OK, arrayOf())
- }
- override fun onGetEuiccProfileInfoList(slotId: Int): GetEuiccProfileInfoListResult? {
- val channel = findChannel(slotId) ?: return null
- val profiles = channel.lpa.profiles.operational.map {
- EuiccProfileInfo.Builder(it.iccid).apply {
- setProfileName(it.name)
- setNickname(it.displayName)
- setServiceProviderName(it.providerName)
- setState(
- when (it.state) {
- LocalProfileInfo.State.Enabled -> EuiccProfileInfo.PROFILE_STATE_ENABLED
- LocalProfileInfo.State.Disabled -> EuiccProfileInfo.PROFILE_STATE_DISABLED
- }
- )
- setProfileClass(
- when (it.profileClass) {
- LocalProfileInfo.Clazz.Testing -> EuiccProfileInfo.PROFILE_CLASS_TESTING
- LocalProfileInfo.Clazz.Provisioning -> EuiccProfileInfo.PROFILE_CLASS_PROVISIONING
- LocalProfileInfo.Clazz.Operational -> EuiccProfileInfo.PROFILE_CLASS_OPERATIONAL
- }
- )
- }.build()
- }
- return GetEuiccProfileInfoListResult(RESULT_OK, profiles.toTypedArray(), channel.removable)
- }
- override fun onGetEuiccInfo(slotId: Int): EuiccInfo {
- return EuiccInfo("Unknown") // TODO: Can we actually implement this?
- }
- override fun onDeleteSubscription(slotId: Int, iccid: String): Int {
- try {
- val channel = findChannel(slotId) ?: return RESULT_FIRST_USER
- if (!channel.profileExists(iccid)) {
- return RESULT_FIRST_USER
- }
- val profile = channel.lpa.profiles.find {
- it.iccid == iccid
- } ?: return RESULT_FIRST_USER
- if (profile.state == LocalProfileInfo.State.Enabled) {
- // Must disable the profile first
- return RESULT_FIRST_USER
- }
- return if (channel.lpa.deleteProfile(iccid, Progress()) == "0") {
- RESULT_OK
- } else {
- RESULT_FIRST_USER
- }
- } catch (e: Exception) {
- return RESULT_FIRST_USER
- }
- }
- // TODO: on some devices we need to update the mapping (and potentially disable a pSIM)
- // for eSIM to be usable, in which case we will have to respect forceDeactivateSim.
- // This is the same for our custom LUI. Both have to take this into consideration.
- @Deprecated("Deprecated in Java")
- override fun onSwitchToSubscription(
- slotId: Int,
- iccid: String?,
- forceDeactivateSim: Boolean
- ): Int {
- try {
- val channel = findChannel(slotId) ?: return RESULT_FIRST_USER
- if (!channel.profileExists(iccid)) {
- 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 {
- return if (channel.lpa.enableProfile(iccid, 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 {
- val channel = findChannel(slotId) ?: return RESULT_FIRST_USER
- if (!channel.profileExists(iccid)) {
- return RESULT_FIRST_USER
- }
- val success = channel.lpa
- .setNickname(iccid, nickname)
- openEuiccApplication.subscriptionManager.tryRefreshCachedEuiccInfo(channel.cardId)
- return if (success) {
- RESULT_OK
- } else {
- RESULT_FIRST_USER
- }
- }
- @Deprecated("Deprecated in Java")
- override fun onEraseSubscriptions(slotId: Int): Int {
- // No-op
- return RESULT_FIRST_USER
- }
- override fun onRetainSubscriptionsForFactoryReset(slotId: Int): Int {
- // No-op -- we do not care
- return RESULT_FIRST_USER
- }
- }
|