Реализация переключения eSIM-профилей через мобильное приложение
Переключение между установленными eSIM-профилями проще, чем активация — профиль уже есть на eUICC, нужно только сменить активный. Но и здесь платформы добавляют сложностей: iOS требует пользовательского подтверждения через системный диалог, Android на некоторых устройствах перезагружает радиомодуль, что занимает 10–30 секунд с потерей связи.
Android: EuiccManager.switchToSubscription
fun switchToProfile(subscriptionId: Int) {
// subscriptionId получаем из SubscriptionManager
val activeSubInfo = subscriptionManager.activeSubscriptionInfoList
val currentEsimId = activeSubInfo?.firstOrNull { it.isEmbedded }?.subscriptionId
if (currentEsimId == subscriptionId) {
showMessage("Этот профиль уже активен")
return
}
euiccManager.switchToSubscription(
subscriptionId,
PendingIntent.getBroadcast(
context,
REQUEST_CODE_SWITCH,
Intent(ACTION_ESIM_SWITCH_COMPLETE),
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
)
}
// BroadcastReceiver для обработки результата
private val switchReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
val resultCode = intent.getIntExtra(EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 0)
if (resultCode == EuiccManager.EMBEDDED_SUBSCRIPTION_RESULT_OK) {
onSwitchSuccess()
} else {
onSwitchError(resultCode)
}
}
}
switchToSubscription не принимает callback напрямую — только PendingIntent. Это не баг: операция асинхронна и может требовать пользовательского взаимодействия. Результат приходит в BroadcastReceiver.
Переключение без системного UI (privileged)
На Samsung и некоторых других производителях доступно через android.telephony.euicc.EuiccManager.switchToSubscription(int, boolean) с флагом forceDeactivateSim = true. Работает только для приложений с WRITE_EMBEDDED_SUBSCRIPTIONS.
// Samsung-специфичный флаг для переключения без диалога
if (Build.MANUFACTURER.equals("samsung", ignoreCase = true)) {
euiccManager.switchToSubscription(
subscriptionId,
forceDeactivateSim = true, // Не спрашивать пользователя
callbackIntent = pendingIntent
)
}
Важно: на некоторых Qualcomm-девайсах после switchToSubscription радиомодуль перезагружается без предупреждения. Предупреждать пользователя заранее: «После переключения на 15–30 секунд пропадёт связь».
iOS: только через Settings или carrier entitlement
На iOS программное переключение между eSIM-профилями недоступно для обычных приложений. Вариантов два:
Ссылка в Settings: UIApplication.shared.open(URL(string: "App-Prefs:root=Cellular")!) — открывает раздел «Сотовая связь» в настройках, где пользователь сам переключает. Не лучший UX, но единственный путь без entitlement.
Carrier entitlement + CTSubscriptionManager: доступно только для операторских приложений с com.apple.developer.CoreTelephony.Subscriber entitlement. Предоставляется Apple только лицензированным операторам:
import CoreTelephony
let subscriptionManager = CTSubscriptionManager()
// Получение списка профилей
let subscriptions = subscriptionManager.subscriptions
// subscriptions: [String: CTSubscriber] — key это subscriber ID
// Для переключения — только через системный UI или оператора
// Прямого API для switch без entitlement нет
Получение списка установленных профилей
Android:
fun getInstalledEsimProfiles(): List<SubscriptionInfo> {
val allSubs = subscriptionManager.availableSubscriptionInfoList ?: emptyList()
return allSubs.filter { it.isEmbedded }
.map { sub ->
// sub.displayName — имя оператора
// sub.simSlotIndex: -1 если профиль inactive, >= 0 если active
// sub.iccId — уникальный идентификатор профиля
sub
}
}
simSlotIndex == -1 для inactive профилей — важно для отображения статуса в списке.
iOS (без entitlement):
import CoreTelephony
func getActiveCarriers() -> [CTCarrier] {
let networkInfo = CTTelephonyNetworkInfo()
return networkInfo.serviceSubscriberCellularProviders?.values
.compactMap { $0 }
.filter { $0.carrierName != nil }
?? []
}
Только активные профили. Список всех установленных (включая неактивные) — недоступен без carrier entitlement.
UX переключения
Время переключения: Android — 10–30 секунд (radio reboot на Qualcomm, быстрее на MTK). iOS через Settings — не контролируемо. Показывать прогресс-индикатор с предупреждением о временной потере связи.
После успешного переключения проверять новый активный профиль через SubscriptionManager.getActiveSubscriptionInfo(newSubscriptionId) — убедиться что переключение завершено перед обновлением UI.
Сроки
Android: переключение между профилями с BroadcastReceiver и обработкой ошибок — 1–2 недели. iOS: deeplink в Settings + отображение текущих профилей — 3–5 дней. Carrier-grade решение для обеих платформ с полным контролем через entitlement: 1–2 месяца.







