双卡搜网(DSDS - Dual SIM Dual Standby)搜网逻辑
📱 核心架构
双卡搜网涉及的主要模块:
- ServiceStateTracker - 每个 SIM 卡对应一个,负责该卡的搜网状态跟踪
- NetworkScanRequestTracker - 全局搜网请求管理器
- PhoneConfigurationManager - 多卡配置管理(DSDS/DSDA/TSTS)
- PhoneSwitcher - 多卡间切换和网络请求调度
- 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 // 等待的
📌 总结
双卡搜网的核心逻辑:
- 每个 SIM 卡独立有 ServiceStateTracker - 负责各自的搜网
- 全局搜网管理器 NetworkScanRequestTracker - 调度多个搜网请求
- 优先级和排队机制 - System 应用优先级高,其他请求排队
- Phone 对象指定 - 每个搜网请求明确指定在哪个 Phone(SIM)上执行
- 结果通知 - 搜网完成后通过 Messenger 异步回调