eSIM Installation
There are several ways to install eSIM on your device.
Method | Description | iOS | Android |
---|---|---|---|
QR | Scan QR with | Supported | Supported |
Link | Click on link (web, browser, email) | Supported | NOT Supported |
App | Installation initiated from an APP | Supported (Via LINK) | Supported (Adittional configuration required) |
QR
The eSIM QR code is a crucial element for activating eSIM profiles on compatible devices. This QR code is provided in two key locations within our API:
-
Retrieve an Order: When you fetch the details of an order using the order retrieval endpoint, the response includes the QR code link for the associated eSIM.
-
Order Completed Webhook: Upon successful completion of an order, the Order Completed webhook notification also contains the QR code link.
In both cases, you can find the QR code link in the response structure under the qrcode
field.
Example: In the Order Completed event the QR Code link located in: Event --> Data --> Order --> _embedded --> _links --> qrcode
iOS
Starting from iOS 17.5 an eSIM profile can be installed via link.
The link should be prepared as follows:
https://esimsetup.apple.com/esim_qrcode_provisioning?carddata=LPA:1$rsp.truphone.com$MATCHING_ID
MATCHING_ID
can be obtained in Retrieve an Orde or Order Completed Webhook or from Subscription
Example of the Installation Link
Install eSIM from BetterRoaming
Android
To enable eSIM Profile installation and APN management, please contact 1Global with information of your application: Application Hash.
Application Hash
What capabilities can be provided to an App that are linked with eSIM profile using Application Hash?
- Install eSIM profile via Android APP
- Manage Profiles
- Get ICCID of the profile
This information should be delivered to 1GLOBAL during onboarding process.
Example:
Certificate: example
Application package name: ie com.COMPANY.COMPANY
Fingerprints:
sha1 - 52:F3:08:E2:.........
sha256 - 9C:9B:E0:71:........
Useful links:
- https://developer.android.com/studio/publish/app-signing
- https://source.android.com/docs/core/connect/uicc
- https://source.android.com/docs/core/connect/esim-overview#carrier-privileges
APN configuration
On Android platform APN should be configured by the application. This allows instant access without manual actions from the user.
Documentation: https://developer.android.com/reference/android/telephony/CarrierConfigManager
Flow:
- Get subscription list with subscription manager
- Check that ICCID is 1Global’s
- Verify that there is connectivity (if there is connectivity it will just update the APN, if not, it deletes and created new APN setting)
- Run insert APN setting
To get Carrier Privileges, an eSIM must be in enabled state on the device.
Android permission manifest example:
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.mod" />
<uses-permission
android:name="android.permission.WRITE_APN_SETTINGS"
tools:ignore="ProtectedPermissions"/>
APN settings values:
public static ContentValues getDefaultAPN() {
ContentValues values = new ContentValues();
values.put(Telephony.Carriers.NAME, "1GLOBAL");
values.put(Telephony.Carriers.APN, "truphone.com");
values.put("user_visible", "1");
values.put(Telephony.Carriers.MMSC, http://mmsc.truphone.com:1981/mm1);
values.put(Telephony.Carriers.MMSPORT, "1981");
values.put(Telephony.Carriers.NUMERIC, "20408");
values.put(Telephony.Carriers.MCC, "204");
values.put(Telephony.Carriers.MVNO_MATCH_DATA, "20408x");
values.put(Telephony.Carriers.TYPE, "default,supl,dun,mms");
values.put(Telephony.Carriers.MVNO_TYPE, "imsi");
return values;
}
ICCID’s from 1Global always start with:
ICCIDS = "894450,894447";
Helpful code snippets:
TelephonyManager tl = (TelephonyManager) getApplicationContext().getSystemService(Context.TELEPHONY_SERVICE);
SubscriptionManager subMangager = (SubscriptionManager) getApplicationContext().getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
int subId = -1;
for (SubscriptionInfo inf : subMangager.getActiveSubscriptionInfoList()) {
if (IccidChecker.isA1GlobalIccid(inf.getIccId())) {
subId = inf.getSubscriptionId();
break;
}
}
SetupApnDevice 1GlobalApnCreator = new SetupApnDevice(getApplicationContext(),subId);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && subId!=-1) {
tl=tl.createForSubscriptionId(subId);
}
try {
ConnectivityManager cm =
(ConnectivityManager)getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
boolean isConnected = activeNetwork != null &&
activeNetwork.isConnected();
1GlobalApnCreator.execute(tl.getSubscriberId().substring(0, 5),String.valueOf(isConnected));
}catch (Exception e){
ApnException.response();
}
SetupApnDevice class:
protected String doInBackground(String... params) {
String response;
this.numeric=params[0];
boolean isConnected= Boolean.parseBoolean(params[1]);
try {
1GlobalApnDevice 1GlobalApnDevice = new 1GlobalApnDevice(context);
1GlobalApnDevice.setNumeric(this.numeric);
1GlobalApnDevice.setSubId(this.subId);
if(isConnected) {
1GlobalApnDevice.execute();
}else{
1GlobalApnDevice.executeForce();
}
PreferredApnDevice preferredApnDevice = new PreferredApnDevice(context, 1GlobalApnDevice.getId());
preferredApnDevice.setNumeric(this.numeric);
preferredApnDevice.execute();
response = MessageDisplay.OK.toString();
}catch(Exception ex){
DeviceContent.getContent().setMessage(MessageDescription.CARRIER_PRIVILEGES.getKey());
response = MessageDisplay.NOK.toString();
}
return response;
}
PreferredApnDevice class
public class PreferredApnDevice implements Apn {
private Uri PREFERRED_APN_URI = Uri.parse("content://telephony/carriers/preferapn");
private ProviderAccess currentApnDevice;
private int id;
private String numeric;
public PreferredApnDevice(Context context, int id, int subId) {
this.id = id;
currentApnDevice = new CurrentApnDevice(Uri.withAppendedPath(PREFERRED_APN_URI, "subId/" + String.valueOf(subId)), context);
}
public PreferredApnDevice(Context context, int id) {
this.id = id;
currentApnDevice = new CurrentApnDevice(PREFERRED_APN_URI, context);
}
@Override
public void execute() {
currentApnDevice.update(getContentValues(),null,null);
}
@Override
public ContentValues getContentValues() {
ContentValues contentValues = new ContentValues();
contentValues.put(ApnUtils.APN_ID.getName(),String.valueOf(id));
return contentValues;
}
public void setNumeric(String numeric) {
this.numeric=numeric;
}
}
1GlobalApnDevice:
public class 1GlobalApnDevice implements Apn {
private CurrentApnDevice currentApnDevice;
private String numeric;
private int subId;
public 1GlobalApnDevice(Context context){
currentApnDevice = new CurrentApnDevice(Telephony.Carriers.CONTENT_URI, context);
}
public int getId() {
return currentApnDevice.getId();
}
public String getNumeric() {
return numeric;
}
public void setNumeric(String numeric) {
this.numeric = numeric;
}
@Override
public void execute() {
if(getId() == 0){
currentApnDevice.insert(getContentValues());
}else{
currentApnDevice.update(getContentValues(), currentApnDevice.getQuery(), currentApnDevice.getSelectionArgs());
}
}
@Override
public ContentValues getContentValues() {
ContentValues contentValues = new ContentValues();
contentValues.put(ApnUtils.APN_NAME.getName(), ApnUtils.APN_NAME.getValue());
contentValues.put(ApnUtils.APN.getName(), ApnUtils.APN.getValue());
contentValues.put(ApnUtils.APN_NUMERIC.getName(), numeric);
contentValues.put(ApnUtils.APN_MCC.getName(), numeric.substring(0,3));
contentValues.put(ApnUtils.APN_MNC.getName(), numeric.substring(3));
contentValues.put(ApnUtils.APN_MVNO_TYPE.getName(), ApnUtils.APN_MVNO_TYPE.getValue());
contentValues.put(ApnUtils.APN_CURRENT.getName(), ApnUtils.APN_CURRENT.getValue());
contentValues.put(ApnUtils.APN_MVNO_DATA.getName(), numeric + ApnUtils.APN_MVNO_DATA.getValue());
contentValues.put(ApnUtils.APN_TYPE.getName(), ApnUtils.APN_TYPE.getValue());
contentValues.put(ApnUtils.APN_MMSC.getName(), ApnUtils.APN_MMSC.getValue());
contentValues.put(ApnUtils.APN_MMSC_PORT.getName(), ApnUtils.APN_MMSC_PORT.getValue());
contentValues.put(ApnUtils.APN_SUBID.getName(),subId);
return contentValues;
}
public void setSubId(int subId) {
this.subId = subId;
}
public int getSubId() {
return subId;
}
public void executeForce() {
if(getId() == 0){
currentApnDevice.insert(getContentValues());
}else{
currentApnDevice.delete(currentApnDevice.getQuery(), currentApnDevice.getSelectionArgs());
currentApnDevice.insert(getContentValues());
}
}
}
CurretnApnDevice:
public class CurrentApnDevice extends ProviderAccess {
private ApnAttributes apnCreateClause;
private String[] selectionArgs;
private String query;
public CurrentApnDevice(Uri uri, Context context) {
super(uri, context.getContentResolver());
apnCreateClause = new ApnAttributes(ApnUtils.APN.getValue(), ApnUtils.APN_NAME.getValue());
selectionArgs = apnCreateClause.getSelectionArgs();
query = apnCreateClause.toString();
}
public void log(){
logAPN(query,selectionArgs);
}
public int getId() {
return select(query, selectionArgs);
}
public String[] getSelectionArgs() {
return selectionArgs;
}
public void setSelectionArgs(String[] selectionArgs) {
this.selectionArgs = selectionArgs;
}
public String getQuery() {
return query;
}
public void setQuery(String query) {
this.query = query;
}
}
providerAccess class
package com.1Global.1GlobalApp.contentresolver;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.util.Log;
public abstract class ProviderAccess {
protected Uri uri;
protected ContentResolver contentResolver;
public ProviderAccess(Uri uri, ContentResolver contentResolver){
this.uri = uri;
this.contentResolver = contentResolver;
}
public int select(String query, String[] arguments) {
Cursor c = contentResolver.query(uri, null, query, arguments, null);
int value = 0;
if(c.moveToFirst()){
value = c.getShort(0);
}
c.close();
return value;
}
public void insert(ContentValues contentValues){
try{
contentResolver.insert(uri,contentValues);
}catch(Exception ex){
Log.d(ProviderAccess.class.getName(), "insert APN: " + ex.getMessage());
}
}
public int update(ContentValues contentValues, String query, String[] arguments){
try {
int apnid= contentResolver.update(uri, contentValues, query, arguments);
return apnid;
}catch(Exception ex){
Log.d(ProviderAccess.class.getName(), "update APN: " + ex.getMessage());
return -1;
}
}
public void delete(String query, String[] arguments){
contentResolver.delete(uri, query, arguments);
}
}