7

Is there a way of returning the value of Android's mobile network setting for "use only 2G networks"?

The app being developed measures the internet speed at a certain location, but in order for this to be relevant, it must know if the user is deliberately restricting mobile internet to 2G.

I've taken a look at ConnectivityManager, but it only provides information about the background data setting or all networks. Iterating through them reveals that despite the setting being enabled, HSPA and UMTS return true for isAvailable():

for (NetworkInfo netInfo : cm.getAllNetworkInfo()) {
    Log.i(TAG, netInfo.getSubtypeName() + ": " + netInfo.isAvailable());
}

The only hint I've found amidst all this is that netInfo.getReason() returns "connectionDisabled" on HSPA and UMTS when the setting is enabled. The trouble is, when the setting is disabled, those network types don't necessarily appear in the list at all. It doesn't seem right to me to use a string comparison specifically on HSPA and UMTS for "connectionDisabled".

What's the right way of tackling this?

Paul Lammertsma
  • 37,593
  • 16
  • 136
  • 187
  • Before anybody goes about trying to obtain meaningful information from the snippet above, don't bother; I've tried. It seems that both when "2G only" mode is enabled or when the only available signal is GPRS, the 3G networks return "connectionDisabled". In other words, `getReason()` doesn't provide any useful information. – Paul Lammertsma Jun 28 '11 at 14:03
  • [Android issue #8753 addresses this.](http://code.google.com/p/android/issues/detail?id=8753) I've tried the suggestion outlined there, but on the tested devices, the `Setting.Secure.getInt()` always returns `1`, regardless of the actual setting or the connectivity state. – Paul Lammertsma Jul 07 '11 at 21:56
  • Follow-up: issue #8753 suggests that `Settings.Secure.NETWORK_PREFERENCE` reflects this setting. The setting's undocumented, but it appears to reflect the preference of `TYPE_WIFI` over `TYPE_MOBILE` when both connections are available, and is not what I'm looking for. – Paul Lammertsma Jul 07 '11 at 22:06

2 Answers2

5

For a small subset of devices (specifically for the LG Optimus 2X Speed, LG-P990), an answer seems to be:

int enabled = Settings.Secure.getInt(getContentResolver(),
        "preferred_network_mode", -1);
Log.d("MYAPP", "2G only enabled: " + enabled);

Where the "use only 2G networks" setting is specified as:

  • 0 indicates the setting is disabled
  • 1 indicates the setting is enabled
  • -1 indicates the setting is not set (some devices?)

How I discovered this? I gathered all the key/value pairs from Settings.Secure using the following:

ContentResolver cr = getContentResolver();
Cursor cursor = cr.query(Settings.Secure.CONTENT_URI, null, null, null, null);
if (cursor.moveToFirst()) {
    while (!cursor.isAfterLast()) {
        Log.d("MYAPP", "cursor: "
                + cursor.getString(0) + ", "
                + cursor.getString(1) + ", "
                + cursor.getString(2));
        cursor.moveToNext();
    }
}

I compared results between enabling and disabling the setting, and sure enough I got:

07-08 00:15:20.991: DEBUG/MYAPP(13813): cursor: 5154, preferred_network_mode, 1

Do NOT use the index column (5154 in the example above), as I've noticed it changes between toggling the setting.

Although this correlates with some documentation for Settings.Secure I found online, this value isn't respected by all phones.

If your device returns -1, perhaps listing the key value pairs will reveal which setting you need. Please comment if you encounter it!

Paul Lammertsma
  • 37,593
  • 16
  • 136
  • 187
  • 1
    I've noticed that HTC and Samsung devices don't display a specific option for preferring 2G networks, and instead allow the user to choose between GSM (2G/2.5G) or WCDMA (3G/4G) or both. I haven't found the setting for this yet. – Paul Lammertsma Jul 08 '11 at 18:47
  • it is one of "hardest" - you should use this solution but tested on different device, find bugs or differences in system and expand your logic for problemly device. so created SuperSU –  May 14 '18 at 17:38
0

As far as I can tell, there is no documented way of getting value for that setting. But there is a Use2GOnlyCheckBoxPreference class that can be used as an example. It uses internal Phone and PhoneFactory classes to obtain the current value of prefer_2g setting.

You can use Phone and PhoneFactory classes via reflection. But of cause this is undocumented and is on your own risk. Here is relevant code from Use2GOnlyCheckBoxPreference:

import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneFactory;

public class Use2GOnlyCheckBoxPreference extends CheckBoxPreference {

    private Phone mPhone;
    private MyHandler mHandler;

    public Use2GOnlyCheckBoxPreference(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        mPhone = PhoneFactory.getDefaultPhone();
        mHandler = new MyHandler();
        mPhone.getPreferredNetworkType(
                mHandler.obtainMessage(MyHandler.MESSAGE_GET_PREFERRED_NETWORK_TYPE));
    }

    private class MyHandler extends Handler {

        private static final int MESSAGE_GET_PREFERRED_NETWORK_TYPE = 0;

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE_GET_PREFERRED_NETWORK_TYPE:
                    handleGetPreferredNetworkTypeResponse(msg);
                    break;
            }
        }

        private void handleGetPreferredNetworkTypeResponse(Message msg) {
            AsyncResult ar = (AsyncResult) msg.obj;

            if (ar.exception == null) {
                int type = ((int[])ar.result)[0];
                Log.i(LOG_TAG, "get preferred network type="+type);
                setChecked(type == Phone.NT_MODE_GSM_ONLY);
            } else {
                // Weird state, disable the setting
                Log.i(LOG_TAG, "get preferred network type, exception="+ar.exception);
                setEnabled(false);
            }
        }
    }   
}
inazaruk
  • 74,247
  • 24
  • 188
  • 156
  • If I understand correctly, in order to access `Phone` and `PhoneFactory`, I need to be working with the source of the SDK, which means it won't work unless you can deploy your own Android distribution. [I've tried to get `getDefaultPhone()` through Java reflection](http://stackoverflow.com/questions/2143754/can-a-telephony-phone-object-be-instantiated-through-the-sdk) before, but the phone needs to be rooted. Am I missing a trick to get this to work? – Paul Lammertsma Jun 28 '11 at 12:48
  • Yes, you are correct. It turns out you can't use `PhoneFactory` from 3rd party application. – inazaruk Jun 29 '11 at 07:27
  • This wont work (at least 5 years later), because PhoneFactory.makeDefaultPhone() must be called before than PhoneFactory.getDefaultPhone() – BamsBamx Oct 26 '16 at 09:24