本篇文档全面描述 Android 16 CellBroadcast 小区广播架构,包括消息接收流程、频道配置管理、紧急告警(ETWS/CMAS)处理机制,完全依据当前仓库代码。
1. 概述与核心概念
1.1 什么是小区广播(CellBroadcast)
小区广播(CB,Cell Broadcast)是一种点对多点的消息服务,允许网络运营商同时向小区内的所有移动设备发送文本消息。与普通 SMS 不同(点对点),小区广播是一次性向所有设备发送消息,用于:
- 公众告警系统(PWS):紧急地震、海啸、气象灾害等告警
- ETWS(Earthquake and Tsunami Warning System,日本地震/海啸预警)
- CMAS(Commercial Mobile Alert Service,美国商业移动告警,含总统级告警、极端威胁、安珀警报等)
- KPAS(韩国公共告警系统)
- EU-Alert(欧盟告警系统)
- 小区信息广播:当前小区名称显示(如巴西要求待机界面显示小区名)
- 资讯广播:新闻、天气、股票等商业资讯
1.2 3GPP 与 3GPP2 标准
| 标准 | 格式常量 | 说明 |
|---|---|---|
| 3GPP (GSM/UMTS) | SmsCbMessage.MESSAGE_FORMAT_3GPP = 1 |
依据 TS 23.041。PDU 格式分 GSM(88字节)和 UMTS(90~1252字节) |
| 3GPP2 (CDMA) | SmsCbMessage.MESSAGE_FORMAT_3GPP2 = 2 |
依据 C.R1001-G。使用 Bearer Data + Service Category |
1.3 关键架构原则:旁路接收模型
CellBroadcast 的接收路径与普通 SMS 完全分离,不经过 InboundSmsHandler 状态机:
RIL (Modem) ─ (BroadcastSms) ─> CellBroadcastServiceManager ─ (Binder IPC) ─> CellBroadcastService APK
Google 设计这一架构的核心原因是:CB 消息的优先级远高于普通 SMS,特别是紧急告警消息需要在毫秒级内完成处理并通知用户,不能阻塞在 SMS 状态机队列中。
2. 核心类与源码锚点
2.1 Framework 层
| 类名 | 路径 | 角色 |
|---|---|---|
| [CellBroadcastServiceManager.java](file:///d:/Resource%20Android%2016/android-16/frameworks/opt/telephony/src/java/com/android/internal/telephony/CellBroadcastServiceManager.java) | frameworks/opt/telephony |
CB 服务管理器,绑定 CellBroadcastService APK,转发 CB 消息 |
| [CellBroadcastConfigTracker.java](file:///d:/Resource%20Android%2016/android-16/frameworks/opt/telephony/src/java/com/android/internal/telephony/CellBroadcastConfigTracker.java) | frameworks/opt/telephony |
CB 配置追踪器(StateMachine),管理频道 ID 范围的启用/禁用 |
| [SmsCbMessage.java](file:///d:/Resource%20Android%2016/android-16/frameworks/base/telephony/java/android/telephony/SmsCbMessage.java) | frameworks/base/telephony |
小区广播消息的统一数据模型 |
| [CellBroadcastService.java](file:///d:/Resource%20Android%2016/android-16/frameworks/base/telephony/java/android/telephony/CellBroadcastService.java) | frameworks/base/telephony |
CB 服务的抽象基类,定义与 APK 的 Binder 接口 |
| [CellBroadcastIdRange.java](file:///d:/Resource%20Android%2016/android-16/frameworks/base/telephony/java/android/telephony/CellBroadcastIdRange.java) | frameworks/base/telephony |
频道 ID 范围数据结构 |
2.2 RIL 层
| 类/方法 | 路径 | 角色 |
|---|---|---|
| [MessagingIndication.java](file:///d:/Resource%20Android%2016/android-16/frameworks/opt/telephony/src/java/com/android/internal/telephony/MessagingIndication.java) | frameworks/opt/telephony |
接收来自 Modem 的 newBroadcastSms 回调 |
| [RadioIndication.java](file:///d:/Resource%20Android%2016/android-16/frameworks/opt/telephony/src/java/com/android/internal/telephony/RadioIndication.java) | frameworks/opt/telephony |
HIDL 版本广播短信回调 |
| [CommandsInterface.java](file:///d:/Resource%20Android%2016/android-16/frameworks/opt/telephony/src/java/com/android/internal/telephony/CommandsInterface.java) | frameworks/opt/telephony |
定义 setOnNewGsmBroadcastSms()、setGsmBroadcastConfig() 等 CB 配置接口 |
2.3 辅助与数据结构类
| 类名 | 路径 | 角色 |
|---|---|---|
| [SmsBroadcastConfigInfo.java](file:///d:/Resource%20Android%2016/android-16/frameworks/base/telephony/java/com/android/internal/telephony/gsm/SmsBroadcastConfigInfo.java) | frameworks/base/telephony |
GSM CB 频道配置项(fromServiceId/toServiceId + fromCodeScheme/toCodeScheme) |
| [CdmaSmsBroadcastConfigInfo.java](file:///d:/Resource%20Android%2016/android-16/frameworks/opt/telephony/src/java/com/android/internal/telephony/cdma/CdmaSmsBroadcastConfigInfo.java) | frameworks/opt/telephony |
CDMA CB 频道配置项(fromServiceCategory/toServiceCategory + language) |
| [SmsCbHeader.java](file:///d:/Resource%20Android%2016/android-16/frameworks/base/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java) | frameworks/base/telephony |
3GPP CB 消息头解析器(地理范围、序列号、消息ID、DCS) |
| [SmsCbEtwsInfo.java](file:///d:/Resource%20Android%2016/android-16/frameworks/base/telephony/java/android/telephony/SmsCbEtwsInfo.java) | frameworks/base/telephony |
ETWS 告警信息载体 |
| [SmsCbCmasInfo.java](file:///d:/Resource%20Android%2016/android-16/frameworks/base/telephony/java/android/telephony/SmsCbCmasInfo.java) | frameworks/base/telephony |
CMAS 告警信息载体 |
| [SmsCbConstants.java](file:///d:/Resource%20Android%2016/android-16/frameworks/base/telephony/java/com/android/internal/telephony/gsm/SmsCbConstants.java) | frameworks/base/telephony |
CB 消息 ID 常量定义(ETWS=0x1100,CMAS=0x1112 起) |
3. 架构全景图
4. 消息接收流程
4.1 从 Modem 到 RIL
当 Modem 收到小区广播消息时,通过 HAL Messaging Service 回调 newBroadcastSms() 方法。
GSM 网络下(源码 [MessagingIndication.java:L86-L97](file:///d:/Resource%20Android%2016/android-16/frameworks/opt/telephony/src/java/com/android/internal/telephony/MessagingIndication.java#L86-L97)):
/**
* @param data If received from GSM network, "data" is byte array of 88 bytes which indicates
* each page of a CBS Message sent to the MS by the BTS as coded in 3GPP 23.041 Section
* 9.4.1.2. If received from UMTS network, "data" is byte array of 90 up to 1252 bytes
* which contain between 1 and 15 CBS Message pages sent as one packet to the MS by the
* BTS as coded in 3GPP 23.041 Section 9.4.2.2
*/
public void newBroadcastSms(int indicationType, byte[] data) {
mRil.processIndication(HAL_SERVICE_MESSAGING, indicationType);
if (mRil.isLogOrTrace()) {
mRil.unsljLogvRet(RIL_UNSOL_RESPONSE_NEW_BROADCAST_SMS, IccUtils.bytesToHexString(data));
}
if (mRil.mGsmBroadcastSmsRegistrant != null) {
mRil.mGsmBroadcastSmsRegistrant.notifyRegistrant(new AsyncResult(null, data, null));
}
}
Google设计技巧 #1 — “Direct Registrant 模式”:
CB 消息不走 SMS 状态机,而是通过mGsmBroadcastSmsRegistrant直接通知注册者。这是一个单一回调的 Registrant 而非 RegistrantList,因为 CB 消息只需要通知一个目标(CellBroadcastServiceManager),且要求最快通路。
4.2 CellBroadcastServiceManager — 服务绑定管理器
CellBroadcastServiceManager 是一个 per-Phone 的单例管理器,核心职责是:
- 绑定系统
CellBroadcastServiceAPK - 接收来自 RIL 的 CB 消息并转发给 APK
- 支持 OEM 禁用 CB 消息
关键代码(源码 [CellBroadcastServiceManager.java:L155-L260](file:///d:/Resource%20Android%2016/android-16/frameworks/opt/telephony/src/java/com/android/internal/telephony/CellBroadcastServiceManager.java#L155-L260)):
private void initCellBroadcastServiceModule() {
mEnabled = true;
if (sServiceConnection == null) {
sServiceConnection = new CellBroadcastServiceConnection();
}
mCellBroadcastServicePackage = getCellBroadcastServicePackage();
if (mCellBroadcastServicePackage != null) {
mModuleCellBroadcastHandler = new Handler() {
@Override
public void handleMessage(@NonNull Message msg) {
if (!mEnabled) {
return;
}
if (sServiceConnection.mService == null) {
return;
}
try {
ICellBroadcastService cellBroadcastService =
ICellBroadcastService.Stub.asInterface(
sServiceConnection.mService);
if (msg.what == EVENT_NEW_GSM_SMS_CB) {
cellBroadcastService.handleGsmCellBroadcastSms(
mPhone.getPhoneId(),
(byte[]) ((AsyncResult) msg.obj).result);
} else if (msg.what == EVENT_NEW_CDMA_SMS_CB) {
SmsMessage sms = (SmsMessage) msg.obj;
cellBroadcastService.handleCdmaCellBroadcastSms(
mPhone.getPhoneId(),
sms.getEnvelopeBearerData(),
sms.getEnvelopeServiceCategory());
} else if (msg.what == EVENT_NEW_CDMA_SCP_MESSAGE) {
// ... SCP (Service Category Program) 处理
}
} catch (RemoteException e) {
mContext.unbindService(sServiceConnection);
sServiceConnection = null;
}
}
};
Intent intent = new Intent(CellBroadcastService.CELL_BROADCAST_SERVICE_INTERFACE);
intent.setPackage(mCellBroadcastServicePackage);
mContext.bindService(intent, sServiceConnection, Context.BIND_AUTO_CREATE);
mPhone.mCi.setOnNewGsmBroadcastSms(mModuleCellBroadcastHandler,
EVENT_NEW_GSM_SMS_CB, null);
}
}
Google设计技巧 #2 — “Per-Phone 管理器 + Shared Connection” 模式:
sServiceConnection是静态变量,意味着多个CellBroadcastServiceManager实例(多 SIM)共享同一个 Binder 连接。但mModuleCellBroadcastHandler是 per-instance 的,每个 Handler 处理自己 Phone 的消息并通过mPhone.getPhoneId()区分。
4.3 CDMA 消息的特殊路径
CDMA CB 消息不走 GSM 广播消息单独回调,而是通过正常的 SMS 回调路径进入,在 CdmaInboundSmsHandler.dispatchMessageRadioSpecific() 中根据 SmsEnvelope.MESSAGE_TYPE_BROADCAST 类型检测并转发:
@Override
protected int dispatchMessageRadioSpecific(SmsMessageBase smsb, @SmsSource int smsSource,
int token) {
SmsMessage sms = (SmsMessage) smsb;
boolean isBroadcastType = (SmsEnvelope.MESSAGE_TYPE_BROADCAST == sms.getMessageType());
// 处理 CMAS 紧急广播消息
if (isBroadcastType) {
log("Broadcast type message");
mCellBroadcastServiceManager.sendCdmaMessageToHandler(sms);
return Intents.RESULT_SMS_HANDLED;
}
// ... 后续普通 SMS 处理
}
此外还有 CDMA SCP(Service Category Program)消息,通过 TELESERVICE_SCPT 检测并转发:
case SmsEnvelope.TELESERVICE_SCPT:
mCellBroadcastServiceManager.sendCdmaScpMessageToHandler(sms, mScpCallback);
return Intents.RESULT_SMS_HANDLED;
Google设计技巧 #3 — "多入一出"的统一出口:
无论CB消息是通过GSM独立通道还是CDMA SMS通道进入Framework,最终都汇聚到CellBroadcastServiceManager的 Handler,再通过 Binder 转发给同一个CellBroadcastServiceAPK。
4.4 OEM 禁用 CB 消息
系统提供 OEM 覆盖机制 config_disable_all_cb_messages(源码 [CellBroadcastServiceManager.java:L80-L88](file:///d:/Resource%20Android%2016/android-16/frameworks/opt/telephony/src/java/com/android/internal/telephony/CellBroadcastServiceManager.java#L80-L88)):
private boolean cbMessagesDisabledByOem() {
if (mContext != null && mContext.getResources() != null) {
return mContext.getResources().getBoolean(
com.android.internal.R.bool.config_disable_all_cb_messages);
} else {
return false;
}
}
在每个 sendXxxMessageToHandler 方法入口处检查此标志,若为 true 则直接丢弃消息并记录统计日志。
Google设计技巧 #4 — “OEM 过滤点 + 统计埋点”:
在每个消息入口点(而非发送后)进行 OEM 过滤,最大化减少不必要的 IPC 开销。同时通过CellBroadcastStatsLog记录过滤统计,方便 OEM 和运营商排查问题。
5. CellBroadcastService APK — 插件化架构
5.1 服务发现机制
CellBroadcastServiceManager 通过 PackageManager.queryIntentServices() 查找系统中实现了 CellBroadcastService.CELL_BROADCAST_SERVICE_INTERFACE 接口的服务:
private String getCellBroadcastServicePackage() {
PackageManager packageManager = mContext.getPackageManager();
List<ResolveInfo> cbsPackages = packageManager.queryIntentServices(
new Intent(CellBroadcastService.CELL_BROADCAST_SERVICE_INTERFACE),
PackageManager.MATCH_SYSTEM_ONLY);
// ...
for (ResolveInfo info : cbsPackages) {
if (packageManager.checkPermission(
Manifest.permission.READ_PRIVILEGED_PHONE_STATE,
packageName) == PackageManager.PERMISSION_GRANTED) {
return packageName;
}
}
return null;
}
Google设计技巧 #5 — “可替换的 CB 服务”:
通过Intent+queryIntentServices发现机制,OEM 可以实现自己的CellBroadcastService子类来替换 AOSP 的默认实现,只需声明相同的 intent filter 和权限。这是一种依赖注入模式,实现了 Framework 与 APK 的解耦。
5.2 CellBroadcastService 抽象类
[CellBroadcastService.java](file:///d:/Resource%20Android%2016/android-16/frameworks/base/telephony/java/android/telephony/CellBroadcastService.java) 是一个抽象 Service,定义了必须实现的接口:
@SystemApi
public abstract class CellBroadcastService extends Service {
public static final String CELL_BROADCAST_SERVICE_INTERFACE =
"android.telephony.CellBroadcastService";
private final ICellBroadcastService.Stub mStubWrapper;
/** 处理 GSM/UMTS CB 消息 */
public abstract void onGsmCellBroadcastSms(int slotIndex, @NonNull byte[] message);
/** 处理 CDMA CB 消息(已弃用) */
@Deprecated
public void onCdmaCellBroadcastSms(int slotIndex, @NonNull byte[] bearerData,
@CdmaSmsCbProgramData.Category int serviceCategory) {
}
/** 处理 CDMA SCP 消息(已弃用) */
@Deprecated
public void onCdmaScpMessage(int slotIndex,
@NonNull List<CdmaSmsCbProgramData> smsCbProgramData,
@NonNull String originatingAddress, @NonNull Consumer<Bundle> callback) {
}
/** 获取小区广播区域信息(显示在 SIM 状态页面) */
@WorkerThread
public abstract @NonNull CharSequence getCellBroadcastAreaInfo(int slotIndex);
}
内部类 ICellBroadcastServiceWrapper 继承自 AIDL 生成的 ICellBroadcastService.Stub,将 Binder 调用转发给抽象方法。
Google设计技巧 #6 — “Wrapper 模式封装 AIDL”:
AIDL 的 Stub 类作为内部类,对外暴露抽象方法,使得子类不需要直接处理 Binder IPC 细节。这是一种 Adapter 模式,AIDL 接口负责跨进程通信,抽象方法负责业务逻辑。
5.3 CellBroadcastReceiver — 广播接收器
CellBroadcastService APK 内部通常包含一个 CellBroadcastReceiver 组件,负责监听两个 Intent:
| Intent | 含义 | 权限要求 |
|---|---|---|
SMS_CB_RECEIVED_ACTION |
非紧急 CB 消息到达 | RECEIVE_SMS |
ACTION_SMS_EMERGENCY_CB_RECEIVED |
紧急 CB 消息到达(ETWS/CMAS 等) | 仅系统应用可接收 |
紧急消息之所以需要独立 Intent,是为了防止恶意或劣质第三方应用干扰告警消息的即时显示和音频/振动提醒。
6. SmsCbMessage — CB 消息统一数据模型
6.1 消息格式区分
SmsCbMessage(源码 [SmsCbMessage.java](file:///d:/Resource%20Android%2016/android-16/frameworks/base/telephony/java/android/telephony/SmsCbMessage.java))是统一了 3GPP 和 3GPP2 两种格式的消息对象:
@SystemApi
public final class SmsCbMessage implements Parcelable {
/** 消息格式:3GPP 或 3GPP2 */
private final int mMessageFormat; // MESSAGE_FORMAT_3GPP=1, MESSAGE_FORMAT_3GPP2=2
/** 地理范围:Cell-wide(0)/PLMN-wide(1)/LA-wide(2)/Cell-wide(3) */
private final int mGeographicalScope;
/** 序列号(CDMA 的 Message ID,GSM 的 GS+Message Code+Update Number) */
private final int mSerialNumber;
/** 位置标识(MCC/MNC + LAC + Cell ID) */
private final SmsCbLocation mLocation;
/** 服务类别(3GPP Message Identifier = CDMA Service Category) */
private final int mServiceCategory;
/** 消息语言(如 "en", "ja") */
private final String mLanguage;
/** 消息体 */
private final String mBody;
/** 消息优先级:Normal(0)/Interactive(1)/Urgent(2)/Emergency(3) */
private final int mPriority;
/** ETWS 告警信息 */
private final SmsCbEtwsInfo mEtwsWarningInfo;
/** CMAS 告警信息 */
private final SmsCbCmasInfo mCmasWarningInfo;
/** Geo-Fencing 最大等待时间(秒) */
private final int mMaximumWaitTimeSec;
/** CMAS 告警区域坐标 */
private final List<Geometry> mGeometries;
}
Google设计技巧 #7 — “统一数据模型 + 可选字段”:
将 3GPP 和 3GPP2 两种完全不同标准的字段统一到一个Parcelable中。某些字段(如mEtwsWarningInfo、mCmasWarningInfo)只在特定消息类型中有值,通过 nullable 处理。
6.2 消息优先级
| 常量 | 值 | 含义 |
|---|---|---|
MESSAGE_PRIORITY_NORMAL |
0 | 普通消息(如新闻、天气) |
MESSAGE_PRIORITY_INTERACTIVE |
1 | 交互消息 |
MESSAGE_PRIORITY_URGENT |
2 | 紧急消息 |
MESSAGE_PRIORITY_EMERGENCY |
3 | 最高优先级紧急消息 |
6.3 地理范围
| 常量 | 值 | 含义 |
|---|---|---|
GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE |
0 | 小区范围内立即显示 |
GEOGRAPHICAL_SCOPE_PLMN_WIDE |
1 | PLMN 范围内(所有 CDMA 广播默认) |
GEOGRAPHICAL_SCOPE_LOCATION_AREA_WIDE |
2 | 位置区/服务区范围 |
GEOGRAPHICAL_SCOPE_CELL_WIDE |
3 | 小区范围 |
7. ETWS 与 CMAS 紧急告警
7.1 ETWS(日本地震/海啸预警)
ETWS 消息由 SmsCbEtwsInfo 承载(源码 [SmsCbEtwsInfo.java](file:///d:/Resource%20Android%2016/android-16/frameworks/base/telephony/java/android/telephony/SmsCbEtwsInfo.java)):
public final class SmsCbEtwsInfo implements Parcelable {
public static final int ETWS_WARNING_TYPE_EARTHQUAKE = 0x00;
public static final int ETWS_WARNING_TYPE_TSUNAMI = 0x01;
public static final int ETWS_WARNING_TYPE_EARTHQUAKE_AND_TSUNAMI = 0x02;
public static final int ETWS_WARNING_TYPE_TEST_MESSAGE = 0x03;
public static final int ETWS_WARNING_TYPE_OTHER_EMERGENCY = 0x04;
private final int mWarningType; // 告警类型
private final boolean mIsEmergencyUserAlert; // 是否触发振动/铃声
private final boolean mIsPopupAlert; // 是否弹出告警窗口
private final boolean mIsPrimary; // 是否 Primary Notification
private final byte[] mWarningSecurityInformation; // 50字节安全信息
}
ETWS 分为两类消息:
- Primary Notification(主通知):仅含告警类型和安全信息,6 字节,需在 4 秒内递交给 UE
- Secondary Notification(辅助通知):含完整告警文本内容
消息 ID 范围(源码 [SmsCbConstants.java](file:///d:/Resource%20Android%2016/android-16/frameworks/base/telephony/java/com/android/internal/telephony/gsm/SmsCbConstants.java)):
| 消息 ID | 含义 |
|---|---|
0x1100 |
地震告警 |
0x1101 |
海啸告警 |
0x1102 |
地震+海啸联合告警 |
0x1103 |
测试消息 |
0x1104 |
其他紧急类型 |
7.2 CMAS(美国商业移动告警)
CMAS 由 SmsCbCmasInfo 承载(源码 [SmsCbCmasInfo.java](file:///d:/Resource%20Android%2016/android-16/frameworks/base/telephony/java/android/telephony/SmsCbCmasInfo.java)):
public final class SmsCbCmasInfo implements Parcelable {
// CMAS 消息类别
public static final int CMAS_CLASS_PRESIDENTIAL_LEVEL_ALERT = 0x00;
public static final int CMAS_CLASS_EXTREME_THREAT = 0x01;
public static final int CMAS_CLASS_SEVERE_THREAT = 0x02;
public static final int CMAS_CLASS_CHILD_ABDUCTION_EMERGENCY = 0x03; // AMBER Alert
public static final int CMAS_CLASS_REQUIRED_MONTHLY_TEST = 0x04;
public static final int CMAS_CLASS_CMAS_EXERCISE = 0x05;
// CMAS 告警类别
public static final int CMAS_CATEGORY_GEO = 0x00; // 地质灾害
public static final int CMAS_CATEGORY_MET = 0x01; // 气象灾害
public static final int CMAS_CATEGORY_SAFETY = 0x02; // 公共安全
public static final int CMAS_CATEGORY_SECURITY = 0x03; // 执法/军事/安全
public static final int CMAS_CATEGORY_RESCUE = 0x04; // 救援
public static final int CMAS_CATEGORY_FIRE = 0x05; // 消防
public static final int CMAS_CATEGORY_HEALTH = 0x06; // 医疗/公共卫生
public static final int CMAS_CATEGORY_ENV = 0x07; // 环境污染
public static final int CMAS_CATEGORY_TRANSPORT = 0x08; // 交通
public static final int CMAS_CATEGORY_INFRA = 0x09; // 基础设施
public static final int CMAS_CATEGORY_CBRNE = 0x0a; // 化生辐射核爆
public static final int CMAS_CATEGORY_OTHER = 0x0b; // 其他
// CMAS 响应类型
public static final int CMAS_RESPONSE_TYPE_SHELTER = 0x00; // 就地避难
public static final int CMAS_RESPONSE_TYPE_EVACUATE = 0x01; // 撤离
}
CMAS 消息 ID 范围:0x1112 ~ 0x112E
6292



被折叠的 条评论
为什么被折叠?



