设置数据卡流程

3 阅读7分钟

整体流程架构

SIM卡插入 → 识别激活 → 订阅注册 → 选择默认数据卡 
    ↓
UICC应用激活 → 设置数据允许 → 建立数据连接 → 获取数据配置

第一阶段:SIM卡识别和激活

1.1 SIM卡插入后的初始化

当SIM卡插入时:

SubscriptionManagerService.onSimStateChanged()
    → updateSubscription()
    → 创建订阅信息(SubscriptionInfoInternal)

1.2 订阅信息创建

// SubscriptionManagerService.java: 订阅初始化时的关键信息
SubscriptionInfoInternal.Builder builder = new SubscriptionInfoInternal.Builder()
    .setId(subId)                              // 订阅ID
    .setSimSlotIndex(phoneId)                  // SIM卡槽位
    .setIccId(iccId)                           // ICC ID
    .setImsi(imsi)                             // IMSI
    .setCarrierId(carrierId)                   // 运营商ID
    .setMcc(mcc)                               // 国家代码
    .setMnc(mnc)                               // 网络代码
    .setDisplayName(displayName)               // 显示名称
    .setCountryIso(countryIso)                 // 国家ISO代码
    .setMccMnc(mccMnc)                         // MCC+MNC
    .setPhoneId(phoneId)                       // Phone ID
    .setPortIndex(portIndex)                   // 端口索引
    .setCardId(cardId)                         // 卡ID (ICC ID或EID)

第二阶段:UICC应用激活

2.1 启用UICC应用

通过调用HAL的setUiccSubscription命令激活卡上的应用:

// RIL.java: 4144-4163 行
@Override
public void setUiccSubscription(int slotId, int appIndex, int subId, int subStatus,
        Message result) {
    RadioSimProxy simProxy = getRadioServiceProxy(RadioSimProxy.class);
    if (!canMakeRequest("setUiccSubscription", simProxy, result, RADIO_HAL_VERSION_1_4)) {
        return;
    }

    RILRequest rr = obtainRequest(RIL_REQUEST_SET_UICC_SUBSCRIPTION, result,
            mRILDefaultWorkSource);

    if (RILJ_LOGD) {
        riljLog(rr.serialString() + "> " + RILUtils.requestToString(rr.mRequest)
                + " slot = " + slotId + " appIndex = " + appIndex
                + " subId = " + subId + " subStatus = " + subStatus);
    }

    radioServiceInvokeHelper(HAL_SERVICE_SIM, rr, "setUiccSubscription", () -> {
        simProxy.setUiccSubscription(rr.mSerial, slotId, appIndex, subId, subStatus);
    });
}

参数说明:

参数含义
slotIdSIM卡槽位索引0, 1, 2等
appIndexUICC应用索引0 (GSM), 1 (CDMA), 2 (IMS) 等
subId订阅ID正整数
subStatus激活状态1 (激活), 0 (停用)

2.2 启用UICC应用(HAL 1.5+)

// RadioSimProxy.java: 139-147 行
public void enableUiccApplications(int serial, boolean enable) throws RemoteException {
    if (isEmpty() || mHalVersion.less(RIL.RADIO_HAL_VERSION_1_5)) return;
    if (isAidl()) {
        mSimProxy.enableUiccApplications(serial, enable);
    } else {
        ((android.hardware.radio.V1_5.IRadio) mRadioProxy).enableUiccApplications(
                serial, enable);
    }
}

第三阶段:设置SIM卡电源状态

3.1 SIM卡电源管理

// RadioSimProxy.java: 651-660 行
public void setSimCardPower(int serial, int state) throws RemoteException {
    if (isEmpty()) return;
    if (isAidl()) {
        mSimProxy.setSimCardPower(serial, state);
    } else if (mHalVersion.greaterOrEqual(RIL.RADIO_HAL_VERSION_1_6)) {
        ((android.hardware.radio.V1_6.IRadio) mRadioProxy).setSimCardPower_1_6(serial, state);
    } else {
        mRadioProxy.setSimCardPower_1_1(serial, state);
    }
}

SIM卡电源状态:

// TelephonyManager.java
CARD_POWER_DOWN = 0              // 关闭SIM卡电源
CARD_POWER_UP = 1                // 打开SIM卡电源 (正常模式)
POWER_UP_PASS_THROUGH = 2        // 打开SIM卡电源 (直通模式)
                                 // 在此模式下,Modem不控制SIM卡
                                 // 由系统直接发送APDU指令

第四阶段:设置默认数据卡

4.1 用户选择数据卡

当用户在设置中选择数据卡时,触发:

// MultiSimSettingController.java: 数据卡选择流程
private void onUserSelectSim(int subId, int simSelectDialogType) {
    if (simSelectDialogType == EXTRA_DEFAULT_SUBSCRIPTION_SELECT_TYPE_DATA) {
        // 用户选择了数据卡
        mSubscriptionManagerService.setDefaultDataSubId(subId);
    }
}

4.2 设置默认数据订阅ID

// SubscriptionManagerService.java
public void setDefaultDataSubId(int subId) {
    setDefaultSubId(subId, SubscriptionManager.DEFAULT_SUBSCRIPTION_ID, 
                    SettingsProvider.SETTING_USER_PREF_DATA_SUB);
}

private void setDefaultSubId(int subId, int subId Constant, String setting) {
    // 1. 更新数据库
    mSubscriptionDatabaseManager.setDefaultSubId(subId);
    
    // 2. 更新全局设置
    android.provider.Settings.Global.putInt(mContext.getContentResolver(),
            setting, subId);
    
    // 3. 触发监听器
    notifyDefaultSubscriptionChanged(subId);
}

存储位置:

设置项说明
user_pref_data_subSubId用户选择的数据卡
user_pref_voice_subSubId用户选择的通话卡
user_pref_sms_subSubId用户选择的短信卡

第五阶段:设置数据使能状态

5.1 用户启用/禁用数据

// DataSettingsManager.java: 用户启用数据
public void setDataEnabledForReason(@DataEnabledReason int reason, boolean enabled,
        @NonNull String callingPackage) {
    Message msg = Message.obtain(
            this,
            EVENT_SET_DATA_ENABLED_FOR_REASON,
            new AsyncResult(callingPackage, enabled, null));
    obtainMessage(msg).sendToTarget();
}

数据使能原因类型:

@DataEnabledReason
// 用户原因
REASON_USER_DATA_ENABLED = 1 << 0        // 用户启用数据
// 策略原因
REASON_POLICY_DATA_ENABLED = 1 << 1      // 政策启用数据
REASON_THERMAL = 1 << 2                  // 热管制禁用
REASON_CARRIER = 1 << 3                  // 运营商禁用
REASON_PROVISIONING_DATA_ENABLED = 1<<4  // 配置数据启用

5.2 通知Modem数据允许状态

// CommandsInterface.java: 2185 行
public void setDataAllowed(boolean allowed, Message result);

实现流程:

// RIL.java
mRil.setDataAllowed(isDataEnabled, message);
    → RadioDataProxy.setDataAllowed(serial, allowed)
    → HAL接口调用

第六阶段:多SIM卡协调设置

6.1 分组订阅管理

某些运营商支持分组SIM卡(Grouped Subscriptions),它们必须共享相同的设置:

// MultiSimSettingController.java: 73-81 行
/**
 * 多SIM卡协调规则:
 * 
 * 1) 分组订阅必须有相同的MOBILE_DATA和DATA_ROAMING设置
 * 2) 默认设置会自动更新和继承
 *    - 如果默认订阅A切换到同组的订阅B,B会变成新的默认
 * 3) 对于主要订阅,只有默认数据订阅会启用MOBILE_DATA
 */

6.2 订阅分组信息

// SubscriptionInfoInternal.java
class SubscriptionInfoInternal {
    private String mGroupUuid;         // 组UUID
    private String mGroupOwner;        // 组所有者
    private int mIsOpportunistic;      // 是否为机会性订阅
}

6.3 自动数据卡选择

系统会自动选择默认数据卡:

// MultiSimSettingController.java: 624-677 行
private void updateDefaultValues() {
    // 场景1: 只有一个主订阅,自动设为默认数据卡
    if (conditionForOnePrimarySim) {
        int subId = mPrimarySubList.get(0);
        if (hasData()) {
            mSubscriptionManagerService.setDefaultDataSubId(subId);
        }
    }
    
    // 场景2: 多个主订阅,如果没有选择默认数据卡,提示用户选择
    if (hasDualActiveData()) {
        // 弹出用户选择对话框
    }
}

第七阶段:建立数据连接

7.1 数据连接请求

一旦设置数据卡并启用数据,系统会建立数据连接:

// DataNetworkController.java
setupDataCall(int accessNetworkType, DataProfile dataProfile, 
              boolean allowRoaming, int reason, ...)

7.2 设置数据配置文件

// RIL.java
public void setupDataCall(int accessNetworkType, DataProfile dataProfile,
        boolean allowRoaming, int reason, LinkProperties linkProperties,
        int pduSessionId, NetworkSliceInfo sliceInfo,
        TrafficDescriptor trafficDescriptor, boolean matchAllRuleAllowed,
        Message result)

数据配置文件包含:

class DataProfile {
    String apn;                    // APN名称
    int protocol;                  // IP/IPV6/IPV4V6
    int roamingProtocol;          // 漫游协议
    String user;                   // 用户名
    String password;               // 密码
    int authType;                  // 认证类型
    String operatorNumeric;        // 运营商代码(MCC+MNC)
    boolean enabled;               // 是否启用
    int type;                      // APN类型(default/mms/supl等)
}

第八阶段:订阅管理数据库

8.1 订阅信息存储

所有订阅信息都存储在SimInfo表中:

字段说明
_id订阅ID
icc_idSIM卡ICC ID
sim_slot_indexSIM卡槽位
display_name显示名称
carrier_name运营商名称
mcc国家代码
mnc网络代码
sim_serial_numberSIM卡序列号
country_iso国家ISO代码
uicc_applications_enabledUICC应用是否启用
is_embedded是否为eSIM
carrier_id运营商ID
group_uuid分组UUID
is_opportunistic是否为机会性订阅
port_index端口索引
usage_setting使用场景(VOICE/SMS/DATA)

8.2 查询订阅信息

// 获取特定订阅信息
SubscriptionInfo subInfo = SubscriptionManager.getActiveSubscriptionInfo(subId);

// 获取所有活跃订阅
List<SubscriptionInfo> subInfoList = SubscriptionManager.getActiveSubscriptionInfoList();

// 获取默认数据订阅
int defaultDataSubId = SubscriptionManager.getDefaultDataSubscriptionId();

完整的设置数据卡流程时序

时间轴 ────────────────────────────────────────────────────────
│
├─ [1] 用户插入SIM卡
│    └─→ Modem检测到卡插入
│        └─→ 发送卡状态改变通知
│
├─ [2] RIL处理卡状态
│    └─→ getIccCardStatus()
│        └─→ UiccController.onIccStatusChanged()
│
├─ [3] SubscriptionManager创建订阅
│    └─→ SubscriptionManagerService.updateSubscription()
│        ├─→ 读取SIM卡信息(IMSI, ICCID等)
│        ├─→ 创建SubscriptionInfoInternal
│        └─→ 插入SimInfo数据库
│
├─ [4] 激活UICC应用
│    └─→ setUiccSubscription(slotId, appIndex, subId, 1)
│        └─→ HAL层激活应用
│
├─ [5] 启用UICC应用(可选, HAL 1.5+)
│    └─→ enableUiccApplications(serial, true)
│
├─ [6] 系统自动或用户手动选择数据卡
│    ├─→ MultiSimSettingController分析
│    ├─→ 如果只有一张卡,自动设为默认
│    └─→ 如果多张卡,提示用户选择
│
├─ [7] 设置默认数据订阅ID
│    └─→ SubscriptionManagerService.setDefaultDataSubId(subId)
│        ├─→ 更新数据库
│        ├─→ Settings.Global更新user_pref_data_sub
│        └─→ 通知监听器
│
├─ [8] DataSettingsManager设置数据使能
│    └─→ setDataEnabledForReason(REASON_USER_DATA_ENABLED, true)
│
├─ [9] 通知Modem数据允许状态
│    └─→ setDataAllowed(true, message)
│        └─→ HAL接口setDataAllowed()
│
├─ [10] 加载数据配置和APN列表
│    └─→ DataProfileManager.loadDataProfiles()
│        ├─→ 从TelephonyProvider查询APN
│        └─→ 创建DataProfile对象
│
├─ [11] 建立初始数据连接 (如果需要)
│    └─→ DataNetworkController.setupDataCall()
│        ├─→ RIL.setupDataCall(AccessNetworkType, DataProfile)
│        └─→ 等待Modem建立连接
│
└─ [12] 数据连接就绪,应用可使用数据

配置文件关键参数

配置优先级

运营商可通过CarrierConfig配置数据卡行为:

// CarrierConfigManager.KEY_* 常见配置
KEY_DATA_LIMIT_THRESHOLD_BYTES         // 数据限制阈值
KEY_MOBILE_DATA_DOWNLOAD_SPEED_MBPS    // 下行速率
KEY_MOBILE_DATA_UPLOAD_SPEED_MBPS      // 上行速率
KEY_ENHANCED_4G_LTE_BOOL               // 是否支持LTE+
KEY_DEFAULT_DATA_ROAMING_ENABLED_BOOL  // 默认漫游数据
KEY_SHOW_OPERATOR_NAME_IN_STATUSBAR    // 显示运营商名称

多SIM协调配置

KEY_ALLOW_METERED_NETWORK_TYPES        // 计量网络类型
KEY_DATA_ROAMING_ENABLED_BY_DEFAULT    // 默认启用数据漫游
KEY_SHOW_DATA_SWITCH_NOTIFICATION_BOOL // 显示数据切换提示

特殊场景处理

1. eSIM (嵌入式SIM)

// eSIM需要下载profile
EuiccManager manager = context.getSystemService(EuiccManager.class);
manager.downloadSubscription(...);

2. 双卡双待(DSDS)

// 两张卡可以同时有网络
// 但通常只有一张是默认数据卡
// 用户可选择使用哪张卡的数据

3. 5G NSA/SA支持

// 新增网络类型支持
NETWORK_TYPE_NR        // 5G NR
NETWORK_TYPE_LTE_CA    // 5G NSA (LTE+NR)

调试和日志

查看数据卡设置的相关日志:

# 查看订阅信息
adb shell dumpsys telephony.registry | grep -i subscription

# 查看多SIM控制器状态
adb shell dumpsys telephony.tel_registry | grep -i "MultiSimSettingController"

# 查看数据设置
adb shell dumpsys telephony.tel_registry | grep -i "DataSettings"

# 查看默认SIM卡设置
settings get global user_pref_data_sub
settings get global user_pref_voice_sub
settings get global user_pref_sms_sub

这就是Android系统完整的设置数据卡流程!关键要点包括:

  1. 自动识别 - 插入SIM卡后系统自动识别和创建订阅
  2. UICC激活 - 通过HAL激活SIM卡上的应用
  3. 智能选择 - 自动或提示用户选择默认数据卡
  4. 多SIM协调 - 分组SIM卡共享设置
  5. 数据配置 - 从数据库加载APN和配置信息
  6. 连接建立 - 最后建立实际的数据连接