双卡搜网逻辑

3 阅读5分钟

双卡搜网(DSDS - Dual SIM Dual Standby)搜网逻辑

📱 核心架构

双卡搜网涉及的主要模块:

  1. ServiceStateTracker - 每个 SIM 卡对应一个,负责该卡的搜网状态跟踪
  2. NetworkScanRequestTracker - 全局搜网请求管理器
  3. PhoneConfigurationManager - 多卡配置管理(DSDS/DSDA/TSTS)
  4. PhoneSwitcher - 多卡间切换和网络请求调度
  5. RadioNetworkProxy - RIL 搜网代理

🔍 搜网状态流程

┌─────────────────────────────────────────────────────────┐
│ DSDS 搜网状态机                                           │
└─────────────────────────────────────────────────────────┘

SIM1 ServiceStateTracker          SIM2 ServiceStateTracker
    │                                  │
    ├─ 初始化:STATE_POWER_OFF         ├─ STATE_POWER_OFF
    │                                  │
    ▼                                  ▼
    │ 开机 → STATE_SEARCHING           │ 开机 → STATE_SEARCHING
    │         (搜网中)                 │         (搜网中)
    │         ↓                        │         ↓
    │    ┌─ 发送搜网请求到 RIL        │    ┌─ 等待 SIM1 搜网完成
    │    │  (可能并发或串行)           │    │  (或并发)
    │    └─ 接收搜网结果               │    └─ 接收搜网结果
    │         ↓                        │         ↓
    └─→ STATE_IN_SERVICE              └─→ STATE_IN_SERVICE
       或 STATE_OUT_OF_SERVICE              或 STATE_OUT_OF_SERVICE

🎯 关键概念

1. 搜网模式(SIM1 和 SIM2 的交互)

// 假设 DSDS 设备(2 个 SIM,1 个无线模块轮流使用)
Phone[] phones = [phone0, phone1];  // SIM1, SIM2

// SIM1 搜网时,SIM2 无法同时搜网
// 需要在 ServiceStateTracker 中进行调度

2. 搜网类型

// NetworkScanRequest 搜网类型
- SCAN_TYPE_ONE_SHOT        // 一次性搜网
- SCAN_TYPE_PERIODIC        // 周期性搜网

// 搜网请求类型
- TYPE_FULL_SERVICE          // 全服务搜网
- TYPE_LIMITED_SERVICE       // 受限服务搜网

3. 搜网优先级调度

NetworkScanRequestScheduler 中:

// 搜网调度策略
mLiveRequestInfo      // 正在进行的搜网请求
mPendingRequestInfo   // 等待的搜网请求

// 调度规则:
- System 应用的搜网请求优先级更高
- 可以中断其他应用的搜网请求

📊 双卡搜网的并发控制

让我查看 ServiceStateTracker 中的搜网触发点:

现在让我用图表和代码示例详细解释双卡搜网逻辑:

🔄 双卡搜网的完整流程

情景:用户开机或从飞行模式恢复,DSDS 设备有 2 个 SIM 卡

时间轴:

T0: 用户开机 / 飞行模式关闭
    
    ├─→ 系统初始化 Phone[0]  Phone[1]
       每个 Phone 初始化各自的 ServiceStateTracker
    
    T1: ServiceStateTracker0.pollState()
       └─ 发送 EVENT_POLL_STATE  Modem (SIM1 搜网请求)
          STATE: SEARCHING
    
    ┼────────────────  DSDS 设备上 ────────────────
          (单无线模块,需要时分复用)
    
    T2: ServiceStateTracker1 等待 SIM0 搜网完成
       (或者等待 Modem 就绪)
    
    T3: Modem 返回 SIM1 搜网结果给 RIL
       └─ ServiceStateTracker0 接收结果
          更新 mSS(ServiceState)
          广播注册者(Registrants)
          STATE: IN_SERVICE  OUT_OF_SERVICE  SEARCHING
    
    T4: ServiceStateTracker1 开始 SIM2 搜网
       └─ 发送 EVENT_POLL_STATE  Modem
          STATE: SEARCHING
    
    T5: Modem 返回 SIM2 搜网结果
       └─ ServiceStateTracker1 接收结果
          更新 mSS(ServiceState)
          广播注册者(Registrants)
          STATE: IN_SERVICE  OUT_OF_SERVICE  SEARCHING
    
    T6: 双卡搜网完成
    └─ 如果需要继续搜网,定时器触发下一次搜网

💾 关键代码位置

1. 搜网初始化点 - ServiceStateTracker:

// frameworks_opt_telephony/src/java/com/android/internal/telephony/ServiceStateTracker.java
// 行约 1387, 2425

// 自动搜网触发
mPhone.setNetworkSelectionModeAutomatic(null);

// 这会调用:
// GsmCdmaPhone.setNetworkSelectionModeAutomatic()
//   → CommandsInterface.setNetworkSelectionModeAutomatic()
//   → RIL.setNetworkSelectionModeAutomatic()
//   → 发送 AT 命令到 Modem

2. 搜网请求调度 - NetworkScanRequestTracker:

// 行 503-596: NetworkScanRequestScheduler

// 核心逻辑:
private boolean interruptLiveScan(NetworkScanRequestInfo nsri) {
    if (mLiveRequestInfo != null && mPendingRequestInfo == null
            && nsri.mUid == Process.SYSTEM_UID              // System 进程优先级高
            && mLiveRequestInfo.mUid != Process.SYSTEM_UID) {
        // 中断当前搜网,启动新的搜网
        doInterruptScan(mLiveRequestInfo.mScanId);
        mPendingRequestInfo = nsri;
        notifyMessenger(mLiveRequestInfo, TelephonyScanManager.CALLBACK_SCAN_ERROR,
                NetworkScan.ERROR_INTERRUPTED, null);
        return true;
    }
    return false;
}

3. 双卡配置管理 - PhoneConfigurationManager:

// 行 64-66: 多卡配置类型
public static final String DSDA = "dsda";      // Dual SIM Dual Active
public static final String DSDS = "dsds";      // Dual SIM Dual Standby
public static final String TSTS = "tsts";      // Triple SIM Triple Standby
public static final String SSSS = "";          // Single SIM

// 行 710-724: DSDS 启用时的处理
if (numOfActiveModems > oldNumOfActiveModems && numOfActiveModems == 2) {
    Log.i(LOG_TAG, "DSDS mode enabled");
    // 设置默认 VOICE subId 为 -1(无偏好)
    SubscriptionManagerService.getInstance().setDefaultVoiceSubId(
            SubscriptionManager.INVALID_SUBSCRIPTION_ID);
}

🎛️ 双卡搜网的并发控制机制

A. 串行搜网(DSDS 典型模式):

SIM1 搜网 → [等待完成] → SIM2 搜网 → [等待完成] → 周期性重复

B. 并行搜网(DSDA 模式):

SIM1 搜网
├─ [并行]
SIM2 搜网

C. 实现机制 - 在 GsmCdmaPhone 中:

// GsmCdmaPhone.java 中的搜网流程
@Override
public void setNetworkSelectionModeAutomatic(Message response) {
    // 每个 Phone 实例独立处理
    // 在 DSDS 中,两个 Phone 会顺序调用此方法
    
    // 调用 RIL 的相应方法
    mCi.setNetworkSelectionModeAutomatic(response);
}

🔗 双卡搜网的关键注册点

// 在 ServiceStateTracker 中的注册
public ServiceStateTracker(GsmCdmaPhone phone, CommandsInterface ci, ...) {
    mCi = ci;
    
    // 注册搜网完成事件
    mCi.registerForNotAvailable(this, EVENT_RADIO_UNAVAILABLE, null);
    mCi.registerForOn(this, EVENT_RADIO_ON, null);
    
    // 注册网络状态变化
    mCi.setOnSignalStrengthUpdate(this, EVENT_SIGNAL_STRENGTH_UPDATE, null);
    mCi.setOnNetworkStateChanged(this, EVENT_NETWORK_STATE_CHANGED, null);
    
    // 这些在 DSDS 中会同时为两个 ServiceStateTracker 注册
}

⚡ 关键的双卡搜网流程

启动搜网 (startNetworkScan):

// NetworkScanRequestTracker.java 行 644-655

public int startNetworkScan(
        boolean renounceFineLocationAccess, NetworkScanRequest request, 
        Messenger messenger, IBinder binder, Phone phone,
        int callingUid, int callingPid, String callingPackage) {
    
    int scanId = mNextNetworkScanRequestId.getAndIncrement();
    NetworkScanRequestInfo nsri = new NetworkScanRequestInfo(
            request, messenger, binder, scanId, phone,  // ← 指定哪个 Phone(SIM1 或 SIM2)
            callingUid, callingPid, callingPackage, renounceFineLocationAccess);
    
    // 发送到调度器处理(在 Handler 线程中)
    mHandler.obtainMessage(CMD_START_NETWORK_SCAN, nsri).sendToTarget();
    
    return scanId;  // 返回扫描 ID 给调用者
}

调度器处理搜网请求 (NetworkScanRequestScheduler):

// 行 587-597: 开始新搜网

private synchronized boolean startNewScan(NetworkScanRequestInfo nsri) {
    if (mLiveRequestInfo == null) {  // ← 检查是否有搜网正在进行
        mLiveRequestInfo = nsri;
        
        // 在指定的 Phone 上开始搜网
        nsri.mPhone.startNetworkScan(nsri.getRequest(),
                mHandler.obtainMessage(EVENT_START_NETWORK_SCAN_DONE, nsri));
        
        // 注册 Modem 重置和无线不可用事件
        nsri.mPhone.mCi.registerForModemReset(mHandler, EVENT_MODEM_RESET, nsri);
        nsri.mPhone.mCi.registerForNotAvailable(mHandler, EVENT_RADIO_UNAVAILABLE, nsri);
        
        return true;
    }
    return false;  // 已有搜网进行中,本次搜网进入 mPendingRequestInfo
}

💡 双卡搜网的完整调用链

App / System (Settings)
  ↓
TelephonyManager.startNetworkScan(phone1, request)
  ↓
PhoneInterfaceManager.startNetworkScan()
  ↓
NetworkScanRequestTracker.startNetworkScan()
  ↓
mHandler.sendMessage(CMD_START_NETWORK_SCAN)
  ↓
NetworkScanRequestScheduler.doStartScan()
  ├─ interruptLiveScan()   // 检查是否可中断
  ├─ cacheScan()           // 缓存或排队
  └─ startNewScan()        // 开始搜网 ← 核心调度
     └─ phone.startNetworkScan(request)
        └─ GsmCdmaPhone.startNetworkScan()
           └─ mCi.startNetworkScan()  ← RIL
              └─ RadioNetworkProxy.startNetworkScan()
                 └─ IRadioNetwork.startNetworkScan()  ← HAL
                    └─ Modem AT 命令 (AT+COPS=?)

🎯 双卡搜网的关键特点

1. 单个 SearchRequest 只能在一个 Phone 上执行

  • 搜网请求通过 NetworkScanRequestInfo.mPhone 指定目标 SIM

2. 不同的优先级策略

  • System UID 的搜网可以中断其他应用的搜网

3. 搜网结果回调

// 搜网完成后,通过 Messenger 回调给请求者
nsri.mMessenger.send(resultMessage);

4. 多个搜网请求的处理

// 同时只能有 1 个 Live 搜网 + 1 个 Pending 搜网
mLiveRequestInfo      // 正在进行的
mPendingRequestInfo   // 等待的

📌 总结

双卡搜网的核心逻辑:

  1. 每个 SIM 卡独立有 ServiceStateTracker - 负责各自的搜网
  2. 全局搜网管理器 NetworkScanRequestTracker - 调度多个搜网请求
  3. 优先级和排队机制 - System 应用优先级高,其他请求排队
  4. Phone 对象指定 - 每个搜网请求明确指定在哪个 Phone(SIM)上执行
  5. 结果通知 - 搜网完成后通过 Messenger 异步回调