forked from PeterCxy/OpenEUICC
Compare commits
No commits in common. "carrier-apps" and "master" have entirely different histories.
carrier-ap
...
master
15 changed files with 13 additions and 414 deletions
|
@ -45,9 +45,8 @@ class LocalProfileAssistantWrapper(orig: LocalProfileAssistant) :
|
||||||
matchingId: String?,
|
matchingId: String?,
|
||||||
imei: String?,
|
imei: String?,
|
||||||
confirmationCode: String?,
|
confirmationCode: String?,
|
||||||
preview: Boolean,
|
|
||||||
callback: ProfileDownloadCallback
|
callback: ProfileDownloadCallback
|
||||||
) = lpa.downloadProfile(smdp, matchingId, imei, confirmationCode, preview, callback)
|
) = lpa.downloadProfile(smdp, matchingId, imei, confirmationCode, callback)
|
||||||
|
|
||||||
override fun deleteNotification(seqNumber: Long): Boolean = lpa.deleteNotification(seqNumber)
|
override fun deleteNotification(seqNumber: Long): Boolean = lpa.deleteNotification(seqNumber)
|
||||||
|
|
||||||
|
|
|
@ -373,7 +373,6 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
|
||||||
smdp: String,
|
smdp: String,
|
||||||
matchingId: String?,
|
matchingId: String?,
|
||||||
confirmationCode: String?,
|
confirmationCode: String?,
|
||||||
preview: Boolean,
|
|
||||||
imei: String?
|
imei: String?
|
||||||
): ForegroundTaskSubscriberFlow =
|
): ForegroundTaskSubscriberFlow =
|
||||||
launchForegroundTask(
|
launchForegroundTask(
|
||||||
|
@ -388,10 +387,7 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
|
||||||
matchingId,
|
matchingId,
|
||||||
imei,
|
imei,
|
||||||
confirmationCode,
|
confirmationCode,
|
||||||
preview,
|
|
||||||
object : ProfileDownloadCallback {
|
object : ProfileDownloadCallback {
|
||||||
override fun onMetadataReceived(profileName: String, iccid: String, appCerts: String) {
|
|
||||||
}
|
|
||||||
override fun onStateUpdate(state: ProfileDownloadCallback.DownloadState) {
|
override fun onStateUpdate(state: ProfileDownloadCallback.DownloadState) {
|
||||||
if (state.progress == 0) return
|
if (state.progress == 0) return
|
||||||
foregroundTaskState.value =
|
foregroundTaskState.value =
|
||||||
|
|
|
@ -29,7 +29,6 @@ class DownloadWizardActivity: BaseEuiccAccessActivity() {
|
||||||
var smdp: String,
|
var smdp: String,
|
||||||
var matchingId: String?,
|
var matchingId: String?,
|
||||||
var confirmationCode: String?,
|
var confirmationCode: String?,
|
||||||
var preview: Boolean,
|
|
||||||
var imei: String?,
|
var imei: String?,
|
||||||
var downloadStarted: Boolean,
|
var downloadStarted: Boolean,
|
||||||
var downloadTaskID: Long,
|
var downloadTaskID: Long,
|
||||||
|
@ -67,7 +66,6 @@ class DownloadWizardActivity: BaseEuiccAccessActivity() {
|
||||||
"",
|
"",
|
||||||
null,
|
null,
|
||||||
null,
|
null,
|
||||||
false,
|
|
||||||
null,
|
null,
|
||||||
false,
|
false,
|
||||||
-1,
|
-1,
|
||||||
|
|
|
@ -160,7 +160,6 @@ class DownloadWizardProgressFragment : DownloadWizardActivity.DownloadWizardStep
|
||||||
state.smdp,
|
state.smdp,
|
||||||
state.matchingId,
|
state.matchingId,
|
||||||
state.confirmationCode,
|
state.confirmationCode,
|
||||||
state.preview,
|
|
||||||
state.imei
|
state.imei
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -53,18 +53,6 @@
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity android:name=".ui.UserConsentActivity"
|
|
||||||
android:exported="true"
|
|
||||||
android:permission="android.permission.BIND_EUICC_SERVICE"
|
|
||||||
android:theme="@style/Theme.UserConsentDialog">
|
|
||||||
<intent-filter android:priority="100">
|
|
||||||
<action android:name="android.service.euicc.action.RESOLVE_DEACTIVATE_SIM" />
|
|
||||||
<action android:name="android.service.euicc.action.RESOLVE_NO_PRIVILEGES" />
|
|
||||||
<category android:name="android.intent.category.DEFAULT" />
|
|
||||||
<category android:name="android.service.euicc.category.EUICC_UI" />
|
|
||||||
</intent-filter>
|
|
||||||
</activity>
|
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
|
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
|
||||||
android:enabled="false"
|
android:enabled="false"
|
||||||
|
|
|
@ -3,9 +3,7 @@ package im.angry.openeuicc.service
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.Bundle
|
|
||||||
import android.service.euicc.*
|
import android.service.euicc.*
|
||||||
import android.telephony.UiccAccessRule
|
|
||||||
import android.telephony.UiccSlotMapping
|
import android.telephony.UiccSlotMapping
|
||||||
import android.telephony.euicc.DownloadableSubscription
|
import android.telephony.euicc.DownloadableSubscription
|
||||||
import android.telephony.euicc.EuiccInfo
|
import android.telephony.euicc.EuiccInfo
|
||||||
|
@ -18,7 +16,6 @@ import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.runBlocking
|
import kotlinx.coroutines.runBlocking
|
||||||
import kotlin.IllegalStateException
|
import kotlin.IllegalStateException
|
||||||
import net.typeblog.lpac_jni.ProfileDownloadCallback
|
|
||||||
|
|
||||||
class OpenEuiccService : EuiccService(), OpenEuiccContextMarker {
|
class OpenEuiccService : EuiccService(), OpenEuiccContextMarker {
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -150,186 +147,14 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker {
|
||||||
// Not implemented
|
// Not implemented
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onDownloadSubscription(
|
|
||||||
slotIndex: Int,
|
|
||||||
subscription: DownloadableSubscription,
|
|
||||||
switchAfterDownload: Boolean,
|
|
||||||
forceDeactivateSim: Boolean,
|
|
||||||
resolvedBundle: Bundle?
|
|
||||||
): DownloadSubscriptionResult {
|
|
||||||
Log.i(TAG, "onDownloadSubscription slotIndex=$slotIndex switchAfterDownload=$switchAfterDownload forceDeactivateSim=$forceDeactivateSim resolvedBundle=$resolvedBundle")
|
|
||||||
|
|
||||||
Log.i(TAG, "onDownloadSubscription getEncodedActivationCode()=${subscription.getEncodedActivationCode()}")
|
|
||||||
|
|
||||||
val activationParts = subscription.getEncodedActivationCode()?.split("$")
|
|
||||||
val smdpAddress = activationParts?.getOrNull(1)
|
|
||||||
val matchingId = activationParts?.getOrNull(2)
|
|
||||||
|
|
||||||
if (smdpAddress.isNullOrBlank() || matchingId.isNullOrBlank()) {
|
|
||||||
Log.e(TAG, "SMDP address or activation code is not valid.")
|
|
||||||
return DownloadSubscriptionResult(
|
|
||||||
RESULT_FIRST_USER,
|
|
||||||
0,
|
|
||||||
-1
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i(TAG, "onDownloadSubscription smdpAddress=$smdpAddress")
|
|
||||||
Log.i(TAG, "onDownloadSubscription matchingId=$matchingId")
|
|
||||||
|
|
||||||
var profileIccid: String? = null
|
|
||||||
|
|
||||||
return try {
|
|
||||||
withEuiccChannelManager {
|
|
||||||
val portId = euiccChannelManager.findFirstAvailablePort(slotIndex)
|
|
||||||
if (portId < 0) {
|
|
||||||
Log.e(TAG, "No available port for slotIndex=$slotIndex")
|
|
||||||
return@withEuiccChannelManager DownloadSubscriptionResult(
|
|
||||||
RESULT_FIRST_USER,
|
|
||||||
0,
|
|
||||||
-1
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
euiccChannelManager.withEuiccChannel(slotIndex, portId) { channel ->
|
|
||||||
channel.lpa.downloadProfile(
|
|
||||||
smdp = smdpAddress,
|
|
||||||
matchingId = matchingId,
|
|
||||||
imei = telephonyManager.imei,
|
|
||||||
confirmationCode = subscription.confirmationCode,
|
|
||||||
preview = false,
|
|
||||||
callback = object : ProfileDownloadCallback {
|
|
||||||
override fun onMetadataReceived(profileName: String, iccid: String, appCerts: String) {
|
|
||||||
Log.i(TAG, "Profile metadata received: profileName=$profileName iccid=$iccid")
|
|
||||||
profileIccid = iccid
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStateUpdate(state: ProfileDownloadCallback.DownloadState) {
|
|
||||||
Log.i(TAG, "Profile download state update: progress=${state.progress}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
if (switchAfterDownload) {
|
|
||||||
onSwitchToSubscriptionWithPort(slotIndex, portId, profileIccid, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
DownloadSubscriptionResult(
|
|
||||||
RESULT_OK,
|
|
||||||
0,
|
|
||||||
channel.cardId
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(TAG, "Error during profile download: ${e.message}", e)
|
|
||||||
DownloadSubscriptionResult(
|
|
||||||
RESULT_FIRST_USER,
|
|
||||||
0,
|
|
||||||
-1
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onGetDownloadableSubscriptionMetadata(
|
override fun onGetDownloadableSubscriptionMetadata(
|
||||||
slotId: Int,
|
slotId: Int,
|
||||||
subscription: DownloadableSubscription?,
|
subscription: DownloadableSubscription?,
|
||||||
forceDeactivateSim: Boolean
|
forceDeactivateSim: Boolean
|
||||||
): GetDownloadableSubscriptionMetadataResult {
|
): GetDownloadableSubscriptionMetadataResult {
|
||||||
Log.i(TAG, "onGetDownloadableSubscriptionMetadata slotId=$slotId")
|
// Stub: return as-is and do not fetch anything
|
||||||
|
// This is incompatible with carrier eSIM apps; should we make it compatible?
|
||||||
if (subscription == null) {
|
return GetDownloadableSubscriptionMetadataResult(RESULT_OK, subscription)
|
||||||
return GetDownloadableSubscriptionMetadataResult(
|
|
||||||
RESULT_FIRST_USER,
|
|
||||||
null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i(TAG, "onGetDownloadableSubscriptionMetadata getEncodedActivationCode()=${subscription.getEncodedActivationCode()}")
|
|
||||||
|
|
||||||
// Extract SMDP address and matching ID
|
|
||||||
val activationParts = subscription.getEncodedActivationCode()?.split("$")
|
|
||||||
val smdpAddress = activationParts?.getOrNull(1)
|
|
||||||
val matchingId = activationParts?.getOrNull(2)
|
|
||||||
|
|
||||||
if (smdpAddress.isNullOrBlank() || matchingId.isNullOrBlank()) {
|
|
||||||
Log.e(TAG, "SMDP address or activation code is not valid.")
|
|
||||||
return GetDownloadableSubscriptionMetadataResult(
|
|
||||||
RESULT_FIRST_USER,
|
|
||||||
null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Log.i(TAG, "onGetDownloadableSubscriptionMetadata smdpAddress=$smdpAddress")
|
|
||||||
Log.i(TAG, "onGetDownloadableSubscriptionMetadata matchingId=$matchingId")
|
|
||||||
|
|
||||||
val portId = withEuiccChannelManager {
|
|
||||||
euiccChannelManager.findFirstAvailablePort(slotId)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (portId < 0) {
|
|
||||||
Log.e(TAG, "No available port for slotId=$slotId")
|
|
||||||
return GetDownloadableSubscriptionMetadataResult(
|
|
||||||
RESULT_FIRST_USER,
|
|
||||||
null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
var updatedSubscription: DownloadableSubscription? = null
|
|
||||||
|
|
||||||
return try {
|
|
||||||
withEuiccChannelManager {
|
|
||||||
euiccChannelManager.withEuiccChannel(slotId, portId) { channel ->
|
|
||||||
channel.lpa.downloadProfile(
|
|
||||||
smdp = smdpAddress,
|
|
||||||
matchingId = matchingId,
|
|
||||||
imei = telephonyManager.imei,
|
|
||||||
confirmationCode = subscription.confirmationCode,
|
|
||||||
preview = true,
|
|
||||||
callback = object : ProfileDownloadCallback {
|
|
||||||
override fun onMetadataReceived(profileName: String, iccid: String, appCerts: String) {
|
|
||||||
Log.i(TAG, "App certificates: appCerts=$appCerts")
|
|
||||||
Log.i(TAG, "Profile metadata received: profileName=$profileName")
|
|
||||||
|
|
||||||
val accessRules = appCerts.split(",").mapNotNull { certHex ->
|
|
||||||
try {
|
|
||||||
val certBytes = certHex.chunked(2).map { it.toInt(16).toByte() }.toByteArray()
|
|
||||||
UiccAccessRule(certBytes, null, 0L)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(TAG, "Failed to parse certificate hash: $certHex", e)
|
|
||||||
null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updatedSubscription = DownloadableSubscription.Builder(subscription)
|
|
||||||
.setCarrierName(profileName)
|
|
||||||
.setAccessRules(accessRules)
|
|
||||||
.build()
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun onStateUpdate(state: ProfileDownloadCallback.DownloadState) {
|
|
||||||
Log.i(TAG, "Profile download state update: progress=${state.progress}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (updatedSubscription == null) {
|
|
||||||
throw IllegalStateException("Metadata not received from callback")
|
|
||||||
}
|
|
||||||
|
|
||||||
GetDownloadableSubscriptionMetadataResult(
|
|
||||||
RESULT_OK,
|
|
||||||
updatedSubscription
|
|
||||||
)
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e(TAG, "Error during profile download: ${e.message}", e)
|
|
||||||
GetDownloadableSubscriptionMetadataResult(
|
|
||||||
RESULT_FIRST_USER,
|
|
||||||
null
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onGetDefaultDownloadableSubscriptionList(
|
override fun onGetDefaultDownloadableSubscriptionList(
|
||||||
|
|
|
@ -1,94 +0,0 @@
|
||||||
package im.angry.openeuicc.ui
|
|
||||||
|
|
||||||
import android.app.Activity
|
|
||||||
import android.app.AlertDialog
|
|
||||||
import android.content.pm.PackageManager
|
|
||||||
import android.os.Bundle
|
|
||||||
import android.telephony.SubscriptionManager
|
|
||||||
import android.telephony.TelephonyManager
|
|
||||||
import android.telephony.euicc.EuiccManager
|
|
||||||
import android.text.TextUtils
|
|
||||||
import android.util.Log
|
|
||||||
|
|
||||||
import im.angry.openeuicc.R
|
|
||||||
|
|
||||||
class UserConsentActivity : Activity() {
|
|
||||||
|
|
||||||
private lateinit var euiccManager: EuiccManager
|
|
||||||
private lateinit var subscriptionManager: SubscriptionManager
|
|
||||||
private lateinit var telephonyManager: TelephonyManager
|
|
||||||
private var mCardId: Int = -1
|
|
||||||
private var mUsePortIndex: Boolean = false
|
|
||||||
private var mPortIndex: Int = -1
|
|
||||||
private var mSubscriptionId: Int = -1
|
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
|
||||||
super.onCreate(savedInstanceState)
|
|
||||||
|
|
||||||
euiccManager = getSystemService(EuiccManager::class.java) as EuiccManager
|
|
||||||
subscriptionManager = getSystemService(SubscriptionManager::class.java) as SubscriptionManager
|
|
||||||
telephonyManager = getSystemService(TelephonyManager::class.java) as TelephonyManager
|
|
||||||
|
|
||||||
val callingPackage = intent.getStringExtra("android.service.euicc.extra.RESOLUTION_CALLING_PACKAGE")
|
|
||||||
if (TextUtils.isEmpty(callingPackage)) {
|
|
||||||
Log.e("UserConsentActivity", "Unknown calling package in resolution activity")
|
|
||||||
sendResultAndFinish(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
val extras = intent.extras
|
|
||||||
if (extras != null) {
|
|
||||||
mCardId = extras.getInt("android.service.euicc.extra.RESOLUTION_CARD_ID", -1)
|
|
||||||
mUsePortIndex = extras.getBoolean("android.service.euicc.extra.RESOLUTION_USE_PORT_INDEX", false)
|
|
||||||
mPortIndex = extras.getInt("android.service.euicc.extra.RESOLUTION_PORT_INDEX", -1)
|
|
||||||
mSubscriptionId = extras.getInt("android.service.euicc.extra.RESOLUTION_SUBSCRIPTION_ID", -1)
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
val packageManager = packageManager
|
|
||||||
val appLabel = packageManager.getApplicationLabel(packageManager.getApplicationInfo(callingPackage!!, 0))
|
|
||||||
if (TextUtils.isEmpty(appLabel)) {
|
|
||||||
Log.e("UserConsentActivity", "Calling app doesn't have a label")
|
|
||||||
sendResultAndFinish(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
} catch (e: PackageManager.NameNotFoundException) {
|
|
||||||
Log.e("UserConsentActivity", "Calling package doesn't exist", e)
|
|
||||||
sendResultAndFinish(false)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
showUserConsentDialog()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun sendResultAndFinish(consent: Boolean) {
|
|
||||||
val bundle = Bundle()
|
|
||||||
|
|
||||||
bundle.putBoolean("android.service.euicc.extra.RESOLUTION_CONSENT", consent)
|
|
||||||
if (mUsePortIndex) {
|
|
||||||
bundle.putInt("android.service.euicc.extra.RESOLUTION_PORT_INDEX", mPortIndex);
|
|
||||||
}
|
|
||||||
|
|
||||||
euiccManager.createForCardId(mCardId)
|
|
||||||
euiccManager.continueOperation(intent, bundle)
|
|
||||||
|
|
||||||
setResult(if (consent) Activity.RESULT_OK else Activity.RESULT_CANCELED)
|
|
||||||
finish()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun showUserConsentDialog() {
|
|
||||||
AlertDialog.Builder(this)
|
|
||||||
.setTitle(getString(R.string.user_consent_title))
|
|
||||||
.setMessage(getString(R.string.user_consent_message))
|
|
||||||
.setPositiveButton(getString(R.string.user_consent_positive)) { _, _ ->
|
|
||||||
sendResultAndFinish(true)
|
|
||||||
}
|
|
||||||
.setNegativeButton(getString(R.string.user_consent_negative)) { _, _ ->
|
|
||||||
sendResultAndFinish(false)
|
|
||||||
}
|
|
||||||
.setOnCancelListener {
|
|
||||||
sendResultAndFinish(false)
|
|
||||||
}
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -22,9 +22,4 @@
|
||||||
<string name="lui_desc">Your device supports eSIMs. To connect to mobile network, download your eSIM issued by a carrier, or insert a physical SIM.</string>
|
<string name="lui_desc">Your device supports eSIMs. To connect to mobile network, download your eSIM issued by a carrier, or insert a physical SIM.</string>
|
||||||
<string name="lui_skip">Skip</string>
|
<string name="lui_skip">Skip</string>
|
||||||
<string name="lui_download">Download eSIM</string>
|
<string name="lui_download">Download eSIM</string>
|
||||||
|
|
||||||
<string name="user_consent_title">Allow your carrier to download SIM?</string>
|
|
||||||
<string name="user_consent_message">Your SIM will be used immediately after download</string>
|
|
||||||
<string name="user_consent_negative">No thanks</string>
|
|
||||||
<string name="user_consent_positive">Yes</string>
|
|
||||||
</resources>
|
</resources>
|
|
@ -1,13 +0,0 @@
|
||||||
<resources>
|
|
||||||
<style name="Theme.UserConsentDialog" parent="@android:style/Theme.DeviceDefault.DayNight">
|
|
||||||
<item name="android:windowBackground">@android:color/transparent</item>
|
|
||||||
<item name="android:windowNoTitle">true</item>
|
|
||||||
<item name="android:windowIsFloating">true</item>
|
|
||||||
<item name="android:windowIsTranslucent">true</item>
|
|
||||||
<item name="android:windowContentOverlay">@null</item>
|
|
||||||
<item name="android:backgroundDimEnabled">true</item>
|
|
||||||
<item name="android:windowActionBar">false</item>
|
|
||||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
|
||||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
|
||||||
</style>
|
|
||||||
</resources>
|
|
|
@ -305,45 +305,12 @@ public abstract class EuiccService extends Service {
|
||||||
* defined in {@code RESOLVABLE_ERROR_}. A subclass should override this method. Otherwise,
|
* defined in {@code RESOLVABLE_ERROR_}. A subclass should override this method. Otherwise,
|
||||||
* this method does nothing and returns null by default.
|
* this method does nothing and returns null by default.
|
||||||
* @see android.telephony.euicc.EuiccManager#downloadSubscription
|
* @see android.telephony.euicc.EuiccManager#downloadSubscription
|
||||||
* @deprecated prefer {@link #onDownloadSubscription(int, int,
|
|
||||||
* DownloadableSubscription, boolean, boolean, Bundle)}
|
|
||||||
*/
|
*/
|
||||||
@Deprecated
|
|
||||||
public DownloadSubscriptionResult onDownloadSubscription(int slotId,
|
public DownloadSubscriptionResult onDownloadSubscription(int slotId,
|
||||||
@NonNull DownloadableSubscription subscription, boolean switchAfterDownload,
|
DownloadableSubscription subscription, boolean switchAfterDownload,
|
||||||
boolean forceDeactivateSim, @Nullable Bundle resolvedBundle) {
|
boolean forceDeactivateSim, Bundle resolvedBundle) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
/**
|
|
||||||
* Download the given subscription.
|
|
||||||
*
|
|
||||||
* @param slotIndex Index of the SIM slot to use for the operation.
|
|
||||||
* @param portIndex Index of the port from the slot. portIndex is used when
|
|
||||||
* switchAfterDownload is set to {@code true}, otherwise download is port agnostic.
|
|
||||||
* @param subscription The subscription to download.
|
|
||||||
* @param switchAfterDownload If true, the subscription should be enabled upon successful
|
|
||||||
* download.
|
|
||||||
* @param forceDeactivateSim If true, and if an active SIM must be deactivated to access the
|
|
||||||
* eUICC, perform this action automatically. Otherwise, {@link #RESULT_MUST_DEACTIVATE_SIM}
|
|
||||||
* should be returned to allow the user to consent to this operation first.
|
|
||||||
* @param resolvedBundle The bundle containing information on resolved errors. It can contain
|
|
||||||
* a string of confirmation code for the key {@link #EXTRA_RESOLUTION_CONFIRMATION_CODE},
|
|
||||||
* and a boolean for key {@link #EXTRA_RESOLUTION_ALLOW_POLICY_RULES} indicating whether
|
|
||||||
* the user allows profile policy rules or not.
|
|
||||||
* @return a DownloadSubscriptionResult instance including a result code, a resolvable errors
|
|
||||||
* bit map, and original the card Id. The result code may be one of the predefined
|
|
||||||
* {@code RESULT_} constants or any implementation-specific code starting with
|
|
||||||
* {@link #RESULT_FIRST_USER}. The resolvable error bit map can be either 0 or values
|
|
||||||
* defined in {@code RESOLVABLE_ERROR_}.
|
|
||||||
* @see android.telephony.euicc.EuiccManager#downloadSubscription
|
|
||||||
*/
|
|
||||||
@NonNull
|
|
||||||
public DownloadSubscriptionResult onDownloadSubscription(int slotIndex, int portIndex,
|
|
||||||
@NonNull DownloadableSubscription subscription, boolean switchAfterDownload,
|
|
||||||
boolean forceDeactivateSim, @NonNull Bundle resolvedBundle) {
|
|
||||||
// stub implementation, LPA needs to implement this
|
|
||||||
throw new UnsupportedOperationException("LPA must override onDownloadSubscription");
|
|
||||||
}
|
|
||||||
/**
|
/**
|
||||||
* Download the given subscription.
|
* Download the given subscription.
|
||||||
*
|
*
|
||||||
|
@ -362,8 +329,8 @@ public abstract class EuiccService extends Service {
|
||||||
* {@link #onDownloadSubscription(int, DownloadableSubscription, boolean, boolean, Bundle)}. The
|
* {@link #onDownloadSubscription(int, DownloadableSubscription, boolean, boolean, Bundle)}. The
|
||||||
* default return value for this one is Integer.MIN_VALUE.
|
* default return value for this one is Integer.MIN_VALUE.
|
||||||
*/
|
*/
|
||||||
@Deprecated public @Result int onDownloadSubscription(int slotId,
|
@Deprecated public int onDownloadSubscription(int slotId,
|
||||||
@NonNull DownloadableSubscription subscription, boolean switchAfterDownload,
|
DownloadableSubscription subscription, boolean switchAfterDownload,
|
||||||
boolean forceDeactivateSim) {
|
boolean forceDeactivateSim) {
|
||||||
return Integer.MIN_VALUE;
|
return Integer.MIN_VALUE;
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,7 +37,7 @@ interface LocalProfileAssistant {
|
||||||
fun deleteProfile(iccid: String): Boolean
|
fun deleteProfile(iccid: String): Boolean
|
||||||
|
|
||||||
fun downloadProfile(smdp: String, matchingId: String?, imei: String?,
|
fun downloadProfile(smdp: String, matchingId: String?, imei: String?,
|
||||||
confirmationCode: String?, preview: Boolean, callback: ProfileDownloadCallback)
|
confirmationCode: String?, callback: ProfileDownloadCallback)
|
||||||
|
|
||||||
fun deleteNotification(seqNumber: Long): Boolean
|
fun deleteNotification(seqNumber: Long): Boolean
|
||||||
fun handleNotification(seqNumber: Long): Boolean
|
fun handleNotification(seqNumber: Long): Boolean
|
||||||
|
|
|
@ -28,7 +28,7 @@ internal object LpacJni {
|
||||||
// es9p + es10b
|
// es9p + es10b
|
||||||
// We do not expose all of the functions because of tediousness :)
|
// We do not expose all of the functions because of tediousness :)
|
||||||
external fun downloadProfile(handle: Long, smdp: String, matchingId: String?, imei: String?,
|
external fun downloadProfile(handle: Long, smdp: String, matchingId: String?, imei: String?,
|
||||||
confirmationCode: String?, preview: Boolean, callback: ProfileDownloadCallback): Int
|
confirmationCode: String?, callback: ProfileDownloadCallback): Int
|
||||||
external fun downloadErrCodeToString(code: Int): String
|
external fun downloadErrCodeToString(code: Int): String
|
||||||
external fun handleNotification(handle: Long, seqNumber: Long): Int
|
external fun handleNotification(handle: Long, seqNumber: Long): Int
|
||||||
// Cancel any ongoing es9p and/or es10b sessions
|
// Cancel any ongoing es9p and/or es10b sessions
|
||||||
|
|
|
@ -21,6 +21,5 @@ interface ProfileDownloadCallback {
|
||||||
Finalizing(80), // load bpp
|
Finalizing(80), // load bpp
|
||||||
}
|
}
|
||||||
|
|
||||||
fun onMetadataReceived(profileName: String, iccid: String, appCerts: String)
|
|
||||||
fun onStateUpdate(state: DownloadState)
|
fun onStateUpdate(state: DownloadState)
|
||||||
}
|
}
|
|
@ -202,14 +202,13 @@ class LocalProfileAssistantImpl(
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
override fun downloadProfile(smdp: String, matchingId: String?, imei: String?,
|
override fun downloadProfile(smdp: String, matchingId: String?, imei: String?,
|
||||||
confirmationCode: String?, preview: Boolean, callback: ProfileDownloadCallback) {
|
confirmationCode: String?, callback: ProfileDownloadCallback) {
|
||||||
val res = LpacJni.downloadProfile(
|
val res = LpacJni.downloadProfile(
|
||||||
contextHandle,
|
contextHandle,
|
||||||
smdp,
|
smdp,
|
||||||
matchingId,
|
matchingId,
|
||||||
imei,
|
imei,
|
||||||
confirmationCode,
|
confirmationCode,
|
||||||
preview,
|
|
||||||
callback
|
callback
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
#include <euicc/es8p.h>
|
|
||||||
#include <euicc/es9p.h>
|
#include <euicc/es9p.h>
|
||||||
#include <euicc/es10b.h>
|
#include <euicc/es10b.h>
|
||||||
#include <euicc/hexutil.h>
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
#include <syslog.h>
|
#include <syslog.h>
|
||||||
|
@ -62,7 +60,7 @@ JNIEXPORT jint JNICALL
|
||||||
Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, jlong handle,
|
Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, jlong handle,
|
||||||
jstring smdp, jstring matching_id,
|
jstring smdp, jstring matching_id,
|
||||||
jstring imei, jstring confirmation_code,
|
jstring imei, jstring confirmation_code,
|
||||||
jboolean preview, jobject callback) {
|
jobject callback) {
|
||||||
struct euicc_ctx *ctx = (struct euicc_ctx *) handle;
|
struct euicc_ctx *ctx = (struct euicc_ctx *) handle;
|
||||||
struct es10b_load_bound_profile_package_result es10b_load_bound_profile_package_result;
|
struct es10b_load_bound_profile_package_result es10b_load_bound_profile_package_result;
|
||||||
const char *_confirmation_code = NULL;
|
const char *_confirmation_code = NULL;
|
||||||
|
@ -111,63 +109,6 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, j
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ctx->http._internal.prepare_download_param->b64_profileMetadata) {
|
|
||||||
struct es8p_metadata *profile_metadata = NULL;
|
|
||||||
ret = es8p_metadata_parse(&profile_metadata, ctx->http._internal.prepare_download_param->b64_profileMetadata);
|
|
||||||
|
|
||||||
if (ret == 0) {
|
|
||||||
const char *profile_name = profile_metadata->serviceProviderName ? profile_metadata->serviceProviderName : "Unknown";
|
|
||||||
const char *iccid = (profile_metadata->iccid[0] != '\0') ? profile_metadata->iccid : "Unknown";
|
|
||||||
|
|
||||||
size_t totalLength = 0;
|
|
||||||
for (int i = 0; i < profile_metadata->numRefArDo; i++) {
|
|
||||||
if (profile_metadata->refArDoList[i].deviceAppIdRefDo) {
|
|
||||||
totalLength += (profile_metadata->refArDoList[i].deviceAppIdLength * 2) + 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
char *certSignatures = malloc(totalLength + 1);
|
|
||||||
if (certSignatures) {
|
|
||||||
certSignatures[0] = '\0';
|
|
||||||
for (int i = 0; i < profile_metadata->numRefArDo; i++) {
|
|
||||||
if (profile_metadata->refArDoList[i].deviceAppIdRefDo) {
|
|
||||||
char deviceAppIdHex[profile_metadata->refArDoList[i].deviceAppIdLength * 2 + 1];
|
|
||||||
euicc_hexutil_bin2hex(deviceAppIdHex, sizeof(deviceAppIdHex),
|
|
||||||
profile_metadata->refArDoList[i].deviceAppIdRefDo,
|
|
||||||
profile_metadata->refArDoList[i].deviceAppIdLength);
|
|
||||||
strcat(certSignatures, deviceAppIdHex);
|
|
||||||
strcat(certSignatures, ",");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (strlen(certSignatures) > 0 && certSignatures[strlen(certSignatures) - 1] == ',') {
|
|
||||||
certSignatures[strlen(certSignatures) - 1] = '\0';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
jclass callbackClass = (*env)->GetObjectClass(env, callback);
|
|
||||||
jmethodID onMetadataReceived = (*env)->GetMethodID(env, callbackClass, "onMetadataReceived",
|
|
||||||
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
|
|
||||||
|
|
||||||
if (onMetadataReceived) {
|
|
||||||
jstring jProfileName = (*env)->NewStringUTF(env, profile_name);
|
|
||||||
jstring jIccid = (*env)->NewStringUTF(env, iccid);
|
|
||||||
jstring jCertSignatures = (*env)->NewStringUTF(env, certSignatures ? certSignatures : "");
|
|
||||||
|
|
||||||
(*env)->CallVoidMethod(env, callback, onMetadataReceived, jProfileName, jIccid, jCertSignatures);
|
|
||||||
|
|
||||||
(*env)->DeleteLocalRef(env, jProfileName);
|
|
||||||
(*env)->DeleteLocalRef(env, jIccid);
|
|
||||||
(*env)->DeleteLocalRef(env, jCertSignatures);
|
|
||||||
}
|
|
||||||
|
|
||||||
es8p_metadata_free(&profile_metadata);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (preview) {
|
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
(*env)->CallVoidMethod(env, callback, on_state_update, download_state_downloading);
|
(*env)->CallVoidMethod(env, callback, on_state_update, download_state_downloading);
|
||||||
ret = es10b_prepare_download(ctx, _confirmation_code);
|
ret = es10b_prepare_download(ctx, _confirmation_code);
|
||||||
syslog(LOG_INFO, "es10b_prepare_download %d", ret);
|
syslog(LOG_INFO, "es10b_prepare_download %d", ret);
|
||||||
|
@ -237,4 +178,4 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadErrCodeToString(JNIEnv *env, jobject
|
||||||
default:
|
default:
|
||||||
return toJString(env, "ES10B_ERROR_REASON_UNDEFINED");
|
return toJString(env, "ES10B_ERROR_REASON_UNDEFINED");
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue