一、背景
Android R及以上版本,必须集成Google Cellbroadcast。 CellBroadcast模块由两部分组成:
CellBroadcastService 此服务支持CellBroadcast SMS解码,无线紧急警报(WEA)3.0的地理围栏,消息重复检查以及向应用程序广播消息。CellBroadcastReceiver: 默认系统应用程序,用于处理紧急/非紧急警报,并根据运营商和区域法规向最终用户显示信息。
CellBroadcastService和CellBroadcastReceiver应用程序包含在单个APEX文件(com.google.android.cellbroadcast)中,OEMs无法修改。但是OEMs可以使用runtime resource overlays覆盖(RRO)自定义配置。 在Android R中,CellBroadcast模块在package/app/CellBroadcastReceiver中包含代码,并将现有框架类迁移到package/modules/CellBroadcastService。也就是说,可以在这两个部分查看代码,修改无效。
二、模块介绍
小区广播(CB)是一对多的地理定位和地理防御消息服务,旨在将消息同时发送到定义区域中的多个移动电话用户。 当警报信息到来,设备会立即弹出警报框:
三、CellBroadcast消息流程
1)无线电接口层(RIL)向InBoundSMSHandler通知CDMA / GSM CellBroadcast SMS。 2)framework将CellBroadcast SMS转发到CBS模块,以解析和处理传入的消息。 3)处理完消息后,CellBroadcastService会将intent转发到系统默认的CellBroadcastReceiver应用程序。 4)CellBroadcastReceiver应用程序将消息显示给用户。
四、CellBroadcast RRO
OEMs无法直接修改源码,但是可以参考RROSampleTestApp使用runtime resource overlays覆盖(RRO)自定义配置(仅overlayable.xml文件中定义的)。 注意事项:必须设置targetPackage为“com.google.android.cellbroadcastreceiver”
五、CellBroadcast Settings解析
<?xml version
="1.0" encoding
="utf-8"?>
<PreferenceScreen
xmlns
:android
="http://schemas.android.com/apk/res/android"
xmlns
:app
="http://schemas.android.com/apk/res-auto">
<Preference android
:key
="alerts_header"
android
:summary
="@string/alerts_header_summary"
android
:selectable
="false" />
<!-- Allow alerts
-->
<SwitchPreference android
:defaultValue
="@bool/master_toggle_enabled_default"
android
:key
="enable_alerts_master_toggle"
android
:summary
="@string/enable_alerts_master_toggle_summary"
android
:title
="@string/enable_alerts_master_toggle_title" />
<!--Alerts
-->
<PreferenceCategory android
:title
="@string/emergency_alerts_title"
android
:key
="category_emergency_alerts">
<!-- Emergency alerts
-->
<SwitchPreference android
:defaultValue
="@bool/emergency_alerts_enabled_default"
android
:key
="enable_emergency_alerts"
android
:summary
="@string/enable_emergency_alerts_message_summary"
android
:title
="@string/enable_emergency_alerts_message_title" />
<!-- Presidential alerts
-->
<SwitchPreference android
:defaultValue
="true"
android
:enabled
="false"
android
:key
="enable_cmas_presidential_alerts"
android
:summary
="@string/enable_cmas_presidential_alerts_summary"
android
:title
="@string/enable_cmas_presidential_alerts_title"/>
<!-- Extreme threats
-->
<SwitchPreference android
:defaultValue
="@bool/extreme_threat_alerts_enabled_default"
android
:key
="enable_cmas_extreme_threat_alerts"
android
:summary
="@string/enable_cmas_extreme_threat_alerts_summary"
android
:title
="@string/enable_cmas_extreme_threat_alerts_title" />
<!-- Severe threats
-->
<SwitchPreference android
:defaultValue
="@bool/severe_threat_alerts_enabled_default"
android
:key
="enable_cmas_severe_threat_alerts"
android
:summary
="@string/enable_cmas_severe_threat_alerts_summary"
android
:title
="@string/enable_cmas_severe_threat_alerts_title" />
<!-- AMBER alerts
-->
<SwitchPreference android
:defaultValue
="@bool/amber_alerts_enabled_default"
android
:key
="enable_cmas_amber_alerts"
android
:summary
="@string/enable_cmas_amber_alerts_summary"
android
:title
="@string/enable_cmas_amber_alerts_title" />
<!-- Public safety messages
-->
<SwitchPreference android
:defaultValue
="@bool/public_safety_messages_enabled_default"
android
:key
="enable_public_safety_messages"
android
:summary
="@string/enable_public_safety_messages_summary"
android
:title
="@string/enable_public_safety_messages_title" />
<!-- State and local tests
-->
<SwitchPreference android
:defaultValue
="@bool/state_local_test_alerts_enabled_default"
android
:key
="enable_state_local_test_alerts"
android
:summary
="@string/enable_state_local_test_alerts_summary"
android
:title
="@string/enable_state_local_test_alerts_title" />
<!-- Test alerts
-->
<SwitchPreference android
:defaultValue
="@bool/test_alerts_enabled_default"
android
:key
="enable_test_alerts"
android
:summary
="@string/enable_cmas_test_alerts_summary"
android
:title
="@string/enable_cmas_test_alerts_title" />
<!-- Area update broadcasts
-->
<!-- Default value is
true for Brazil and India
. This preference is ignored and hidden
unless the
boolean "config_showAreaUpdateInfoSettings" is set to
true in the global resource
. -->
<SwitchPreference android
:defaultValue
="@bool/area_update_info_alerts_enabled_default"
android
:key
="enable_area_update_info_alerts"
android
:summary
="@string/enable_area_update_info_alerts_summary"
android
:title
="@string/enable_area_update_info_alerts_title" />
<!-- Emergency alert history
-->
<Preference android
:key
="emergency_alert_history"
android
:title
="@string/emergency_alert_history_title" />
</PreferenceCategory
>
<!-- Alert preferences
-->
<PreferenceCategory android
:title
="@string/alert_preferences_title"
android
:key
="category_alert_preferences">
<!-- Vibration
-->
<SwitchPreference android
:defaultValue
="true"
android
:key
="enable_alert_vibrate"
android
:title
="@string/enable_alert_vibrate_title" />
<!-- Alert reminder
-->
<ListPreference android
:key
="alert_reminder_interval"
android
:title
="@string/alert_reminder_interval_title"
android
:entries
="@array/alert_reminder_interval_entries"
android
:entryValues
="@array/alert_reminder_interval_values"
android
:defaultValue
="@string/alert_reminder_interval_in_min_default"
android
:dialogTitle
="@string/alert_reminder_dialog_title" />
<!-- Show additional language on
/off
switch in settings
-->
<SwitchPreference android
:defaultValue
="false"
android
:key
="receive_cmas_in_second_language"
android
:summary
="@string/receive_cmas_in_second_language_summary"
android
:title
="@string/receive_cmas_in_second_language_title" />
<!-- Always alert at full volume
-->
<SwitchPreference android
:defaultValue
="@bool/override_dnd_default"
android
:key
="override_dnd"
android
:summary
="@string/override_dnd_summary"
android
:title
="@string/override_dnd_title" />
</PreferenceCategory
>
</PreferenceScreen
>
1、Allow alerts
<SwitchPreference android
:defaultValue
="@bool/master_toggle_enabled_default"
android
:key
="enable_alerts_master_toggle"
android
:summary
="@string/enable_alerts_master_toggle_summary"
android
:title
="@string/enable_alerts_master_toggle_title" />
用于启用/禁用所有警报消息(默认启用,仅对Presidential alerts无效) 该控件无法隐藏
2、Presidential alerts
<SwitchPreference android
:defaultValue
="true"
android
:enabled
="false"
android
:key
="enable_cmas_presidential_alerts"
android
:summary
="@string/enable_cmas_presidential_alerts_summary"
android
:title
="@string/enable_cmas_presidential_alerts_title"/>
默认开启该channel但该控件隐藏。 OEM可在RRO中配置show_presidential_alerts_settings,设置是否显示该控件。
if (mPresidentialCheckBox
!= null
) {
mPresidentialCheckBox
.setVisible(
res
.getBoolean(R
.bool
.show_presidential_alerts_settings
));
}
默认控制channel为:gsm(4370, 4383)和cdma(4096)
3、Extreme threats
<SwitchPreference android
:defaultValue
="@bool/extreme_threat_alerts_enabled_default"
android
:key
="enable_cmas_extreme_threat_alerts"
android
:summary
="@string/enable_cmas_extreme_threat_alerts_summary"
android
:title
="@string/enable_cmas_extreme_threat_alerts_title" />
默认开启该channel。 该控件在show_extreme_alert_settings为true且cmas_alert_extreme_channels_range_strings不为null的情况下才显示。
if (mExtremeCheckBox
!= null
) {
mExtremeCheckBox
.setVisible(res
.getBoolean(R
.bool
.show_extreme_alert_settings
)
&& !channelManager
.getCellBroadcastChannelRanges(
R
.array
.cmas_alert_extreme_channels_range_strings
).isEmpty());
}
默认show_extreme_alert_settings为true。 默认控制channel为:gsm(4371~4372, 4384~4385)和cdma(4097)
4、Severe threats
<SwitchPreference android
:defaultValue
="@bool/severe_threat_alerts_enabled_default"
android
:key
="enable_cmas_severe_threat_alerts"
android
:summary
="@string/enable_cmas_severe_threat_alerts_summary"
android
:title
="@string/enable_cmas_severe_threat_alerts_title" />
默认开启该channel。 该控件在show_severe_alert_settings为true且cmas_alerts_severe_range_strings不为null的情况下才显示。
if (mSevereCheckBox
!= null
) {
mSevereCheckBox
.setVisible(res
.getBoolean(R
.bool
.show_severe_alert_settings
)
&& !channelManager
.getCellBroadcastChannelRanges(
R
.array
.cmas_alerts_severe_range_strings
).isEmpty());
}
默认show_severe_alert_settings为true。 默认控制channel为:gsm(4373~4378, 4386~4391)和cdma(4098)
5、AMBER alerts
<SwitchPreference android
:defaultValue
="@bool/amber_alerts_enabled_default"
android
:key
="enable_cmas_amber_alerts"
android
:summary
="@string/enable_cmas_amber_alerts_summary"
android
:title
="@string/enable_cmas_amber_alerts_title" />
默认开启该channel。 该控件在show_amber_alert_settings为true且cmas_amber_alerts_channels_range_strings不为null的情况下才显示。
if (mAmberCheckBox
!= null
) {
mAmberCheckBox
.setVisible(res
.getBoolean(R
.bool
.show_amber_alert_settings
)
&& !channelManager
.getCellBroadcastChannelRanges(
R
.array
.cmas_amber_alerts_channels_range_strings
).isEmpty());
}
默认show_amber_alert_settings为true。 默认控制channel为:gsm(4379, 4392)和cdma(4099)
6、Public safety messages
<SwitchPreference android
:defaultValue
="@bool/public_safety_messages_enabled_default"
android
:key
="enable_public_safety_messages"
android
:summary
="@string/enable_public_safety_messages_summary"
android
:title
="@string/enable_public_safety_messages_title" />
默认开启该channel。 该控件在show_public_safety_settings为true且public_safety_messages_channels_range_strings不为null的情况下才显示。
if (mPublicSafetyMessagesChannelCheckBox
!= null
) {
mPublicSafetyMessagesChannelCheckBox
.setVisible(
res
.getBoolean(R
.bool
.show_public_safety_settings
)
&& !channelManager
.getCellBroadcastChannelRanges(
R
.array
.public_safety_messages_channels_range_strings
)
.isEmpty());
}
默认show_public_safety_settings为true。 默认控制channel为null。 MCCMNC不同,public_safety_messages_channels_range_strings值不同。mcc310的情况下,控制channel为:gsm(4096, 4097)
7、State and local tests
<SwitchPreference android
:defaultValue
="@bool/state_local_test_alerts_enabled_default"
android
:key
="enable_state_local_test_alerts"
android
:summary
="@string/enable_state_local_test_alerts_summary"
android
:title
="@string/enable_state_local_test_alerts_title" />
默认不开启该channel。 该控件在show_state_local_test_settings为true且state_local_test_alert_range_strings不为null的情况下才显示。
if (mStateLocalTestCheckBox
!= null
) {
mStateLocalTestCheckBox
.setVisible(
res
.getBoolean(R
.bool
.show_state_local_test_settings
)
&& !channelManager
.getCellBroadcastChannelRanges(
R
.array
.state_local_test_alert_range_strings
).isEmpty());
}
默认show_state_local_test_settings为true。 默认控制channel为null。 MCCMNC不同,state_local_test_alert_range_strings值不同。mcc310的情况下,控制channel为:gsm(4098, 4099)
8、Test alerts
<SwitchPreference android
:defaultValue
="@bool/test_alerts_enabled_default"
android
:key
="enable_test_alerts"
android
:summary
="@string/enable_cmas_test_alerts_summary"
android
:title
="@string/enable_cmas_test_alerts_title" />
默认不开启该channel。 该控件是否显示由isTestAlertsToggleVisible(Context context)控制。
if (mTestCheckBox
!= null
) {
mTestCheckBox
.setVisible(isTestAlertsToggleVisible(getContext()));
}
当(show_test_settings为true或isTestingMode(context))且(required_monthly_test_range_strings或exercise_alert_range_strings或operator_defined_alert_range_strings或etws_test_alerts_range_strings不为null)时显示。
public static boolean isTestAlertsToggleVisible(Context context
) {
CellBroadcastChannelManager channelManager
= new CellBroadcastChannelManager(context
,
SubscriptionManager
.DEFAULT_SUBSCRIPTION_ID
);
Resources res
= CellBroadcastSettings
.getResources(context
,
SubscriptionManager
.DEFAULT_SUBSCRIPTION_ID
);
boolean isTestAlertsAvailable
= !channelManager
.getCellBroadcastChannelRanges(
R
.array
.required_monthly_test_range_strings
).isEmpty()
|| !channelManager
.getCellBroadcastChannelRanges(
R
.array
.exercise_alert_range_strings
).isEmpty()
|| !channelManager
.getCellBroadcastChannelRanges(
R
.array
.operator_defined_alert_range_strings
).isEmpty()
|| !channelManager
.getCellBroadcastChannelRanges(
R
.array
.etws_test_alerts_range_strings
).isEmpty();
return (res
.getBoolean(R
.bool
.show_test_settings
)
|| CellBroadcastReceiver
.isTestingMode(context
))
&& isTestAlertsAvailable
;
}
默认show_test_settings为true 默认required_monthly_test_range_strings控制channel为:gsm(4380, 4393)和cdma(4100) 默认exercise_alert_range_strings控制channel为:gsm(4381, 4394) 默认operator_defined_alert_range_strings控制channel为:gsm(4382, 4395) 默认etws_test_alerts_range_strings控制channel为:gsm(4352~4354, 4356) isTestingMode(context)取决于SECRET_CODE
”ro.debuggable“为1即root版本或者allow_testing_mode_on_user_build为true时,可通过暗码”##CMAS##“启动。
else if (TelephonyManager
.ACTION_SECRET_CODE
.equals(action
)) {
if (SystemProperties
.getInt("ro.debuggable", 0) == 1
|| res
.getBoolean(R
.bool
.allow_testing_mode_on_user_build
)) {
setTestingMode(!isTestingMode(mContext
));
int msgId
= (isTestingMode(mContext
)) ? R
.string
.testing_mode_enabled
: R
.string
.testing_mode_disabled
;
String msg
= res
.getString(msgId
);
Toast
.makeText(mContext
, msg
, Toast
.LENGTH_SHORT
).show();
LocalBroadcastManager
.getInstance(mContext
)
.sendBroadcast(new Intent(ACTION_TESTING_MODE_CHANGED
));
log(msg
);
}
}
发送了一个本地广播
LocalBroadcastManager
.getInstance(mContext
)
.sendBroadcast(new Intent(ACTION_TESTING_MODE_CHANGED
));
广播接收
private final BroadcastReceiver mTestingModeChangedReeiver
= new BroadcastReceiver() {
@Override
public void onReceive(Context context
, Intent intent
) {
switch (intent
.getAction()) {
case CellBroadcastReceiver
.ACTION_TESTING_MODE_CHANGED
:
updatePreferenceVisibility();
break;
}
}
};
该控件是否显示还是由isTestAlertsToggleVisible(Context context)控制。
private void updatePreferenceVisibility() {
......
if (mTestCheckBox
!= null
) {
mTestCheckBox
.setVisible(isTestAlertsToggleVisible(getContext()));
}
......
}
例如: 插入卡MCCMNC为310260,且不为root版本时。 直接从Settings菜单看,show_test_settings为false,则再看isTestingMode(context)。可看到allow_testing_mode_on_user_build为true,isTestAlertsAvailable为true,则isTestAlertsToggleVisible(Context context)返回true,即该控件存在。 从暗码中看,虽不为root版本,但allow_testing_mode_on_user_build为true,固可通过暗码启动。
六、关于语言过滤
private boolean shouldDisplayMessage(SmsCbMessage message
) {
......
CellBroadcastChannelManager channelManager
= new CellBroadcastChannelManager(mContext
,
message
.getSubscriptionId());
CellBroadcastChannelRange range
= channelManager
.getCellBroadcastChannelRangeFromMessage(message
);
String messageLanguage
= message
.getLanguageCode();
if (range
!= null
&& range
.mFilterLanguage
) {
final String secondLanguageCode
= CellBroadcastSettings
.getResources(mContext
,
SubscriptionManager
.DEFAULT_SUBSCRIPTION_ID
)
.getString(R
.string
.emergency_alert_second_language_code
);
if (!secondLanguageCode
.isEmpty()) {
SharedPreferences prefs
= PreferenceManager
.getDefaultSharedPreferences(this);
boolean receiveInSecondLanguage
= prefs
.getBoolean(
CellBroadcastSettings
.KEY_RECEIVE_CMAS_IN_SECOND_LANGUAGE
, false);
if (!TextUtils
.isEmpty(messageLanguage
)
&& !secondLanguageCode
.equalsIgnoreCase(messageLanguage
)) {
Log
.w(TAG
, "Ignoring message in the unspecified second language:"
+ messageLanguage
);
return false;
} else if (!receiveInSecondLanguage
) {
Log
.d(TAG
, "Ignoring message in second language because setting is off");
return false;
}
} else {
String deviceLanguage
= Locale
.getDefault().getLanguage();
if (!TextUtils
.isEmpty(messageLanguage
)
&& !messageLanguage
.equalsIgnoreCase(deviceLanguage
)) {
Log
.d(TAG
, "ignoring the alert due to language mismatch. Message lang="
+ messageLanguage
+ ", device lang=" + deviceLanguage
);
return false;
}
}
}
......
return true;
}
range != null && range.mFilterLanguage才会进入语言过滤。 range.mFilterLanguage看如下中filter_language的值,默认false。
<item>0x111F:rat
=gsm
, emergency
=true, filter_language
=true</item
>
七、其他
1、添加启动icon
google有提供以下两个参数,用于通过其自己的启动icon来访问CellBroadcast消息历史记录。
<item type
="bool" name
="show_message_history_in_launcher" />
<item type
="mipmap" name
="ic_launcher_cell_broadcast" />
使用如下:
<?xml version
="1.0" encoding
="utf-8"?>
<resources>
<bool name
="show_message_history_in_launcher">true</bool
>
<mipmap name
="ic_launcher_cell_broadcast">@mipmap/ic_launcher_cell_broadcast
</mipmap
>
</resources
>