forked from PeterCxy/OpenEUICC
Compare commits
1 commit
master
...
carrier-ap
Author | SHA1 | Date | |
---|---|---|---|
571a5be347 |
15 changed files with 414 additions and 13 deletions
|
@ -45,8 +45,9 @@ class LocalProfileAssistantWrapper(orig: LocalProfileAssistant) :
|
|||
matchingId: String?,
|
||||
imei: String?,
|
||||
confirmationCode: String?,
|
||||
preview: Boolean,
|
||||
callback: ProfileDownloadCallback
|
||||
) = lpa.downloadProfile(smdp, matchingId, imei, confirmationCode, callback)
|
||||
) = lpa.downloadProfile(smdp, matchingId, imei, confirmationCode, preview, callback)
|
||||
|
||||
override fun deleteNotification(seqNumber: Long): Boolean = lpa.deleteNotification(seqNumber)
|
||||
|
||||
|
|
|
@ -373,6 +373,7 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
|
|||
smdp: String,
|
||||
matchingId: String?,
|
||||
confirmationCode: String?,
|
||||
preview: Boolean,
|
||||
imei: String?
|
||||
): ForegroundTaskSubscriberFlow =
|
||||
launchForegroundTask(
|
||||
|
@ -387,7 +388,10 @@ class EuiccChannelManagerService : LifecycleService(), OpenEuiccContextMarker {
|
|||
matchingId,
|
||||
imei,
|
||||
confirmationCode,
|
||||
preview,
|
||||
object : ProfileDownloadCallback {
|
||||
override fun onMetadataReceived(profileName: String, iccid: String, appCerts: String) {
|
||||
}
|
||||
override fun onStateUpdate(state: ProfileDownloadCallback.DownloadState) {
|
||||
if (state.progress == 0) return
|
||||
foregroundTaskState.value =
|
||||
|
|
|
@ -29,6 +29,7 @@ class DownloadWizardActivity: BaseEuiccAccessActivity() {
|
|||
var smdp: String,
|
||||
var matchingId: String?,
|
||||
var confirmationCode: String?,
|
||||
var preview: Boolean,
|
||||
var imei: String?,
|
||||
var downloadStarted: Boolean,
|
||||
var downloadTaskID: Long,
|
||||
|
@ -66,6 +67,7 @@ class DownloadWizardActivity: BaseEuiccAccessActivity() {
|
|||
"",
|
||||
null,
|
||||
null,
|
||||
false,
|
||||
null,
|
||||
false,
|
||||
-1,
|
||||
|
|
|
@ -160,6 +160,7 @@ class DownloadWizardProgressFragment : DownloadWizardActivity.DownloadWizardStep
|
|||
state.smdp,
|
||||
state.matchingId,
|
||||
state.confirmationCode,
|
||||
state.preview,
|
||||
state.imei
|
||||
)
|
||||
|
||||
|
|
|
@ -53,6 +53,18 @@
|
|||
</intent-filter>
|
||||
</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
|
||||
android:name="androidx.appcompat.app.AppLocalesMetadataHolderService"
|
||||
android:enabled="false"
|
||||
|
|
|
@ -3,7 +3,9 @@ package im.angry.openeuicc.service
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.service.euicc.*
|
||||
import android.telephony.UiccAccessRule
|
||||
import android.telephony.UiccSlotMapping
|
||||
import android.telephony.euicc.DownloadableSubscription
|
||||
import android.telephony.euicc.EuiccInfo
|
||||
|
@ -16,6 +18,7 @@ import kotlinx.coroutines.delay
|
|||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import kotlin.IllegalStateException
|
||||
import net.typeblog.lpac_jni.ProfileDownloadCallback
|
||||
|
||||
class OpenEuiccService : EuiccService(), OpenEuiccContextMarker {
|
||||
companion object {
|
||||
|
@ -147,14 +150,186 @@ class OpenEuiccService : EuiccService(), OpenEuiccContextMarker {
|
|||
// 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(
|
||||
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)
|
||||
Log.i(TAG, "onGetDownloadableSubscriptionMetadata slotId=$slotId")
|
||||
|
||||
if (subscription == null) {
|
||||
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(
|
||||
|
|
|
@ -0,0 +1,94 @@
|
|||
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,4 +22,9 @@
|
|||
<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_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>
|
13
app/src/main/res/values/themes.xml
Normal file
13
app/src/main/res/values/themes.xml
Normal file
|
@ -0,0 +1,13 @@
|
|||
<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,12 +305,45 @@ public abstract class EuiccService extends Service {
|
|||
* defined in {@code RESOLVABLE_ERROR_}. A subclass should override this method. Otherwise,
|
||||
* this method does nothing and returns null by default.
|
||||
* @see android.telephony.euicc.EuiccManager#downloadSubscription
|
||||
* @deprecated prefer {@link #onDownloadSubscription(int, int,
|
||||
* DownloadableSubscription, boolean, boolean, Bundle)}
|
||||
*/
|
||||
@Deprecated
|
||||
public DownloadSubscriptionResult onDownloadSubscription(int slotId,
|
||||
DownloadableSubscription subscription, boolean switchAfterDownload,
|
||||
boolean forceDeactivateSim, Bundle resolvedBundle) {
|
||||
@NonNull DownloadableSubscription subscription, boolean switchAfterDownload,
|
||||
boolean forceDeactivateSim, @Nullable Bundle resolvedBundle) {
|
||||
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.
|
||||
*
|
||||
|
@ -329,8 +362,8 @@ public abstract class EuiccService extends Service {
|
|||
* {@link #onDownloadSubscription(int, DownloadableSubscription, boolean, boolean, Bundle)}. The
|
||||
* default return value for this one is Integer.MIN_VALUE.
|
||||
*/
|
||||
@Deprecated public int onDownloadSubscription(int slotId,
|
||||
DownloadableSubscription subscription, boolean switchAfterDownload,
|
||||
@Deprecated public @Result int onDownloadSubscription(int slotId,
|
||||
@NonNull DownloadableSubscription subscription, boolean switchAfterDownload,
|
||||
boolean forceDeactivateSim) {
|
||||
return Integer.MIN_VALUE;
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ interface LocalProfileAssistant {
|
|||
fun deleteProfile(iccid: String): Boolean
|
||||
|
||||
fun downloadProfile(smdp: String, matchingId: String?, imei: String?,
|
||||
confirmationCode: String?, callback: ProfileDownloadCallback)
|
||||
confirmationCode: String?, preview: Boolean, callback: ProfileDownloadCallback)
|
||||
|
||||
fun deleteNotification(seqNumber: Long): Boolean
|
||||
fun handleNotification(seqNumber: Long): Boolean
|
||||
|
|
|
@ -28,7 +28,7 @@ internal object LpacJni {
|
|||
// es9p + es10b
|
||||
// We do not expose all of the functions because of tediousness :)
|
||||
external fun downloadProfile(handle: Long, smdp: String, matchingId: String?, imei: String?,
|
||||
confirmationCode: String?, callback: ProfileDownloadCallback): Int
|
||||
confirmationCode: String?, preview: Boolean, callback: ProfileDownloadCallback): Int
|
||||
external fun downloadErrCodeToString(code: Int): String
|
||||
external fun handleNotification(handle: Long, seqNumber: Long): Int
|
||||
// Cancel any ongoing es9p and/or es10b sessions
|
||||
|
|
|
@ -21,5 +21,6 @@ interface ProfileDownloadCallback {
|
|||
Finalizing(80), // load bpp
|
||||
}
|
||||
|
||||
fun onMetadataReceived(profileName: String, iccid: String, appCerts: String)
|
||||
fun onStateUpdate(state: DownloadState)
|
||||
}
|
|
@ -202,13 +202,14 @@ class LocalProfileAssistantImpl(
|
|||
|
||||
@Synchronized
|
||||
override fun downloadProfile(smdp: String, matchingId: String?, imei: String?,
|
||||
confirmationCode: String?, callback: ProfileDownloadCallback) {
|
||||
confirmationCode: String?, preview: Boolean, callback: ProfileDownloadCallback) {
|
||||
val res = LpacJni.downloadProfile(
|
||||
contextHandle,
|
||||
smdp,
|
||||
matchingId,
|
||||
imei,
|
||||
confirmationCode,
|
||||
preview,
|
||||
callback
|
||||
)
|
||||
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
#include <euicc/es8p.h>
|
||||
#include <euicc/es9p.h>
|
||||
#include <euicc/es10b.h>
|
||||
#include <euicc/hexutil.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <syslog.h>
|
||||
|
@ -60,7 +62,7 @@ JNIEXPORT jint JNICALL
|
|||
Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, jlong handle,
|
||||
jstring smdp, jstring matching_id,
|
||||
jstring imei, jstring confirmation_code,
|
||||
jobject callback) {
|
||||
jboolean preview, jobject callback) {
|
||||
struct euicc_ctx *ctx = (struct euicc_ctx *) handle;
|
||||
struct es10b_load_bound_profile_package_result es10b_load_bound_profile_package_result;
|
||||
const char *_confirmation_code = NULL;
|
||||
|
@ -109,6 +111,63 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadProfile(JNIEnv *env, jobject thiz, j
|
|||
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);
|
||||
ret = es10b_prepare_download(ctx, _confirmation_code);
|
||||
syslog(LOG_INFO, "es10b_prepare_download %d", ret);
|
||||
|
@ -178,4 +237,4 @@ Java_net_typeblog_lpac_1jni_LpacJni_downloadErrCodeToString(JNIEnv *env, jobject
|
|||
default:
|
||||
return toJString(env, "ES10B_ERROR_REASON_UNDEFINED");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue