Managing APN Settings on Google Android

Managing APN Settings on Google Android

  • Comments 10

An APN (Access Point Name) is the information needed to establish a GPRS/EDGE/UMTS cellular packet data connection on a mobile device. Usually the device can be configured by the operator, the OEM, and users with an APN address such as wap.cingular or epc.tmobile.com that will be eventually resolved to the IP address of the GGSN in the operator's network.

Users can go to Settings-->Wireless control-->Mobile networks-->Access point names to view and edit existing APNs.

Google Android uses a SQLite data table to store all APNs configured on the device, as shown below:

Database: /data/data/com.android.providers.telephony/databases/telephony.db
Table: carriers
URI: content://telephony/carriers

_id

 name

numeric

mcc

mnc

apn

user

server

 password

1

 T-Mobile US

310260

310

260

epc.tmobile.com

none

   *

 none

 

proxy

port

mmsproxy

mmsport

mmsc

type

current

  

      

      

      

http://mms.msg.eng.t-mobile.com/mms/wapenc

default

1

This is a data record in the carriers table. the "_id" is the primary key auto-generated when you add new APN records using APIs or the UI manually. The "name" filed will appear on the setting UI. The 'numeric' field identifies the network that the APN associates with, which is a combination of mcc (mobile country code) and mnc (mobile network code). An operator may have a number of 'numeric' values to cover all this network. The "mmsproxy", "mmsport", and "mmsc" fields are for MMS configurations. The "type" field for an APN can be either 'default' for general data traffic, or 'mms' for MMS.

Note: Android does not support multiple actively APNs (simultaneous PDP contexts), as of 1.6 SDK. In other words, if MMS APN is activated, then the default web APN will be disconnected. 

The Android SDK (1.5 and 1.6) does not provide APIs to manage APN (Access Point Name)s directly. So you have to use the Telephony content provider to do that. Take a look at the TelephonyProvider.java source code will definitely help.I wrote some quick test code to enumerate and add APNs to the system, as well as set an APN to be the default one such that the device will use it for subsequent connections (this is indicated by the radio button in the APN list UI).

  •  Enumerate all APNs in the system:
    /*
     * Information of all APNs
     * Details can be found in com.android.providers.telephony.TelephonyProvider
     */
    public static final Uri APN_TABLE_URI =
        Uri.parse("content://telephony/carriers");
    /*
     * Information of the preferred APN
     *
     */
    public static final Uri PREFERRED_APN_URI =
        Uri.parse("content://telephony/carriers/preferapn"); 

    /*
     * Enumerate all APN data
     */
    private void EnumerateAPNs()
    {
        Cursor   c = context.getContentResolver().query(
                APN_TABLE_URI, null, null, null, null);
        if (c != null)
        {
            /*
             *  Fields you can retrieve can be found in
                com.android.providers.telephony.TelephonyProvider :
                
                db.execSQL("CREATE TABLE " + CARRIERS_TABLE +
                "(_id INTEGER PRIMARY KEY," +
                "name TEXT," +
                "numeric TEXT," +
                "mcc TEXT," +
                "mnc TEXT," +
                "apn TEXT," +
                "user TEXT," +
                "server TEXT," +
                "password TEXT," +
                "proxy TEXT," +
                "port TEXT," +
                "mmsproxy TEXT," +
                "mmsport TEXT," +
                "mmsc TEXT," +
                "type TEXT," +
                "current INTEGER);");
             */
            
            String s = "All APNs:\n";
            Log.d(TAG, s);
              try
            {
                s += printAllData(c); //Print the entire result set
            }
              catch(SQLException e)
              {
                  Log.d(TAG, e.getMessage());
              }
 
              //Log.d(TAG, s + "\n\n");
            c.close();
        }

    }

  • Add a new APN record:
   /*
     * Insert a new APN entry into the system APN table
     * Require an apn name, and the apn address. More can be added.
     * Return an id (_id) that is automatically generated for the new apn entry.
     */
    public int InsertAPN(String name, String apn_addr)
    {
        int id = -1;
        ContentResolver resolver = context.getContentResolver();
        ContentValues values = new ContentValues();
        values.put("name", name);
        values.put("apn", apn_addr);
       
        /*
         * The following three field values are for testing in Android emulator only
         * The APN setting page UI will ONLY display APNs whose 'numeric' filed is
         * TelephonyProperties.PROPERTY_SIM_OPERATOR_NUMERIC.
         * On Android emulator, this value is 310260, where 310 is mcc, and 260 mnc.
         * With these field values, the newly added apn will appear in system UI.
         */
        values.put("mcc", "310");
        values.put("mnc", "260");
        values.put("numeric", "310260");
       
        Cursor c = null;
        try
        {
            Uri newRow = resolver.insert(APN_TABLE_URI, values);
            if(newRow != null)
            {
                c = resolver.query(newRow, null, null, null, null);
                Log.d(TAG, "Newly added APN:");
                printAllData(c); //Print the entire result set
               
                // Obtain the apn id
                int idindex = c.getColumnIndex("_id");
                c.moveToFirst();
                id = c.getShort(idindex);
                Log.d(TAG, "New ID: " + id + ": Inserting new APN succeeded!");
            }
        }
        catch(SQLException e)
        {
            Log.d(TAG, e.getMessage());
        }

        if(c !=null )
            c.close();
        return id;
    }
  •  Set an APN to be the default
   /*
     * Set an apn to be the default apn for web traffic
     * Require an input of the apn id to be set
     */
    public boolean SetDefaultAPN(int id)
    {
        boolean res = false;
        ContentResolver resolver = context.getContentResolver();
        ContentValues values = new ContentValues();
       
        //See /etc/apns-conf.xml. The TelephonyProvider uses this file to provide
        //content://telephony/carriers/preferapn URI mapping
        values.put("apn_id", id);
        try
        {
            resolver.update(PREFERRED_APN_URI, values, null, null);
            Cursor c = resolver.query(
                    PREFERRED_APN_URI,
                    new String[]{"name","apn"},
                    "_id="+id,
                    null,
                    null);
            if(c != null)
            {
                res = true;
                c.close();
            }
        }
        catch (SQLException e)
        {
            Log.d(TAG, e.getMessage());
        }
         return res;
    }
Two helper functions are created to print data using a cursor:
    /*
     * Return all column names stored in the string array
     */
    private String getAllColumnNames(String[] columnNames)
    {
        String s = "Column Names:\n";
        for(String t:columnNames)
        {
            s += t + ":\t";
        }
        return s+"\n";
    }
   
    /*
     *  Print all data records associated with Cursor c.
     *  Return a string that contains all record data.
     *  For some weird reason, Android SDK Log class cannot print very long string message.
     *  Thus we have to log record-by-record.
     */
    private String printAllData(Cursor c)
    {
        if(c == null) return null;
        String s = "";
        int record_cnt = c.getColumnCount();
        Log.d(TAG, "Total # of records: " + record_cnt);
       
        if(c.moveToFirst())
        {
            String[] columnNames = c.getColumnNames();
            Log.d(TAG,getAllColumnNames(columnNames));
            s += getAllColumnNames(columnNames);
            do{
                String row = "";
                for(String columnIndex:columnNames)
                {
                    int i = c.getColumnIndex(columnIndex);
                    row += c.getString(i)+":\t";
                }
                row += "\n";
                Log.d(TAG, row);
                s += row;
            }while(c.moveToNext());
            Log.d(TAG,"End Of Records");
        }
        return s;
    }
The Android emulator's default APN is a T-Mobile APN as shown in the picture below:

 Then, let's add a new APN and set it to default:

       //Let's try insert a new APN, whose name is 'google2' and apn address is google.com, just for fun.
        int id = InsertAPN("google2","google.com");
       
        //Set the newly added APN to be the default one for web traffic.
        //The new one will show up in settings->Wireless controls->Mobile networks->Access Point Names),
        //and has been set as default (indicated by the green check button)
        SetDefaultAPN(id);

Then the newly added APN will appear in the UI and shown as 'default'.

Leave a Comment
  • Please add 2 and 8 and type the answer here:
  • Post
  • More of a question...after the APN has been added, how does the device ensure that the correct APN is used when an application is launched? how does the mapping between the APN name and the Application id look like?

  • I can successfuly enumerate and add APN but the problem is that the newly added APN is not displaying as what you have shown on the steps above.  I'm using HTC Tattoo as my test device. Any advice?

  • this is the most useful sample code I have ever found, thanks a lot! List all apns and set a target apn really worked!

  • Perfect! Just what I was looking for

  • Got a message "Force Close" Any help will be so much appreciated.

    import android.app.Activity;

    import android.os.Bundle;

    import android.content.ContentResolver;

    import android.content.Context;

    import android.content.ContentValues;

    import android.database.Cursor;

    import android.database.SQLException;

    import android.net.Uri;

    import android.util.Log;

    public class SmartAPN extends Activity {

    private static final String TAG = "MyActivity";

    private Context context;

       /** Called when the activity is first created. */

       @Override

       public void onCreate(Bundle savedInstanceState) {

           super.onCreate(savedInstanceState);

           setContentView(R.layout.main);

           EnumerateAPNs();

           //int id = InsertAPN("google2","google.com");

           //SetDefaultAPN(id);

       }

       /*

        * Information of all APNs

        * Details can be found in com.android.providers.telephony.TelephonyProvider

        */

       public static final Uri APN_TABLE_URI =

           Uri.parse("content://telephony/carriers");

       /*

        * Information of the preferred APN

        *

        */

       public static final Uri PREFERRED_APN_URI =

           Uri.parse("content://telephony/carriers/preferapn");  

       /*

        * Enumerate all APN data

        */

       private void EnumerateAPNs()

       {

           Cursor c = context.getContentResolver().query(APN_TABLE_URI, null, null, null, null);

           if (c != null)

           {

               /*

                *  Fields you can retrieve can be found in

                   com.android.providers.telephony.TelephonyProvider :

                   db.execSQL("CREATE TABLE " + CARRIERS_TABLE +

                   "(_id INTEGER PRIMARY KEY," +

                   "name TEXT," +

                   "numeric TEXT," +

                   "mcc TEXT," +

                   "mnc TEXT," +

                   "apn TEXT," +

                   "user TEXT," +

                   "server TEXT," +

                   "password TEXT," +

                   "proxy TEXT," +

                   "port TEXT," +

                   "mmsproxy TEXT," +

                   "mmsport TEXT," +

                   "mmsc TEXT," +

                   "type TEXT," +

                   "current INTEGER);");

                */

               String s = "All APNs:\n";

               Log.d(TAG, s);

                 try

               {

                   s += printAllData(c); //Print the entire result set

               }

                 catch(SQLException e)

                 {

                     Log.d(TAG, e.getMessage());

                 }

                 //Log.d(TAG, s + "\n\n");

               c.close();

           }

       }

       /*

        * Insert a new APN entry into the system APN table

        * Require an apn name, and the apn address. More can be added.

        * Return an id (_id) that is automatically generated for the new apn entry.

        */

       public int InsertAPN(String name, String apn_addr)

       {

           int id = -1;

           ContentResolver resolver = context.getContentResolver();

           ContentValues values = new ContentValues();

           values.put("name", name);

           values.put("apn", apn_addr);

           /*

            * The following three field values are for testing in Android emulator only

            * The APN setting page UI will ONLY display APNs whose 'numeric' filed is

            * TelephonyProperties.PROPERTY_SIM_OPERATOR_NUMERIC.

            * On Android emulator, this value is 310260, where 310 is mcc, and 260 mnc.

            * With these field values, the newly added apn will appear in system UI.

            */

           values.put("mcc", "310");

           values.put("mnc", "260");

           values.put("numeric", "310260");

           Cursor c = null;

           try

           {

               Uri newRow = resolver.insert(APN_TABLE_URI, values);

               if(newRow != null)

               {

                   c = resolver.query(newRow, null, null, null, null);

                   Log.d(TAG, "Newly added APN:");

                   printAllData(c); //Print the entire result set

                   // Obtain the apn id

                   int idindex = c.getColumnIndex("_id");

                   c.moveToFirst();

                   id = c.getShort(idindex);

                   Log.d(TAG, "New ID: " + id + ": Inserting new APN succeeded!");

               }

           }

           catch(SQLException e)

           {

               Log.d(TAG, e.getMessage());

           }

           if(c !=null )

               c.close();

           return id;

       }

       /*

        * Set an apn to be the default apn for web traffic

        * Require an input of the apn id to be set

        */

       public boolean SetDefaultAPN(int id)

       {

           boolean res = false;

           ContentResolver resolver = context.getContentResolver();

           ContentValues values = new ContentValues();

           //See /etc/apns-conf.xml. The TelephonyProvider uses this file to provide

           //content://telephony/carriers/preferapn URI mapping

           values.put("apn_id", id);

           try

           {

               resolver.update(PREFERRED_APN_URI, values, null, null);

               Cursor c = resolver.query(

                       PREFERRED_APN_URI,

                       new String[]{"name","apn"},

                       "_id="+id,

                       null,

                       null);

               if(c != null)

               {

                   res = true;

                   c.close();

               }

           }

           catch (SQLException e)

           {

               Log.d(TAG, e.getMessage());

           }

            return res;

       }

       /*

        * Return all column names stored in the string array

        */

       private String getAllColumnNames(String[] columnNames)

       {

           String s = "Column Names:\n";

           for(String t:columnNames)

           {

               s += t + ":\t";

           }

           return s+"\n";

       }

       /*

        *  Print all data records associated with Cursor c.

        *  Return a string that contains all record data.

        *  For some weird reason, Android SDK Log class cannot print very long string message.

        *  Thus we have to log record-by-record.

        */

       private String printAllData(Cursor c)

       {

           if(c == null) return null;

           String s = "";

           int record_cnt = c.getColumnCount();

           Log.d(TAG, "Total # of records: " + record_cnt);

           if(c.moveToFirst())

           {

               String[] columnNames = c.getColumnNames();

               Log.d(TAG,getAllColumnNames(columnNames));

               s += getAllColumnNames(columnNames);

               do{

                   String row = "";

                   for(String columnIndex:columnNames)

                   {

                       int i = c.getColumnIndex(columnIndex);

                       row += c.getString(i)+":\t";

                   }

                   row += "\n";

                   Log.d(TAG, row);

                   s += row;

               }while(c.moveToNext());

               Log.d(TAG,"End Of Records");

           }

           return s;

       }

    }

  • how do you check if the APN name is already in the telephony.db , so you are not creating duplicate APN entries on your android handset?

  • In Gingerbread, possible APN types are "default,mms,supl,dun,hipri" or "*" to cover all types mentioned before. BTW: an empty type is equal to "*".

    In Ice Cream Sandwich, the 3 APN types "fota,cbs,ims" were added.

    If an APN entry is valid for several types, you can list all relevant types in a comma delimited list WITHOUT space.

  • on Ice cream sandwich, inserting a new apn throws a WRITE_PERMISSION ERROR even if it is declared on the manifest itself.

    @Grandswiss were you able to insert or modify APN's?

  • i am unable to add the port value using above code.

    values.put("Port","8080") is not getting inserted Any help will be appreciated.

  • It works on most devices, but fails on those that use android 4+, do you have any suggestions, thank you!

Page 1 of 1 (10 items)