1.0、我们从frameworks/opt/telephony/src/java/com/android/internal/telephony/gsm/GsmCellBroadcastHandler.java makeGsmCellBroadcastHandler开始
/**
* Create a new CellBroadcastHandler.
* @param context the context to use for dispatching Intents
* @return the new handler
*/
public static GsmCellBroadcastHandler makeGsmCellBroadcastHandler(Context context,
PhoneBase phone) {
GsmCellBroadcastHandler handler = new GsmCellBroadcastHandler(context, phone);
handler.start();
return handler;
}
1.1 通过调用GsmCellBroadcastHandler对象 handler.start()调用handleSmsMessage
/**
* Handle 3GPP-format Cell Broadcast messages sent from radio.
*
* @param message the message to process
* @return true if an ordered broadcast was sent; false on failure
*/
@Override
protected boolean handleSmsMessage(Message message) {
if (message.obj instanceof AsyncResult) {
SmsCbMessage cbMessage = handleGsmBroadcastSms((AsyncResult) message.obj);
if (cbMessage != null) {
handleBroadcastSms(cbMessage);
return true;
}
}
return super.handleSmsMessage(message);
}
1.2 handleGsmBroadcastSms中MTK在实例化SmsCbHeader对象的时候,将所有的小区广播都初始化为非ETWS primary message。
/**
* Handle 3GPP format SMS-CB message.
* @param ar the AsyncResult containing the received PDUs
*/
private SmsCbMessage handleGsmBroadcastSms(AsyncResult ar) {
try {
..................
// MTK-START, set it is not a ETWS primary message
SmsCbHeader header = new SmsCbHeader(receivedPdu, false);
// MTK-END
..................
return GsmSmsCbMessage.createSmsCbMessage(header, location, pdus);
} catch (RuntimeException e) {
loge("Error in decoding SMS CB pdu", e);
return null;
}
}
1.3 我们来看看createSmsCbMessage,从上面实例化的对象来看,代码会走到else里面。priority这属性很重要,它用来区别是一般的小区广播还是紧急的小区广播
/**
* Create a new SmsCbMessage object from a header object plus one or more received PDUs.
*
* @param pdus PDU bytes
*/
static SmsCbMessage createSmsCbMessage(SmsCbHeader header, SmsCbLocation location,
byte[][] pdus) throws IllegalArgumentException {
if (header.isEtwsPrimaryNotification()) {
return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP,
header.getGeographicalScope(), header.getSerialNumber(),
location, header.getServiceCategory(),
null, "ETWS", SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY,
header.getEtwsInfo(), header.getCmasInfo());
} else {
String language = null;
StringBuilder sb = new StringBuilder();
for (byte[] pdu : pdus) {
Pair<String, String> p = parseBody(header, pdu);
language = p.first;
sb.append(p.second);
}
int priority = header.isEmergencyMessage() ? SmsCbMessage.MESSAGE_PRIORITY_EMERGENCY
: SmsCbMessage.MESSAGE_PRIORITY_NORMAL;
return new SmsCbMessage(SmsCbMessage.MESSAGE_FORMAT_3GPP,
header.getGeographicalScope(), header.getSerialNumber(), location,
header.getServiceCategory(), language, sb.toString(), priority,
header.getEtwsInfo(), header.getCmasInfo());
}
}
1.4 从isEmergencyMessage()可以看出当mMessageIdentifier 在SmsCbConstants.MESSAGE_ID_PWS_FIRST_IDENTIFIER、mMessageIdentifier <= SmsCbConstants.MESSAGE_ID_PWS_LAST_IDENTIFIER之间就是紧急小区广播。mMessageIdentifier它就是上面 1.2 中实例化SmsCbHeader对象时指定的。
/**
* Return whether this broadcast is an emergency (PWS) message type.
* @return true if this message is emergency type; false otherwise
*/
boolean isEmergencyMessage() {
return mMessageIdentifier >= SmsCbConstants.MESSAGE_ID_PWS_FIRST_IDENTIFIER
&& mMessageIdentifier <= SmsCbConstants.MESSAGE_ID_PWS_LAST_IDENTIFIER;
}
1.5 经过以上步骤,小区广播的实例化完毕,然后会调用1.1 中的handleBroadcastSms来广播。
/**
* Dispatch a Cell Broadcast message to listeners.
* @param message the Cell Broadcast to broadcast
*/
protected void handleBroadcastSms(SmsCbMessage message, boolean isPrimary) {
String receiverPermission;
int appOp;
Intent intent;
if (message.isEmergencyMessage()) {
log("Dispatching emergency SMS CB, SmsCbMessage is: " + message);
intent = new Intent(Telephony.Sms.Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION);
// MTK-START
intent.putExtra(SmsConstants.IS_EMERGENCY_CB_PRIMARY, isPrimary);
// MTK-END
receiverPermission = Manifest.permission.RECEIVE_EMERGENCY_BROADCAST;
appOp = AppOpsManager.OP_RECEIVE_EMERGECY_SMS;
} else {
log("Dispatching SMS CB, SmsCbMessage is: " + message);
intent = new Intent(Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION);
receiverPermission = Manifest.permission.RECEIVE_SMS;
appOp = AppOpsManager.OP_RECEIVE_SMS;
}
intent.putExtra("message", message);
SubscriptionManager.putPhoneIdAndSubIdExtra(intent, mPhone.getPhoneId());
mContext.sendOrderedBroadcastAsUser(intent, UserHandle.ALL, receiverPermission, appOp,
mReceiver, getHandler(), Activity.RESULT_OK, null, null);
}
从上面的代码可以看出,如果是紧急小区广播intent就会标示为SMS_EMERGENCY_CB_RECEIVED_ACTION,普通的就为SMS_CB_RECEIVED_ACTION
下面我们来看看上层接收到广播以后出来流程
packages/apps/CellBroadcastReceiver/src/com/android/cellbroadcastreceiver/CellBroadcastReceiver.java
vendor/mediatek/proprietary/packages/apps/CMASReceiver/src/com/mediatek/cellbroadcastreceiver/CellBroadcastReceiver.java
protected void onReceiveWithPrivilege(Context context, Intent intent, boolean privileged) {
............
} else if (Telephony.Sms.Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION.equals(action)) {
.......
if (privileged) {
intent.setClass(context, CellBroadcastAlertService.class);
context.startService(intent);
} else {
loge("ignoring unprivileged action received " + action);
}
}
...........
}
1、当接收到紧急小区广播会启动CellBroadcastAlertService服务。
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
String action = intent.getAction();
if (Telephony.Sms.Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION.equals(action) ||
Telephony.Sms.Intents.SMS_CB_RECEIVED_ACTION.equals(action)) {
handleCellBroadcastIntent(intent);
} else if (SHOW_NEW_ALERT_ACTION.equals(action)) {
try {
if (UserHandle.myUserId() ==
ActivityManagerNative.getDefault().getCurrentUser().id) {
showNewAlert(intent);
} else {
Log.d(TAG,"Not active user, ignore the alert display");
}
} catch (RemoteException e) {
e.printStackTrace();
}
} else {
Log.e(TAG, "Unrecognized intent action: " + action);
}
return START_NOT_STICKY;
}
1.6 当服务开启以后,handleCellBroadcastIntent会重新新建一个广播showNewAlert。
private void showNewAlert(Intent intent) {
Bundle extras = intent.getExtras();
if (extras == null) {
Log.e(TAG, "received SHOW_NEW_ALERT_ACTION with no extras!");
return;
}
CellBroadcastMessage cbm = (CellBroadcastMessage) intent.getParcelableExtra("message");
if (cbm == null) {
Log.e(TAG, "received SHOW_NEW_ALERT_ACTION with no message extra");
return;
}
if (CellBroadcastConfigService.isEmergencyAlertMessage(cbm)) {
// start alert sound / vibration / TTS and display full-screen alert
openEmergencyAlertNotification(cbm);
} else {
// add notification to the bar
addToNotificationBar(cbm);
}
}
当判断小区广播为紧急小区广播会调用openEmergencyAlertNotification(cbm)。
/**
* Display a full-screen alert message for emergency alerts.
* @param message the alert to display
*/
private void openEmergencyAlertNotification(CellBroadcastMessage message) {
..............
// start audio/vibration/speech service for emergency alerts
Intent audioIntent = new Intent(this, CellBroadcastAlertAudio.class);
audioIntent.setAction(CellBroadcastAlertAudio.ACTION_START_ALERT_AUDIO);
.............
startService(audioIntent);
}
1.7 然后启动CellBroadcastAlertAudio服务
packages/apps/CellBroadcastReceiver/src/com/android/cellbroadcastreceiver/CellBroadcastAlertAudio.java
vendor/mediatek/proprietary/packages/apps/CMASReceiver/src/com/mediatek/cellbroadcastreceiver/CellBroadcastAlertAudio.java
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// No intent, tell the system not to restart us.
if (intent == null) {
stopSelf();
return START_NOT_STICKY;
}
// This extra should always be provided by CellBroadcastAlertService,
// but default to 10.5 seconds just to be safe (CMAS requirement).
int duration = intent.getIntExtra(ALERT_AUDIO_DURATION_EXTRA, 10500);
if (SystemProperties.get("ro.mtk_gemini_support").equals("1")) {
mSubId = intent.getIntExtra("subscription", -1);
}
mHasTempVolume = intent.getBooleanExtra("has_alert_volume", false);
if (mHasTempVolume) {
mTempVolume = intent.getFloatExtra("alert_volume", 1.0f);
}
// Get text to speak (if enabled by user)
mMessageBody = intent.getStringExtra(ALERT_AUDIO_MESSAGE_BODY);
mMessageLanguage = intent.getStringExtra(ALERT_AUDIO_MESSAGE_LANGUAGE);
mEnableVibrate = intent.getBooleanExtra(ALERT_AUDIO_VIBRATE_EXTRA, true);
/*if (intent.getBooleanExtra(ALERT_AUDIO_ETWS_VIBRATE_EXTRA, false)) {
mEnableVibrate = true; // force enable vibration for ETWS alerts
}*/
switch (mAudioManager.getRingerMode()) {
case AudioManager.RINGER_MODE_SILENT:
if (DBG) log("Ringer mode: silent");
mEnableAudio = false;
mEnableVibrate = false;
break;
case AudioManager.RINGER_MODE_VIBRATE:
if (DBG) log("Ringer mode: vibrate");
mEnableAudio = false;
break;
case AudioManager.RINGER_MODE_NORMAL:
default:
if (DBG) log("Ringer mode: normal");
mEnableAudio = true;
break;
}
if (mMessageBody != null && mEnableAudio) {
if (mTts == null) {
mTts = new TextToSpeech(this, this);
} else if (mTtsEngineReady) {
setTtsLanguage();
}
}
if (mEnableAudio || mEnableVibrate) {
play(duration); // in milliseconds
} else {
stopSelf();
return START_NOT_STICKY;
}
// Record the initial call state here so that the new alarm has the
// newest state.
mInitialCallState = mTelephonyManager.getCallState();
return START_STICKY;
}
CellBroadcastAlertAudio启动以后会根据当前的情景模式 响铃或者震动。
1.6 步骤中如果判断为一般小区广播会调用addToNotificationBar,
/**
* Add the new alert to the notification bar (non-emergency alerts), or launch a
* high-priority immediate intent for emergency alerts.
* @param message the alert to display
*/
private void addToNotificationBar(CellBroadcastMessage message) {
int channelTitleId = CellBroadcastResources.getDialogTitleResource(message);
CharSequence channelName = getText(channelTitleId);
String messageBody = message.getMessageBody();
// Pass the list of unread non-emergency CellBroadcastMessages
ArrayList<CellBroadcastMessage> messageList = CellBroadcastReceiverApp
.addNewMessageToList(message);
// Create intent to show the new messages when user selects the notification.
Intent intent = createDisplayMessageIntent(this, CellBroadcastAlertDialog.class,
messageList);
intent.putExtra(CellBroadcastAlertFullScreen.FROM_NOTIFICATION_EXTRA, true);
PendingIntent pi = PendingIntent.getActivity(this, NOTIFICATION_ID, intent,
PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
// use default sound/vibration/lights for non-emergency broadcasts
Notification.Builder builder = new Notification.Builder(this)
.setSmallIcon(R.drawable.ic_notify_alert)
.setTicker(channelName)
.setWhen(System.currentTimeMillis())
.setContentIntent(pi)
.setCategory(Notification.CATEGORY_SYSTEM)
.setPriority(Notification.PRIORITY_HIGH)
.setColor(getResources().getColor(R.color.notification_color))
.setVisibility(Notification.VISIBILITY_PUBLIC)
.setDefaults(Notification.DEFAULT_ALL);
builder.setDefaults(Notification.DEFAULT_ALL);
// increment unread alert count (decremented when user dismisses alert dialog)
int unreadCount = messageList.size();
if (unreadCount > 1) {
// use generic count of unread broadcasts if more than one unread
builder.setContentTitle(getString(R.string.notification_multiple_title));
builder.setContentText(getString(R.string.notification_multiple, unreadCount));
} else {
builder.setContentTitle(channelName).setContentText(messageBody);
}
Notification note = builder.build();
note.defaults |= Notification.DEFAULT_SOUND;
note.defaults |= Notification.DEFAULT_VIBRATE;
long[] vibrate = {0,100,200,300};
note.vibrate = vibrate;
NotificationManager notificationManager =
(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
notificationManager.notify(NOTIFICATION_ID, note);
}
```