How to switch from 3G and 2G/EDGE vice versa?

senthil prabhu picture senthil prabhu · Jan 30, 2012 · Viewed 8.5k times · Source

I am trying to switch the network preference from 3G to 2G/EDGE through code and vice versa. I can able to switch on and off the mobile data connection. Now i need know how to switch between 3G to 2G/EDGE and vice versa through code. can somebody help me here. Thanks in advance.

Answer

pulce picture pulce · Jul 29, 2015

I've stumbled upon a way to solve this with reflection and system call commands, and decided to report it even though the thread is old and there are some caveats:

  1. Root required
  2. Hackish and maybe ROM-specific (tested on CM 12.1 titan)
  3. Probably not working on all android versions (tested on 5.1.1)

Much of the code is borrowed from / inspired by this answer by ChuongPham.

First we need to get the correct transaction code by getting the value of a declared field of the ITelephony class. Since I suspect the name of the field might be slightly different depending on the platform (for mine the field name is "TRANSACTION_setPreferredNetworkType_96"), I provide a solution that is as flexible as possible:

private static String get3gTransactionCode(Context context) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, NoSuchFieldException {
    final TelephonyManager mTelephonyManager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
    final Class<?> mTelephonyClass = Class.forName(mTelephonyManager.getClass().getName());
    final Method mTelephonyMethod = mTelephonyClass.getDeclaredMethod("getITelephony");
    mTelephonyMethod.setAccessible(true);
    final Object mTelephonyStub = mTelephonyMethod.invoke(mTelephonyManager);
    final Class<?> mTelephonyStubClass = Class.forName(mTelephonyStub.getClass().getName());
    final Class<?> mClass = mTelephonyStubClass.getDeclaringClass();
    for (Field f:mClass.getDeclaredFields()) {
        if (f.getName().contains("setPreferredNetworkType")) {
            final Field field = mClass.getDeclaredField(f.getName());
            field.setAccessible(true);
            return String.valueOf(field.getInt(null));
        }
    }
    throw new NoSuchFieldException();
}

Next we can use the transaction code in a system call via su:

private static void setPreferredNetworkType(Context context, int preferredType) throws ClassNotFoundException, NoSuchMethodException, NoSuchFieldException, IllegalAccessException, InvocationTargetException {
    String transactionCode = get3gTransactionCode(context);
    String command = "service call phone " + transactionCode + " i32 " + preferredType;
    executeCommandViaSu(context, "-c", command);
}

In my case, I call that method with the 2nd parameter being 1 for 2G, and 10 for 3G preference. The constants for different network types can be found here.

For convenience and completeness I also copy-paste the executeCommandViaSu method from ChuongPham's answer here:

private static void executeCommandViaSu(Context context, String option, String command) {
    boolean success = false;
    String su = "su";
    for (int i=0; i < 3; i++) {
        // Default "su" command executed successfully, then quit.
        if (success) {
            break;
        }
        // Else, execute other "su" commands.
        if (i == 1) {
            su = "/system/xbin/su";
        } else if (i == 2) {
            su = "/system/bin/su";
        }
        try {
            // Execute command as "su".
            Runtime.getRuntime().exec(new String[]{su, option, command});
        } catch (IOException e) {
            success = false;
            // Oops! Cannot execute `su` for some reason.
            // Log error here.
        } finally {
            success = true;
        }
    }
}