Android CarrierTestOverride 实战:无需实体卡模拟指定运营商网络环境
1. 什么是CarrierTestOverride如果你是一名Android开发者肯定遇到过这样的场景需要测试某个功能在不同运营商网络下的表现但手头又没有对应的实体SIM卡。比如想验证应用在美国Verizon网络下的兼容性总不能专门买张卡吧这时候CarrierTestOverride机制就能派上大用场了。简单来说CarrierTestOverride是Android系统提供的一个调试接口允许开发者通过XML配置文件模拟特定运营商的网络环境。这个功能隐藏在系统底层位于frameworks/opt/telephony模块中。它的核心原理是通过重写SIM卡的关键参数如MCC/MNC、IMSI、ICCID等让系统误以为插入了指定的运营商SIM卡。我在实际项目中使用这个功能解决过不少棘手问题。比如去年开发一个跨国支付应用时需要测试在20多个不同国家运营商网络下的支付流程。如果全靠实体卡测试光收集这些SIM卡就得花上万元。而用CarrierTestOverride只需要准备对应的XML配置文件半小时就能完成所有测试场景的搭建。2. 核心参数解析2.1 必须配置的关键字段要让CarrierTestOverride生效XML配置文件中必须包含以下核心参数carrierTestOverrides carrierTestOverride keyisInTestMode valuetrue/ carrierTestOverride keymccmnc value310010/ carrierTestOverride keygid1 valuebae0000000000000/ carrierTestOverride keygid2 valueffffffffffffffff/ carrierTestOverride keyimsi value310010123456789/ carrierTestOverride keyspn valueVerizon/ carrierTestOverride keypnn valueVerizon network/ carrierTestOverride keyiccid value123456789012345678901/ /carrierTestOverrides每个参数的作用如下isInTestMode必须设为true才能启用模拟模式mccmnc运营商国家代码和网络代码如310是美国的MCC010是Verizon的MNCimsi国际移动用户识别码通常由MCCMNC用户编号组成iccid集成电路卡识别码SIM卡的唯一标识spn运营商名称如Verizonpnn运营商网络名称如Verizon network2.2 如何获取运营商参数很多开发者第一次使用时不知道这些参数该填什么值。其实有几种简单的方法可以获取从实体SIM卡提取如果你有目标运营商的实体卡插入手机后通过以下ADB命令查看adb shell getprop gsm.sim.operator.numeric # 获取MCCMNC adb shell getprop gsm.sim.operator.imsi # 获取IMSI从公开数据库查询像维基百科上有完整的MCC/MNC列表比如Verizon Wireless的代码是310-010。从系统日志抓取插入实体卡后过滤logcat中的Telephony相关日志能看到完整的SIM卡信息。我在测试日本运营商时发现有些参数如GID1/GID2在不同运营商下的格式差异很大。这时候最好的办法是找一台插有该运营商SIM卡的真机把参数完整记录下来。3. 完整操作指南3.1 准备XML配置文件首先创建一个XML文件比如命名为carrier_test_conf_sim1.xml。文件内容参考上面的模板根据你要模拟的运营商修改对应参数。这里有个实际踩过的坑XML文件的编码必须是UTF-8否则系统解析时会报错。建议用专业的文本编辑器如VS Code创建不要用Windows记事本。3.2 推送配置文件到设备配置文件准备好后需要将其推送到手机的特定目录。根据Android版本不同路径可能略有差异adb root adb remount adb push carrier_test_conf_sim1.xml /data/user_de/0/com.android.phone/files/注意必须要有root权限文件名最后的数字如sim1对应phoneId双卡手机通常是1或2如果/data/user_de目录不存在可以尝试/data/data/com.android.phone/files/3.3 重启电话进程推送完成后需要重启com.android.phone进程使配置生效# 查找进程ID adb shell ps -A | grep phone # 杀死进程进程ID以实际查询结果为准 adb shell kill 3037 # 等待进程自动重启约5-10秒进程重启后系统会读取新的配置文件并生成一个carrierconfig开头的XML文件这表明配置已生效。3.4 验证配置可以通过以下几种方式验证模拟是否成功查看系统属性adb shell getprop gsm.sim.operator.numeric检查logcat日志adb logcat -b all | grep CarrierTestOverride在代码中获取TelephonyManager tm (TelephonyManager) getSystemService(TELEPHONY_SERVICE); String mccMnc tm.getSimOperator(); // 应返回配置的MCCMNC4. 常见问题排查4.1 配置未生效的可能原因在实际使用中可能会遇到配置不生效的情况。根据我的经验主要有以下几种原因文件权限问题确保配置文件有正确的读写权限通常是644。可以通过adb shell ls -l检查。phoneId不匹配双卡手机要确认使用的是sim1还是sim2的配置。可以通过adb shell getprop ril.mcc.mnc1和ril.mcc.mnc2来区分。进程未完全重启有些定制ROM的电话服务是多个进程组成的可能需要重启整个系统服务adb shell am broadcast -a android.intent.action.ACTION_CARRIER_CONFIG_CHANGED系统版本差异Android 10之前和之后的实现略有不同特别是文件路径可能有变化。如果遇到问题建议查看对应Android版本的源码。4.2 如何恢复原始状态测试完成后可以通过以下方式恢复原始SIM卡状态删除配置文件adb shell rm /data/user_de/0/com.android.phone/files/carrier_test_conf_sim1.xml adb shell rm /data/user_de/0/com.android.phone/files/carrierconfig-*.xml重启电话进程同上或者简单修改配置文件将isInTestMode设为false并重新推送5. 高级应用场景5.1 自动化测试集成这个功能在自动化测试中特别有用。我们可以编写脚本动态生成不同的运营商配置实现全自动化的多运营商测试import os import subprocess def test_with_carrier(mccmnc, imsi): # 生成XML配置文件 config fcarrierTestOverrides carrierTestOverride keyisInTestMode valuetrue/ carrierTestOverride keymccmnc value{mccmnc}/ carrierTestOverride keyimsi value{imsi}/ /carrierTestOverrides with open(temp.xml, w) as f: f.write(config) # 推送配置 subprocess.run([adb, push, temp.xml, /data/user_de/0/com.android.phone/files/carrier_test_conf_sim1.xml]) # 重启电话服务 subprocess.run([adb, shell, kill, $(adb shell ps | grep phone | awk {print $2})]) # 运行测试 subprocess.run([adb, shell, am, instrument, -w, com.example.test/androidx.test.runner.AndroidJUnitRunner])5.2 多运营商快速切换对于需要频繁切换运营商测试的场景可以准备多个配置文件通过脚本快速切换#!/bin/bash # 切换到Verizon配置 adb push verizon.xml /data/user_de/0/com.android.phone/files/carrier_test_conf_sim1.xml adb shell kill $(adb shell ps | grep phone | awk {print $2}) # 等待10秒后切换到ATT配置 sleep 10 adb push att.xml /data/user_de/0/com.android.phone/files/carrier_test_conf_sim1.xml adb shell kill $(adb shell ps | grep phone | awk {print $2})5.3 结合Mock Location使用有时候需要同时模拟运营商和地理位置。这时候可以结合Mock Location一起使用// 设置模拟位置 LocationManager lm (LocationManager) getSystemService(LOCATION_SERVICE); lm.addTestProvider(LocationManager.GPS_PROVIDER, false, false, false, false, true, true, true, 0, 5); lm.setTestProviderEnabled(LocationManager.GPS_PROVIDER, true); Location mockLocation new Location(LocationManager.GPS_PROVIDER); mockLocation.setLatitude(37.7749); // 旧金山纬度 mockLocation.setLongitude(-122.4194); // 旧金山经度 mockLocation.setAccuracy(5); mockLocation.setTime(System.currentTimeMillis()); lm.setTestProviderLocation(LocationManager.GPS_PROVIDER, mockLocation); // 设置模拟运营商通过CarrierTestOverride // 这里需要已经配置好XML文件并重启了电话服务6. 注意事项与限制虽然CarrierTestOverride非常强大但在使用时还是有一些需要注意的地方需要root权限由于要修改系统保护目录下的文件普通应用无法直接使用这个功能。在真机上测试需要root或者在模拟器中使用。部分功能可能受限模拟的运营商环境可能无法支持所有功能比如VoLTE、WiFi Calling等需要运营商证书的功能。Android版本兼容性这个机制在不同Android版本上实现可能不同。比如在Android 12上文件路径从/data/data改为了/data/user_de。影响系统稳定性频繁重启电话服务可能导致系统不稳定建议在测试完成后及时恢复原始配置。不能完全替代实体卡测试虽然能模拟大部分场景但对于需要实际网络交互的功能如短信收发还是需要实体卡验证。我在一个海外项目中就遇到过这样的问题用CarrierTestOverride模拟的运营商环境能通过大部分测试但实际部署后用户反馈支付失败。后来发现是因为没有模拟运营商的特殊APN配置。所以建议关键功能还是要用实体卡做最终验证。