In Android 9, profile management APIs (public and
@SystemApi) are available through the class EuiccManager
. eUICC communication
APIs (@SystemApi only) are available through the class EuiccCardManager
.
About eUICC
Carriers can make carrier apps using EuiccManager to manage profiles, as shown in Figure 1. Carrier apps don't need to be system apps but need to have carrier privileges granted by eUICC profiles. An LPA app (LUI and LPA backend) needs to be a system app (i.e., included in the system image) to call the @SystemApi.
Figure 1. Android phones with carrier app and OEM LPA
Besides the logic of calling EuiccCardManager
and talking to eUICC, LPA apps
must implement the following:
- SM-DP+ client talking to SM-DP+ server to authenticate and download profiles
- [Optional] SM-DS to get more potential downloadable profiles
- Notification handling to send notifications to the server to update the profile state
- [Optional] Slots management including switching between eSIM and pSIM logic. This is optional if the phone only has an eSIM chip.
- eSIM OTA
Although more than one LPA app can be present in an Android phone, only one LPA
can be selected to be the actual working LPA based on the priority defined in
the AndroidManifest.xml
file of each app.
Use EuiccManager
The LPA APIs are public through EuiccManager
(under package
android.telephony.euicc
). A carrier app can get the instance of EuiccManager
,
and call the methods in EuiccManager
to get the eUICC information and manage
subscriptions (referred to as profiles in GSMA RSP documents) as
SubscriptionInfo instances.
To call public APIs including download, switch, and delete subscription operations, the carrier app must have the required privileges. Carrier privileges are added by the mobile carrier in the profile metadata. The eUICC API enforces the carrier privilege rules accordingly.
The Android platform does not handle the profile policy rules. If a policy rule is declared in the profile metadata, the LPA can choose how to handle the profile download and installation procedure. For example, it is possible for a third-party OEM LPA to handle policy rules using a special error code (the error code is passed from the OEM LPA to the platform, then the platform passes the code to the OEM LUI).
For information on multiple enabled profiles APIs, see Multiple enabled profiles.
APIs
The following APIs can be found in the
EuiccManager
reference documentation
and
EuiccManager.java
.
Get instance (public)
Gets the instance of EuiccManager
through Context#getSystemService
.
For details, see
getSystemService
.
EuiccManager mgr = (EuiccManager) context.getSystemService(Context.EUICC_SERVICE);
Check enabled (public)
Checks whether the embedded subscription is enabled. This should be checked
before accessing LPA APIs. For details, see
isEnabled
.
boolean isEnabled = mgr.isEnabled();
if (!isEnabled) {
return;
}
Get EID (public)
Gets the EID identifying the eUICC hardware. This may be null if the eUICC is
not ready. The caller must have carrier privilege or the
READ_PRIVILEGED_PHONE_STATE
permission. For details, see
getEid
.
String eid = mgr.getEid();
if (eid == null) {
// Handle null case.
}
Get EuiccInfo (public)
Gets information about the eUICC. This contains the OS version. For details,
see
getEuiccInfo
.
EuiccInfo info = mgr.getEuiccInfo();
String osVer = info.getOsVersion();
Download subscription (public)
Downloads the given subscription (referred to as "profile" in GSMA RSP documents). The subscription can be created from an activation code. For example, an activation code can be parsed from a QR code. Downloading a subscription is an asynchronous operation.
The caller must either have the WRITE_EMBEDDED_SUBSCRIPTIONS
permission or
have carrier privileges for the target subscription. For details, see
downloadSubscription
.
// Register receiver.
String action = "download_subscription";
BroadcastReceiver receiver =
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (!action.equals(intent.getAction())) {
return;
}
resultCode = getResultCode();
detailedCode = intent.getIntExtra(
EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE,
0 /* defaultValue*/);
resultIntent = intent;
}
};
context.registerReceiver(
receiver,
new IntentFilter(action),
"example.broadcast.permission" /* broadcastPermission*/, null /* handler */);
// Download subscription asynchronously.
DownloadableSubscription sub =
DownloadableSubscription.forActivationCode(code /* encodedActivationCode*/);
Intent intent = new Intent(action);
PendingIntent callbackIntent = PendingIntent.getBroadcast(
getContext(), 0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT);
mgr.downloadSubscription(sub, true /* switchAfterDownload */, callbackIntent);
Switch subscription (public)
Switches to (enables) the given subscription. The caller must either have
WRITE_EMBEDDED_SUBSCRIPTIONS
or have carrier privileges for the current
enabled subscription and the target subscription. For details, see
switchToSubscription
.
// Register receiver.
String action = "switch_to_subscription";
BroadcastReceiver receiver =
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (!action.equals(intent.getAction())) {
return;
}
resultCode = getResultCode();
detailedCode = intent.getIntExtra(
EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 0 /* defaultValue*/);
resultIntent = intent;
}
};
context.registerReceiver(receiver, new IntentFilter(action),
"example.broadcast.permission" /* broadcastPermission*/, null /* handler */);
// Switch to a subscription asynchronously.
Intent intent = new Intent(action);
PendingIntent callbackIntent = PendingIntent.getBroadcast(
getContext(), 0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT);
mgr.switchToSubscription(1 /* subscriptionId */, callbackIntent);
Switch subscription with port (public)
(Available from Android 13) Switches to (enables)
the given subscription with the port index specified.
The caller must either have WRITE_EMBEDDED_SUBSCRIPTIONS
or have carrier
privileges for the current enabled subscription and the target subscription.
For details, see
switchToSubscription
.
// Register receiver.
String action = "switch_to_subscription";
BroadcastReceiver receiver =
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (!action.equals(intent.getAction())) {
return;
}
resultCode = getResultCode();
detailedCode = intent.getIntExtra(
EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE, 0 /* defaultValue*/);
resultIntent = intent;
}
};
context.registerReceiver(receiver, new IntentFilter(action),
"example.broadcast.permission" /* broadcastPermission*/, null /* handler */);
// Switch to a subscription asynchronously.
Intent intent = new Intent(action);
PendingIntent callbackIntent = PendingIntent.getBroadcast(
getContext(), 0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT);
mgr.switchToSubscription(1 /* subscriptionId */, 0 /*portIndex*/, callbackIntent);
Is SIM port available (public)
public boolean isSimPortAvailable(int portIndex)
(Available from Android 13) Returns whether the
passing port index is available. A port is available if it
has no subscription enabled or the calling app has carrier privilege over the
subscription installed on the selected port. For details, see
isSimPortAvailable
.
Delete subscription (public)
Deletes a subscription with a subscription ID. If the subscription is currently
active, it is first disabled. The caller must have either
WRITE_EMBEDDED_SUBSCRIPTIONS
or carrier privileges for the target
subscription. For details, see
deleteSubscription
.
// Register receiver.
String action = "delete_subscription";
BroadcastReceiver receiver =
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (!action.equals(intent.getAction())) {
return;
}
resultCode = getResultCode();
detailedCode = intent.getIntExtra(
EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE,
0 /* defaultValue*/);
resultIntent = intent;
}
};
context.registerReceiver(receiver, new IntentFilter(action),
"example.broadcast.permission" /* broadcastPermission*/,
null /* handler */);
// Delete a subscription asynchronously.
Intent intent = new Intent(action);
PendingIntent callbackIntent = PendingIntent.getBroadcast(
getContext(), 0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT);
mgr.deleteSubscription(1 /* subscriptionId */, callbackIntent);
Erase all subscriptions (system API)
Erases all subscriptions on a device. Starting in Android
11, you should provide an EuiccCardManager#ResetOption
enum value to specify whether to erase all test, operational, or both types of
subscriptions. The caller must have WRITE_EMBEDDED_SUBSCRIPTIONS
permission.
// Register receiver.
String action = "delete_subscription";
BroadcastReceiver receiver =
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (!action.equals(intent.getAction())) {
return;
}
resultCode = getResultCode();
detailedCode = intent.getIntExtra(
EuiccManager.EXTRA_EMBEDDED_SUBSCRIPTION_DETAILED_CODE,
0 /* defaultValue*/);
resultIntent = intent;
}
};
context.registerReceiver(receiver, new IntentFilter(action),
"example.broadcast.permission" /* broadcastPermission*/,
null /* handler */);
// Erase all operational subscriptions asynchronously.
Intent intent = new Intent(action);
PendingIntent callbackIntent = PendingIntent.getBroadcast(
getContext(), 0 /* requestCode */, intent, PendingIntent.FLAG_UPDATE_CURRENT);
mgr.eraseSubscriptions(
EuiccCardManager.RESET_OPTION_DELETE_OPERATIONAL_PROFILES, callbackIntent);
Start resolution activity (public)
Starts an activity to resolve a user-resolvable error. If an operation returns
EuiccManager#EMBEDDED_SUBSCRIPTION_RESULT_RESOLVABLE_ERROR
, this method can be
called to prompt the user to resolve the issue. This method can only be called
once for a particular error.
...
mgr.startResolutionActivity(getActivity(), 0 /* requestCode */, resultIntent, callbackIntent);
Constants
To see a list of the public
constants in EuiccManager
, see
Constants.