I run a Southeast Asia Xperia 1 VI (XQ-EC72) on T-Mobile in the US. Sony never sold a US model, so the SEA firmware ships a modem band policy that leaves most US 5G NR off. The old workaround was the DSDS toggle in *#*#4636#*#*. I wasn't able to get that to work, so I ended up trying a different approach: editing the modem's band policy directly. Every firmware flash wipes it, because the flash rewrites modemst1 and modemst2, so this is a thing I now have to redo after every update.
The XDA guide by htcmage covers the file changes, but it drives the phone with Windows QPST EFS Explorer. I don't run Windows. This post is the Linux path, start to finish, and it worked on 69.2.A.4.110.
Three XML files live in the modem's EFS filesystem under /policyman:
band_set_01.xml defines which LTE and NR bands the modem will use.plmn_mcc_supported_01.xml maps carriers (MCC/MNC) to those band sets.policies.xml sets the execution order so the changes take effect.The replacement versions add T-Mobile, Verizon, AT&T, and Dish LTE and NR bands. You write those three files into /policyman, reboot, and the modem picks up the new bands. The EFS is not mounted on the Android filesystem, so you can't just cp them in. You reach it over the Qualcomm DIAG protocol.
A note on band n71, which is T-Mobile's 600 MHz. The band_set_01.xml below lists it for T-Mobile, so you would expect it to turn on. It will not, and this one is hardware, not policy. The APAC XQ-EC72 has no 600 MHz radio at all. Compare the band tables: the US model XQ-EC64 lists both LTE B71 and NR n71, while the APAC XQ-EC72 lists neither, even though both are the same PDX-245 platform. Sony only fits the 600 MHz front end on the US variant. LTE B71 and NR n71 ride the same RF path, and this phone has neither, so no policy edit will bring n71 back. On T-Mobile you get LTE 2/12/66 and NR n41, which is real mid-band 5G. Where the band files actually earn their keep is the bands the hardware does have but the SEA firmware switches off for US carriers. The big one is n77, the C-band that Verizon runs its 5G on, and the XQ-EC72 supports it.
The band data came from the XDA thread linked above. I have mirrored the three files here so they stay with this post:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Execution Ordering Configuration file
$Header: //commercial/MPSS.DE.7.0.c3/Main/modem_proc/mmcp/policyman/configurations/Master/Default/policies.xml#1 $
-->
<policy_list name = "XML Ordering"
changelist = "$Change: 57693037 $"
policy_ver = "8000.0.26"
>
<policy file="/policyman/global_defines.xml" execute_for="device" load_for="device" refresh_on_sim_change="false" />
<policy file="/policyman/device_config.xml" execute_for="device" load_for="device" refresh_on_sim_change="false" />
<policy file="/policyman/pre.xml" execute_for="subs" load_for="subs" refresh_on_sim_change="true" />
<!-- SONY BEGIN -->
<policy file="/policyman/plmn_mcc_supported_01.xml" execute_for="subs" load_for="device" refresh_on_sim_change="true" />
<policy file="/policyman/band_set_01.xml" execute_for="subs" load_for="device" refresh_on_sim_change="true" />
<!-- SONY END -->
<policy file="/policyman/carrier_policy.xml" execute_for="subs" load_for="subs" refresh_on_sim_change="true" />
<policy file="/policyman/post.xml" execute_for="subs" load_for="device" refresh_on_sim_change="false" />
<policy file="/policyman/generic_band_restrictions.xml" execute_for="subs" load_for="device" refresh_on_sim_change="false" />
<policy file="/policyman/global_1x_restrictions.xml" execute_for="subs" load_for="device" refresh_on_sim_change="false" />
<policy file="/policyman/nrdc_msim_concurrency.xml" execute_for="subs" load_for="device" refresh_on_sim_change="false" />
<policy file="/policyman/restrictions.xml" execute_for="subs" load_for="subs" refresh_on_sim_change="false" />
<policy file="/policyman/segment_loading.xml" execute_for="subs" load_for="subs" refresh_on_sim_change="false" />
</policy_list>
Keep all three together; you push them to the phone with efs2.py further down.
<policy> or <policy_list> and that schema_ver matches the files already on your device, or the modem may refuse to boot.python-pyserial, and root on that box to load a kernel module.By default the phone exposes only ADB over USB. Switch it to also expose the Qualcomm diagnostic port:
adb shell su -c 'setprop sys.usb.config diag,serial_cdev,rmnet,adb'
USB re-enumerates and ADB survives, because adb is still in the list. The phone now shows up as a Qualcomm DIAG device:
# lsusb | grep 05c6
Bus 001 Device 028: ID 05c6:9091 Qualcomm, Inc. ... Diagnostic Mode
The kernel does not bind a serial driver to it on its own. Load the option driver and tell it to claim that USB ID:
sudo modprobe option
echo 05c6 9091 | sudo tee /sys/bus/usb-serial/drivers/option1/new_id
This composition exposes three serial nodes and only one of them is the DIAG channel. The ttyUSB numbering is not stable across reboots or when you have other USB serial devices attached, so don't hardcode ttyUSB0. /dev/serial/by-id/ names each node by its USB interface number instead, and the DIAG channel is always interface 0, the -if00- link:
# ls -l /dev/serial/by-id/
usb-Sony_XQ-EC72_XXXXXXXXXX-if00-port0 -> ../../ttyUSB0
usb-Sony_XQ-EC72_XXXXXXXXXX-if01-port0 -> ../../ttyUSB1
usb-Sony_XQ-EC72_XXXXXXXXXX-if03-port0 -> ../../ttyUSB2
You don't have to identify it by hand though. The efs2.py client probes each candidate port, sends a DIAG version request (command 0x00), and keeps the one that answers with a valid framed reply. -if00- answers, the other two stay silent, so the script lands on the right port on its own. Set DIAGPORT only if you want to override the auto-detection.
EfsTools is the usual cross-platform tool for this. I built it and pointed it at /dev/ttyUSB0, and every command died with Critical error. Error 5. That is EIO. Its serial layer, libnserial, sets modem control lines on open (TIOCMSET for DTR and RTS), and the option driver rejects those ioctls on this diag interface. Raw pyserial opens the same port with no such problem and talks DIAG fine, so rather than patch a C library I wrote a small EFS2 client on top of pyserial. Its packet layouts are copied from EfsTools, so the wire format is identical, only the serial handling differs.
I wrote a small pyserial client, efs2.py, that speaks the DIAG EFS2 protocol: HDLC framing with the FCS-16 CRC (the same one PPP uses), subsystem command 0x4b, subsystem 19, and the file operations underneath (open, read, write, close, stat, and directory listing). It is a command-line tool, and it auto-detects the diag port as described above.
The script and its README are on GitHub: github.com/grmrgecko/qcdm-efs2.
It needs only Python and pyserial (pip install pyserial). The subcommands:
$ python3 efs2.py -h
usage: efs2.py [-h] [-p PORT] {ls,stat,pull,push,backup} ...
ls list an EFS directory
stat stat an EFS file
pull download one EFS file
push upload/overwrite one EFS file, then verify
backup recursively download an EFS directory
A couple of implementation details that matter, if you read the source. Writes go in 1 KB chunks. To replace a file, push deletes it first and then opens with O_WRONLY | O_CREAT, which is what the Windows tools do. The read loop stops on a short read.
Never write to the modem EFS without a backup. backup pulls the whole /policyman folder, subdirectories and all, to a local directory:
python3 efs2.py backup /policyman ./policyman-backup
Look at what came down and confirm the three XML files are well formed before you touch anything. Then push the replacement versions. push writes the file and immediately reads it back to check it landed byte for byte. Do not skip that: a truncated write here is how you end up with a modem that won't register.
python3 efs2.py push band_set_01.xml /policyman/band_set_01.xml
python3 efs2.py push plmn_mcc_supported_01.xml /policyman/plmn_mcc_supported_01.xml
python3 efs2.py push policies.xml /policyman/policies.xml
Each one prints read-back matches on success. ls shows the folder whenever you want to check:
python3 efs2.py ls /policyman
adb reboot
The reboot also drops the phone back to normal ADB, so the DIAG port is gone. Once it is back up, check that the modem now sees NR:
# adb shell dumpsys telephony.registry | grep -o 'isNrAvailable = [a-z]*'
isNrAvailable = true
On the SEA firmware before the change this reads false. Also confirm your preferred network mode includes NR:
# adb shell settings get global preferred_network_mode
26
Mode 26 is NR_LTE_GSM_WCDMA, so 5G is in the allowed radio types. If yours is set to an LTE-only mode, the modem will never camp on NR no matter what the band policy says.
After the reboot my phone came up on Mint (T-Mobile, MCC 310 MNC 260) with isNrAvailable = true, and 5G showed up where it had been LTE-only since the update.
Invalid XML in /policyman can stop the modem from booting. Two ways back:
.sin files with Newflasher, which restores the stock policy.Neither touches userdata, so a bad band edit is recoverable, not a brick. Just don't skip the backup.
The band data came from the XDA thread; the Linux serial and EFS2 client here are my own.