Skip to main content

eSIM Installation

There are several ways to install eSIM on your device.

MethodDescriptioniOSAndroid
QRScan QR withSupportedSupported
LinkClick on link (web, browser, email)SupportedNOT Supported
AppInstallation initiated from an APPSupported (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:

  1. 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.

  2. 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

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?

  1. Install eSIM profile via Android APP
  2. Manage Profiles
  3. 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:

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:

  1. Get subscription list with subscription manager
  2. Check that ICCID is 1Global’s
  3. Verify that there is connectivity (if there is connectivity it will just update the APN, if not, it deletes and created new APN setting)
  4. 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);
}
}