[Android Q&A, Help & Troubleshooting] LSS4181:
Possible solutions.

A very, very late bump of my own thread.

I think I found some possible solutions, and they can be easy to test if you can build a custom ROM for your own device.

1. Setting KEY_PREFER_2G_BOOL to false globally

Go to frameworks/base/telephony/java/android/telephony/CarrierConfigManager.java, look for the key KEY_PREFER_2G_BOOL.

The default is set to true, set this to false, like this:

sDefaults.putBoolean(KEY_PREFER_2G_BOOL, false);

This would make this parameter defaults to false, even for carriers whose config file didn't explicitly set it to false (will be explained below).

2. Setting KEY_PREFER_2G_BOOL to false only for your carrier

Go to folder packages/apps/CarrierConfig/assets/. Look for a carrier config that matches your carrier's MCC and MNC (carrier_config_310260.xml), add this line into it.

<boolean name="prefer_2g_bool" value="false"/>

You can also refer to some other carriers' files, as some carriers already have this line defined in it.

For the above two methods, however, some detailed tests are needed to make sure the phone can really never downgrade to 2G signal under such conditions, as on the surface (at least from the looks of the respective source code files), what these do is simply removing the "2G/GSM Only" option from the "Preferred network type" spinner, but it'll still be using the network mode values that would permit 2G/GSM (refer to RILConstants.java for details).

EDIT: Unfortunately, I discovered that it's still possible for the device to fall back to 2G under certain situations, even with the above methods which removed 2G from the options.

3. Modify the values contained in the framework/service packages

If you can decompile/recompile your current ROM's respective framework/service packages, you can modify it this way.

Go to folder packages/services/Telephony/res/values/strings.xml (if you have the source code).

Look for this block (for example). You may need to look for the respective variants depending on your device/SIM type (GSM, CDMA, With or without 4G/LTE, etc).

<string-array name="enabled_networks_choices" translatable="false">
<item>@string/network_lte</item>
<item>@string/network_3G</item>
<item>@string/network_2G</item>
</string-array>
<string-array name="enabled_networks_4g_choices" translatable="false">
<item>@string/network_4G</item>
<item>@string/network_3G</item>
<item>@string/network_2G</item>
</string-array>
<string-array name="enabled_networks_values" translatable="false">
<item>"9"</item>
<item>"0"</item>
<item>"1"</item>
</string-array>

Note the enabled_networks_values part. Change it to match the ones you need. The details can be found frameworks/base/telephony/java/com/android/internal/telephony/RILConstants.java. Change the values to the ones that matches the modes you want. In the above example, the first value is for 4G/LTE, the second value is for 3G, and the last value is for GSM.

/* NETWORK_MODE_* See ril.h RIL_REQUEST_SET_PREFERRED_NETWORK_TYPE */
int NETWORK_MODE_WCDMA_PREF = 0; /* GSM/WCDMA (WCDMA preferred) */
int NETWORK_MODE_GSM_ONLY = 1; /* GSM only */
int NETWORK_MODE_WCDMA_ONLY = 2; /* WCDMA only */
int NETWORK_MODE_GSM_UMTS = 3; /* GSM/WCDMA (auto mode, according to PRL)
AVAILABLE Application Settings menu*/
int NETWORK_MODE_CDMA = 4; /* CDMA and EvDo (auto mode, according to PRL)
AVAILABLE Application Settings menu*/
int NETWORK_MODE_CDMA_NO_EVDO = 5; /* CDMA only */
int NETWORK_MODE_EVDO_NO_CDMA = 6; /* EvDo only */
int NETWORK_MODE_GLOBAL = 7; /* GSM/WCDMA, CDMA, and EvDo (auto mode, according to PRL)
AVAILABLE Application Settings menu*/
int NETWORK_MODE_LTE_CDMA_EVDO = 8; /* LTE, CDMA and EvDo */
int NETWORK_MODE_LTE_GSM_WCDMA = 9; /* LTE, GSM/WCDMA */
int NETWORK_MODE_LTE_CDMA_EVDO_GSM_WCDMA = 10; /* LTE, CDMA, EvDo, GSM/WCDMA */
int NETWORK_MODE_LTE_ONLY = 11; /* LTE Only mode. */
int NETWORK_MODE_LTE_WCDMA = 12; /* LTE/WCDMA */
int NETWORK_MODE_TDSCDMA_ONLY = 13; /* TD-SCDMA only */
int NETWORK_MODE_TDSCDMA_WCDMA = 14; /* TD-SCDMA and WCDMA */
int NETWORK_MODE_LTE_TDSCDMA = 15; /* TD-SCDMA and LTE */
int NETWORK_MODE_TDSCDMA_GSM = 16; /* TD-SCDMA and GSM */
int NETWORK_MODE_LTE_TDSCDMA_GSM = 17; /* TD-SCDMA,GSM and LTE */
int NETWORK_MODE_TDSCDMA_GSM_WCDMA = 18; /* TD-SCDMA, GSM/WCDMA */
int NETWORK_MODE_LTE_TDSCDMA_WCDMA = 19; /* TD-SCDMA, WCDMA and LTE */
int NETWORK_MODE_LTE_TDSCDMA_GSM_WCDMA = 20; /* TD-SCDMA, GSM/WCDMA and LTE */
int NETWORK_MODE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 21; /*TD-SCDMA,EvDo,CDMA,GSM/WCDMA*/
int NETWORK_MODE_LTE_TDSCDMA_CDMA_EVDO_GSM_WCDMA = 22; /* TD-SCDMA/LTE/GSM/WCDMA, CDMA, and EvDo */
int PREFERRED_NETWORK_MODE = Integer.parseInt(TelephonyManager.getTelephonyProperty(0,
"ro.telephony.default_network", Integer.toString(NETWORK_MODE_WCDMA_PREF)));

EDIT: I confirmed that via method 1 the 2G option would be removed from the "Preferred network type" options, but I still need to test whether the device really can no longer downgrade to 2G. Since method 1 works, method 2 should also work as it's based on the same principle. The system would see the KEY_PREFER_2G_BOOL parameter as false and remove the 2G option from the list.

EDIT 2: After some testing it doesn't seem to work. It seems it's also allowed to specify network settings inside the device trees via overlays (or build.prop parameters, such as ro.telephony.default_network), when building ROM. As such, the values set in device-specific scopes take a higher priority.

EDIT 3: It seems you need to do both: After modifying framework files like that in Method 3, you also need to change the "ro.telephony.default_network" parameter in build.prop (preferrably also in vendor build.prop), in order to let the custom network mode persist.