Android 多卡数据管理系统完整架构

4 阅读10分钟

Android 多卡数据管理系统完整架构

我来为你深入讲解 DSDS (Dual SIM Dual Standby) 和 DSDA (Dual SIM Dual Active) 设备上的数据管理机制。

1. 核心概念层

1.1 关键术语

概念定义说明
DDS (Default Data Subscription)默认数据订阅用户通常使用的数据 SIM,由用户或系统选择
NIDS (Non-Default Data Subscription)非默认数据订阅备用数据 SIM,平时不提供数据
PDS (Preferred Data Subscription)首选数据订阅当前实际使用的数据 SIM,可能与 DDS 不同
Opportunistic Data机会性数据特定场景(如 CBRS)下临时切换到的数据 SIM
mMaxDataAttachModemCount最大数据附着 Modem 数同时允许附着 PS 的 Modem 个数(单卡=1,双卡可能=1 或 2)

2. 核心组件架构

┌─────────────────────────────────────────────────────────────┐
│           ConnectivityService (Framework)                     │
│        - 处理 NetworkRequest                                   │
│        - 选择最佳网络                                          │
└────────────────┬────────────────────────────────────────────┘
                 │ NetworkRequest (MMS、INTERNET 等)
                 ▼
┌─────────────────────────────────────────────────────────────┐
│           PhoneSwitcher (Singleton)                           │
│        - 决定哪个 Modem 处理数据                                │
│        - 向 Modem 发送 ALLOW_DATA/setPreferredData 命令      │
│        - 处理 DDS 切换、紧急通话、语音通话中数据切换           │
└────────────────┬────────────────────────────────────────────┘
                 │
        ┌────────┴────────┐
        ▼                 ▼
  ┌──────────────┐  ┌──────────────────┐
  │ RadioConfig  │  │ MultiSimSetting  │
  │              │  │ Controller       │
  │ HAL 命令执行 │  │ 设置协调、RA引擎 │
  └──────────────┘  └──────────────────┘
        │                 │
        ▼                 ▼
┌─────────────────────────────────────────────────────────────┐
│    Per-Phone DataNetworkController (x2 on dual SIM)          │
│        - 创建和管理该 SIM 的所有数据网络 (DataNetwork)      │
│        - 处理 APN、PDP 上下文                                 │
│        - 管理网络请求(NetworkRequest)匹配                    │
└─────────────────────────────────────────────────────────────┘

3. PhoneSwitcher - 数据路由决策引擎

3.1 主要职责

// PhoneSwitcher 职责
public class PhoneSwitcher extends Handler {
    // 用户设置的默认数据 SIM ID
    protected int mPrimaryDataSubId;
    
    // 自动选择的数据 SIM ID(CBRS、自动切换等)
    private int mAutoSelectedDataSubId;
    
    // 通话中的手机 ID(影响数据路由)
    protected int mPhoneIdInVoiceCall;
    
    // 最终的首选数据手机 ID(发送给 Modem)
    protected int mPreferredDataPhoneId;
    
    // 最终的首选数据订阅 ID
    protected WatchedInt mPreferredDataSubId;
}

3.2 mPreferredDataPhoneId 的决策逻辑

updatePreferredDataPhoneId() 决策流程:

1️⃣ 紧急通话覆盖?
   YES  使用 mEmergencyOverride.mPhoneId
        (用于 GNSS SUPL 定位)

2️⃣ 有活跃语音通话?
   YES  IMS 注册技术 != IWLAN?
        YES  shouldSwitchDataDueToInCall()?
              YES  使用通话手机的数据
              NO   使用 getFallbackDataPhoneIdForInternetRequests()
        NO   使用 IWLAN 上的通话, 不切换数据

3️⃣ 无活跃通话
    使用 getFallbackDataPhoneIdForInternetRequests()

getFallbackDataPhoneIdForInternetRequests() 逻辑:
┌─ 有机会性数据 SIM (Opportunistic)?
   YES  使用机会性 SIM
   NO   使用主数据 SIM (mPrimaryDataSubId)

└─ 自动切换建议 (mAutoSelectedDataSubId)?
   YES  用自动选择的
   NO   用主数据 SIM
// 核心代码示例
protected void updatePreferredDataPhoneId() {
    // 1. 检查紧急覆盖
    if (mEmergencyOverride != null && findPhoneById(mEmergencyOverride.mPhoneId) != null) {
        mPreferredDataPhoneId = mEmergencyOverride.mPhoneId;
        return;
    }
    
    // 2. 检查语音通话
    int imsRegTech = mImsRegTechProvider.get(mContext, mPhoneIdInVoiceCall);
    if (isAnyVoiceCallActiveOnDevice() && imsRegTech != REGISTRATION_TECH_IWLAN) {
        if (imsRegTech != REGISTRATION_TECH_CROSS_SIM) {
            mPreferredDataPhoneId = shouldSwitchDataDueToInCall()
                    ? mPhoneIdInVoiceCall 
                    : getFallbackDataPhoneIdForInternetRequests();
        } else {
            logl("IMS call on cross-SIM, skip switching data");
        }
    } else {
        // 3. 无通话
        mPreferredDataPhoneId = getFallbackDataPhoneIdForInternetRequests();
    }
    
    // 更新订阅 ID
    mPreferredDataSubId.set(SubscriptionManager.getSubscriptionId(mPreferredDataPhoneId));
}

4. 数据路由命令执行

4.1 两种 HAL 命令方案

方案 A: setDataAllowed(较旧)

// 适用于 mMaxDataAttachModemCount < mActiveModemCount 的情况
// 例如:双卡但只能有一个 Modem 附着 PS

private void sendRilCommands(int phoneId) {
    if (mHalCommandToUse == HAL_COMMAND_ALLOW_DATA) {
        if (mActiveModemCount > 1) {
            // 对每个 Modem 分别发送 setDataAllowed 命令
            PhoneFactory.getPhone(phoneId)
                .mCi.setDataAllowed(isPhoneActive(phoneId), message);
        }
    }
}

// 逻辑:
// - 活跃手机: setDataAllowed(true)  ➜ Modem 可以附着 PS
// - 非活跃手机: setDataAllowed(false) ➜ Modem 不能附着 PS

方案 B: setPreferredDataModem(新型)

// 适用于 mMaxDataAttachModemCount == mActiveModemCount 的情况
// 例如:双卡双活(DSDA)

private void sendRilCommands(int phoneId) {
    if (mHalCommandToUse == HAL_COMMAND_PREFERRED_DATA) {
        if (phoneId == mPreferredDataPhoneId) {
            // 告诉 Modem 哪个逻辑 Modem 是首选数据 Modem
            mRadioConfig.setPreferredDataModem(mPreferredDataPhoneId, message);
        }
    }
}

// 优点:
// - 所有 Modem 都可以附着 PS(DSDA 优势)
// - Modem 层知道哪个是"首选",可以优化资源分配
// - 减少切换次数(不需要 attach/detach 序列)

4.2 选择流程

private void updateHalCommandToUse() {
    mHalCommandToUse = mRadioConfig.isSetPreferredDataCommandSupported()
            ? HAL_COMMAND_PREFERRED_DATA 
            : HAL_COMMAND_ALLOW_DATA;
}

5. 多卡数据管理的关键场景

场景 1: 用户改变默认数据 SIM

用户在设置中选择 SIM2 作为默认数据
    ↓
SubscriptionManager.setDefaultDataSubId(subId2)
    ↓
mPrimaryDataSubId = subId2
    ↓
SubscriptionManagerService.broadcastSubId(
    ACTION_DEFAULT_DATA_SUBSCRIPTION_CHANGED, subId2)
    ↓
PhoneSwitcher 收到广播
    ↓
evaluateIfImmediateDataSwitchIsNeeded()
    ↓
updatePreferredDataPhoneId()  // 更新 mPreferredDataPhoneId 到 phoneId1sendRilCommands(phoneId1)  // 向 Modem 发送新的数据路由命令
    ↓
实际数据流量转移到 SIM2

场景 2: MMS 在非 DDS 上发送(多卡数据特例)

应用请求 NET_CAPABILITY_MMS
    ↓
ConnectivityService 向 PhoneSwitcher 发送 NetworkRequest(MMS)
    ↓
TelephonyNetworkFactory 收到请求
    ↓
TelephonyNetworkFactory 为非 DDS SIM 的 MMS 创建专用网络
    ↓
如果非 DDS SIM 需要使用数据:
    - PhoneSwitcher.onRequestNetwork(mmsRequest)
    - 检查: mActiveModemCount > 1 && 是 MMS 请求?
    - YES ➜ 将非 DDS Modem 也激活 (activate phoneId)
    - 向非 DDS Modem 发送 setDataAllowed(true)
    ↓
MMS 可在非 DDS SIM 上建立 PDP 上下文
    ↓
MMS 发送完成或超时
    ↓
PhoneSwitcher.onReleaseNetwork(mmsRequest)
    ↓
取消激活非 DDS Modem: setDataAllowed(false)
    ↓
数据流量回到 DDS SIM

关键代码:

// PhoneSwitcher.onRequestNetwork()
private void onRequestNetwork(NetworkRequest networkRequest) {
    TelephonyNetworkRequest req = new TelephonyNetworkRequest(networkRequest, ...);
    if (!mNetworkRequestList.contains(req)) {
        mNetworkRequestList.add(req);
        onEvaluate(REQUESTS_CHANGED, "netRequest");
    }
}

// 在 onEvaluate() 中:
if (mActiveModemCount > 1 && networkRequest.hasCapability(MMS)) {
    // 临时激活非 DDS Modem 以处理 MMS
    activate(nonDdsPhoneId);
}

场景 3: 语音通话中的数据 SIM 切换

用户在 SIM1 上接听电话(SIM1 非 DDS)
    ↓
Phone.getState() != IDLE
    ↓
PhoneSwitcher.onEvaluate()
    ↓
updatesIfPhoneInVoiceCallChanged() = true
    ↓
mPhoneIdInVoiceCall = 0  (SIM1 的 phoneId)
    ↓
updatePreferredDataPhoneId()
    ↓
shouldSwitchDataDueToInCall() 检查:
  ✓ 通话手机 (SIM1) 数据是否启用?
  ✓ DDS 手机 (SIM2) 数据是否启用?
    ↓
YES ➜ 切换数据到 SIM1 (通话手机)
NO  ➜ 保留数据在 SIM2 (DDS)

// 为什么这样设计?
// 在 DSDS 设备中,同时只有一个 Modem 能处理数据
// 如果 SIM1 打电话,其 PS 就被占用
// 数据流量无法通过 SIM1,必须切到其他 SIM
// 此时如果用户启用了 SIM1 的数据,系统会临时
// 切换数据到 SIM1 以优化用户体验

场景 4: 自动数据切换(Auto Data Switch)

AutoDataSwitchController 监控信号质量和网络质量
    ↓
定期评估: evaluateAutoDataSwitch()
    ↓
对比 DDS 和 NIDS 的信号强度、网络类型
    ↓
分数评估 (Score Tolerance):
  NIDS 分数 > DDS 分数 + ScoreTolerance?
    ↓
YES ➜ 建议切换到 NIDS
    ▼
检查稳定性: 
  信号状态保持稳定 > StabilityThreshold 毫秒?
    ↓
YES ➜ 开始网络验证 (Ping 测试)
    ▼
验证结果 ✓
    ↓
PhoneSwitcher.trySetOpportunisticDataSubscription(nidsSubId, needValidation=false)
    ↓
mAutoSelectedDataSubId = nidsSubId
    ↓
updatePreferredDataPhoneId()  // 现在用 NIDS 作为数据 SIM
    ↓
sendRilCommands() ➜ 切换到 NIDS
    ↓
数据通过更好的 NIDS 传输

// DDS 恢复时的回退:
当 DDS 信号改善或 NIDS 信号恶化 > Tolerance
    ↓
Auto switch 建议切回 DDS
    ↓
PhoneSwitcher.trySetOpportunisticDataSubscription(
    DEFAULT_SUBSCRIPTION_ID)  // 取消机会性设置
    ↓
mAutoSelectedDataSubId = DEFAULT_SUBSCRIPTION_ID
    ↓
switchback to DDS

6. 多 Modem 激活/禁用逻辑

6.1 使用 HAL_COMMAND_ALLOW_DATA 时

活动 Modem 管理:

┌─────────────────────────────────────┐
│ 初始状态 (DSDS 设备)                 │
│ mActiveModemCount = 2               │
│ mMaxDataAttachModemCount = 1        │
│ SIM1 (DDS), SIM2 (NIDS)             │
└─────────────────────────────────────┘
                 │
      ┌──────────┴──────────┐
      ▼                     ▼
  Phone 0 (SIM1)       Phone 1 (SIM2)
  active=true          active=false
  ║                    ║
  ║ setDataAllowed(1)  ║ setDataAllowed(0)
  ║                    ║
  ▼                    ▼
 Modem 0               Modem 1
 允许 PS 附着           禁止 PS 附着
 可建立 PDP 上下文      无法建立 PDP 上下文

场景 1: MMS 在 SIM2 上请求
        ↓
        激活 Phone 1
        ↓
        active=true
        ↓
        setDataAllowed(1) 发送到 Modem 1
        ↓
        Modem 1 也可以 PS 附着
        ↓
        SIM2 可以建立 PDP 上下文
        ↓
        MMS 完成
        ↓
        停用 Phone 1
        ↓
        setDataAllowed(0) 发送到 Modem 1

6.2 使用 HAL_COMMAND_PREFERRED_DATA 时(DSDA)

┌─────────────────────────────────────┐
│ DSDA 设备状态                        │
│ mActiveModemCount = 2               │
│ mMaxDataAttachModemCount = 2        │
│ 所有 Modem 都可以 PS 附着            │
└─────────────────────────────────────┘
                 │
      ┌──────────┴──────────┐
      ▼                     ▼
  Phone 0 (SIM1)       Phone 1 (SIM2)
  active=true          active=true
  ║                    ║
  ║ (无需 setDataAllowed) ║ (无需 setDataAllowed)
  ║                    ║
  ▼ setPreferredDataModem(0)
 
 Modem 0 = 首选        Modem 1 = 普通
 优先处理 Internet     可处理其他 capability
 可建立 PDP 上下文      可建立 PDP 上下文

优势:
- 两个 Modem 都可以建立 PDP
- Internet 流量优先用 Modem 0
- 无需频繁 attach/detach
- MMS 可以直接在 Modem 1 上建立
- 支持真正的双活数据连接

7. DataNetworkController - Per-SIM 数据网络管理

// 每个 SIM 有独立的 DataNetworkController 实例

public class DataNetworkController extends Handler {
    // 此 Controller 关联的 SIM
    private int mSubId;
    
    // 此 SIM 上所有的数据网络(DataNetwork 实例)
    // 例如: INTERNET, MMS, IMS, CBS 等
    private List<DataNetwork> mDataNetworkList;
    
    // 所有待满足的网络请求
    private NetworkRequestList mAllNetworkRequestList;
    
    // Internet 数据网络状态
    private int mInternetDataNetworkState;
    
    // 当前活跃的 Internet 数据网络集合
    private Set<DataNetwork> mConnectedInternetNetworks;
}

7.1 DataNetworkController 的职责

网络请求 (NetworkRequest)
    │
    ▼ addNetworkRequest()
    
╔═══════════════════════════════════════════════════════╗
║     DataNetworkController (Per-SIM)                     ║
║                                                         ║
║  1. 查找满足请求的 DataProfile                         ║
║  2. 检查是否有现有网络可以满足此请求                    ║
║  3. 如果没有,创建新的 DataNetwork                      ║
║  4. 将请求与 DataNetwork 关联                          ║
║  5. 监控 DataNetwork 的连接状态                        ║
╚═══════════════════════════════════════════════════════╝
    │
    ▼
DataNetwork (通信具体细节)
    │
    ├─ 建立 PDP 上下文
    ├─ 等待连接
    ├─ 成功 ✓ / 失败 ✗
    └─ 监控连接状态
    
    ▼
LinkProperties / NetworkCapabilities
    │
    ▼
TelephonyNetworkFactory
    │
    ▼
ConnectivityService (最终使用)

7.2 多卡场景下的 DataNetworkController 交互

场景: 用户有两个活跃 SIM,MMS 请求来自非 DDS SIM

ConnectivityService
    │
    ├─ 要求 SIM1 (DDS) 的 DataNetworkController:
    │   "我需要 INTERNET 数据"
    │   ↓ 创建 Internet DataNetwork
    │
    └─ 要求 SIM2 (NIDS) 的 DataNetworkController:
        "我需要 MMS 数据"
        ↓
        但首先检查 PhoneSwitcher:
        ➜ "SIM2 的 Modem 被激活了吗?"
        ↓
        PhoneSwitcher 说: "等等,我需要先激活 Modem2"
        ↓
        setDataAllowed(true) 发送给 Modem2
        ↓
        DataNetworkController2 现在可以创建 DataNetwork
        ↓
        创建 MMS DataNetwork
        ↓
        连接完成后:
        MMS 通信通过 SIM2 的 PDP 上下文传输

场景结束:
MMS 请求结束
    ↓
PhoneSwitcher: "可以停用 Modem2 吗?"
    ↓
检查还有其他请求吗? NO
    ↓
setDataAllowed(false) 发送给 Modem2
    ↓
MMS DataNetwork 断开连接

8. 多卡数据管理的特殊功能

8.1 Cross-SIM IMS (Dual IMS)

用户有两个 IMS 注册:
- SIM1: 本地 IMS
- SIM2:  DDS (SIM1 的数据)  IMS

PhoneSwitcher:
    
    ├─ SIM1 通话  使用 SIM1 的数据
    
    ├─ SIM2 通话 (Cross-SIM)   SIM1  Internet APN
      └─ 不切换数据 SIM,而是通过 Cross-SIM IMS PDN 共享
    
    └─ 两个同时通话? (DSDA)
       ├─ SIM1: 自己的数据
       └─ SIM2: 通过 Cross-SIM IMS 共享 SIM1  Internet APN

8.2 紧急通话 DDS 切换

用户在 SIM2 (NIDS) 上拨打 911,但 SIM1 (DDS) 有 GPS 位置

PhoneSwitcher:
    │
    ├─ Emergency call on SIM2?
    │   ├─ 设备支持 Emergency SUPL on non-DDS?
    │   │   ├─ YES: 不切换 DDS,直接用 SIM2 的数据
    │   │   └─ NO: 需要切换 DDS 到 SIM2
    │   │       ↓
    │   │       mEmergencyOverride.mPhoneId = 1 (SIM2)
    │   │       ↓
    │   │       updatePreferredDataPhoneId()
    │   │       ↓
    │   │       sendRilCommands(1)
    │   │       ↓
    │   │       DDS 临时切换到 SIM2
    │   │
    │   └─ 通话结束?
    │       ├─ 进入 ECBM?
    │       │   └─ 等待 ECBM 退出或超时
    │       │
    │       └─ 恢复原 DDS

9. 多卡数据管理的配置参数

运营商配置 (CarrierConfig)

参数含义影响
KEY_AUTO_DATA_SWITCH_ALLOW_ROAMING_BOOL漫游时是否允许自动切换是否在漫游状态切换数据
KEY_AUTO_DATA_SWITCH_SCORE_TOLERANCE_INT自动切换的信号分数容差分数差 > 容差时切换
KEY_AUTO_DATA_SWITCH_AVAILABILITY_STABILITY_TIME_THRESHOLD_MILLIS稳定性时间网络状态稳定多久后才切换
KEY_CARRIER_CROSS_SIM_IMS_AVAILABLE_BOOL是否支持 Cross-SIM IMSNIDS 是否可通过 DDS 数据使用 IMS
KEY_REQUIRE_PING_TEST_BEFORE_SWITCH_BOOL切换前是否需要 Ping 测试网络验证策略

系统配置 (Framework Resource)

<!-- android/config.xml -->
<integer name="config_auto_data_switch_score_tolerance">10</integer>
<integer name="config_auto_data_switch_availability_stability_time_threshold_millis">2000</integer>
<bool name="config_auto_data_switch_allow_roaming">true</bool>

10. 完整数据路由决策树

┌─────────────────────────────────────────────────────────┐
│ 评估何时应该使用哪个 SIM 的数据                          │
└─────────────────────────────────────────────────────────┘
                       │
            ┌──────────┴──────────┐
            ▼                     ▼
      有紧急通话?            有语音通话?
        │                       │
     YESYES│
        ▼                       ▼
   使用紧急         检查通话 SIM 是否
   SIM 的数据       启用了数据?
   (GNSS SUPL)      │
                    ├─ YES ➜ 切换数据到通话 SIM
                    │        (DSDS 限制)
                    └─ NO  ➜ 保持原 DDS
                    
                    
        NONO│
        ▼                       ▼
        └─────────────┬─────────────┘
                      ▼
              有机会性数据 SIM?
              (Opportunistic/CBRS)
                      │
                   YES│
                      ▼
              验证该 SIM 可用?
                      │
                   YES│
                      ▼
              使用机会性 SIM
              
                   NO│
                      ▼
              有自动切换建议?
              (Auto Data Switch)
                      │
                   YES│
                      ▼
              检查信号/网络质量
              分数 > DDS + 容差?
                      │
                   YES│
                      ▼
              验证稳定性
              和网络连通性
                      │
                   YES│
                      ▼
              使用建议的 SIM
              
                   NO│
                      ▼
              ┌───────────────┐
              │  使用 DDS      │
              │(最终默认)      │
              └───────────────┘

11. 故障排查点

问题原因检查点
NIDS 无法建立数据连接Modem 未激活PhoneSwitcher 是否发送了 setDataAllowed(1)
MMS 在 NIDS 上失败mMaxDataAttachModemCount 限制设备是 DSDS 还是 DSDA?
自动切换不工作配置禁用或分数计算错误CarrierConfig 中 auto switch 参数是否为正数?
通话中数据中断没有切换到通话 SIMshouldSwitchDataDueToInCall() 返回值检查
DDS 切换失败Modem 命令执行失败检查 RadioConfig 响应状态

总结:多卡数据管理的核心设计理念

┌────────────────────────────────────────────────────────┐
 多卡数据管理的三层架构                                 
                                                        
  1: 决策层 (PhoneSwitcher)                            
      └─ 根据用户偏好、系统状态、网络质量               
         决定使用哪个 SIM 的数据                       
                                                        
  2: 执行层 (RadioConfig/MultiSimSettingController)  
      └─ 将决策转化为 HAL 命令                         
         (setDataAllowed / setPreferredDataModem)      
          Settings 更新                              
                                                        
  3: 实现层 (DataNetworkController)                   
      └─ 每个 SIM 独立管理自己的 PDP 上下文            
         根据 PhoneSwitcher 的指示创建/销毁 PDP       
                                                        
└────────────────────────────────────────────────────────┘

核心原则

  1. 单一责任: PhoneSwitcher 决策,RadioConfig 执行
  2. 隔离性: 每个 SIM 的 DataNetworkController 独立工作
  3. 优先级: 紧急 > 语音 > 用户偏好 > 自动切换 > 默认
  4. 可验证: 每次决策都有日志和指标记录