Loading src/java/android/telephony/CellBroadcastMessage.javadeleted 100644 → 0 +0 −452 Original line number Diff line number Diff line /* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package android.telephony; import android.annotation.UnsupportedAppUsage; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.os.Parcel; import android.os.Parcelable; import android.provider.Telephony; import android.text.format.DateUtils; /** * Application wrapper for {@link SmsCbMessage}. This is Parcelable so that * decoded broadcast message objects can be passed between running Services. * New broadcasts are received by the CellBroadcastReceiver app, which exports * the database of previously received broadcasts at "content://cellbroadcasts/". * The "android.permission.READ_CELL_BROADCASTS" permission is required to read * from the ContentProvider, and writes to the database are not allowed.<p> * * Use {@link #createFromCursor} to create CellBroadcastMessage objects from rows * in the database cursor returned by the ContentProvider. * * {@hide} */ public class CellBroadcastMessage implements Parcelable { /** Identifier for getExtra() when adding this object to an Intent. */ public static final String SMS_CB_MESSAGE_EXTRA = "com.android.cellbroadcastreceiver.SMS_CB_MESSAGE"; /** SmsCbMessage. */ private final SmsCbMessage mSmsCbMessage; private final long mDeliveryTime; private boolean mIsRead; /** * Indicates the subId * * @hide */ private int mSubId = 0; /** * set Subscription information * * @hide */ public void setSubId(int subId) { mSubId = subId; } /** * get Subscription information * * @hide */ public int getSubId() { return mSubId; } @UnsupportedAppUsage public CellBroadcastMessage(SmsCbMessage message) { mSmsCbMessage = message; mDeliveryTime = System.currentTimeMillis(); mIsRead = false; } private CellBroadcastMessage(SmsCbMessage message, long deliveryTime, boolean isRead) { mSmsCbMessage = message; mDeliveryTime = deliveryTime; mIsRead = isRead; } private CellBroadcastMessage(Parcel in) { mSmsCbMessage = new SmsCbMessage(in); mDeliveryTime = in.readLong(); mIsRead = (in.readInt() != 0); mSubId = in.readInt(); } /** Parcelable: no special flags. */ @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel out, int flags) { mSmsCbMessage.writeToParcel(out, flags); out.writeLong(mDeliveryTime); out.writeInt(mIsRead ? 1 : 0); out.writeInt(mSubId); } public static final Parcelable.Creator<CellBroadcastMessage> CREATOR = new Parcelable.Creator<CellBroadcastMessage>() { @Override public CellBroadcastMessage createFromParcel(Parcel in) { return new CellBroadcastMessage(in); } @Override public CellBroadcastMessage[] newArray(int size) { return new CellBroadcastMessage[size]; } }; /** * Create a CellBroadcastMessage from a row in the database. * @param cursor an open SQLite cursor pointing to the row to read * @return the new CellBroadcastMessage * @throws IllegalArgumentException if one of the required columns is missing */ @UnsupportedAppUsage public static CellBroadcastMessage createFromCursor(Cursor cursor) { int geoScope = cursor.getInt( cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.GEOGRAPHICAL_SCOPE)); int serialNum = cursor.getInt( cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.SERIAL_NUMBER)); int category = cursor.getInt( cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.SERVICE_CATEGORY)); String language = cursor.getString( cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.LANGUAGE_CODE)); String body = cursor.getString( cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.MESSAGE_BODY)); int format = cursor.getInt( cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.MESSAGE_FORMAT)); int priority = cursor.getInt( cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.MESSAGE_PRIORITY)); String plmn; int plmnColumn = cursor.getColumnIndex(Telephony.CellBroadcasts.PLMN); if (plmnColumn != -1 && !cursor.isNull(plmnColumn)) { plmn = cursor.getString(plmnColumn); } else { plmn = null; } int lac; int lacColumn = cursor.getColumnIndex(Telephony.CellBroadcasts.LAC); if (lacColumn != -1 && !cursor.isNull(lacColumn)) { lac = cursor.getInt(lacColumn); } else { lac = -1; } int cid; int cidColumn = cursor.getColumnIndex(Telephony.CellBroadcasts.CID); if (cidColumn != -1 && !cursor.isNull(cidColumn)) { cid = cursor.getInt(cidColumn); } else { cid = -1; } SmsCbLocation location = new SmsCbLocation(plmn, lac, cid); SmsCbEtwsInfo etwsInfo; int etwsWarningTypeColumn = cursor.getColumnIndex( Telephony.CellBroadcasts.ETWS_WARNING_TYPE); if (etwsWarningTypeColumn != -1 && !cursor.isNull(etwsWarningTypeColumn)) { int warningType = cursor.getInt(etwsWarningTypeColumn); etwsInfo = new SmsCbEtwsInfo(warningType, false, false, false, null); } else { etwsInfo = null; } SmsCbCmasInfo cmasInfo; int cmasMessageClassColumn = cursor.getColumnIndex( Telephony.CellBroadcasts.CMAS_MESSAGE_CLASS); if (cmasMessageClassColumn != -1 && !cursor.isNull(cmasMessageClassColumn)) { int messageClass = cursor.getInt(cmasMessageClassColumn); int cmasCategory; int cmasCategoryColumn = cursor.getColumnIndex( Telephony.CellBroadcasts.CMAS_CATEGORY); if (cmasCategoryColumn != -1 && !cursor.isNull(cmasCategoryColumn)) { cmasCategory = cursor.getInt(cmasCategoryColumn); } else { cmasCategory = SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN; } int responseType; int cmasResponseTypeColumn = cursor.getColumnIndex( Telephony.CellBroadcasts.CMAS_RESPONSE_TYPE); if (cmasResponseTypeColumn != -1 && !cursor.isNull(cmasResponseTypeColumn)) { responseType = cursor.getInt(cmasResponseTypeColumn); } else { responseType = SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN; } int severity; int cmasSeverityColumn = cursor.getColumnIndex( Telephony.CellBroadcasts.CMAS_SEVERITY); if (cmasSeverityColumn != -1 && !cursor.isNull(cmasSeverityColumn)) { severity = cursor.getInt(cmasSeverityColumn); } else { severity = SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN; } int urgency; int cmasUrgencyColumn = cursor.getColumnIndex( Telephony.CellBroadcasts.CMAS_URGENCY); if (cmasUrgencyColumn != -1 && !cursor.isNull(cmasUrgencyColumn)) { urgency = cursor.getInt(cmasUrgencyColumn); } else { urgency = SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN; } int certainty; int cmasCertaintyColumn = cursor.getColumnIndex( Telephony.CellBroadcasts.CMAS_CERTAINTY); if (cmasCertaintyColumn != -1 && !cursor.isNull(cmasCertaintyColumn)) { certainty = cursor.getInt(cmasCertaintyColumn); } else { certainty = SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN; } cmasInfo = new SmsCbCmasInfo(messageClass, cmasCategory, responseType, severity, urgency, certainty); } else { cmasInfo = null; } SmsCbMessage msg = new SmsCbMessage(format, geoScope, serialNum, location, category, language, body, priority, etwsInfo, cmasInfo); long deliveryTime = cursor.getLong(cursor.getColumnIndexOrThrow( Telephony.CellBroadcasts.DELIVERY_TIME)); boolean isRead = (cursor.getInt(cursor.getColumnIndexOrThrow( Telephony.CellBroadcasts.MESSAGE_READ)) != 0); return new CellBroadcastMessage(msg, deliveryTime, isRead); } /** * Return a ContentValues object for insertion into the database. * @return a new ContentValues object containing this object's data */ @UnsupportedAppUsage public ContentValues getContentValues() { ContentValues cv = new ContentValues(16); SmsCbMessage msg = mSmsCbMessage; cv.put(Telephony.CellBroadcasts.GEOGRAPHICAL_SCOPE, msg.getGeographicalScope()); SmsCbLocation location = msg.getLocation(); if (location.getPlmn() != null) { cv.put(Telephony.CellBroadcasts.PLMN, location.getPlmn()); } if (location.getLac() != -1) { cv.put(Telephony.CellBroadcasts.LAC, location.getLac()); } if (location.getCid() != -1) { cv.put(Telephony.CellBroadcasts.CID, location.getCid()); } cv.put(Telephony.CellBroadcasts.SERIAL_NUMBER, msg.getSerialNumber()); cv.put(Telephony.CellBroadcasts.SERVICE_CATEGORY, msg.getServiceCategory()); cv.put(Telephony.CellBroadcasts.LANGUAGE_CODE, msg.getLanguageCode()); cv.put(Telephony.CellBroadcasts.MESSAGE_BODY, msg.getMessageBody()); cv.put(Telephony.CellBroadcasts.DELIVERY_TIME, mDeliveryTime); cv.put(Telephony.CellBroadcasts.MESSAGE_READ, mIsRead); cv.put(Telephony.CellBroadcasts.MESSAGE_FORMAT, msg.getMessageFormat()); cv.put(Telephony.CellBroadcasts.MESSAGE_PRIORITY, msg.getMessagePriority()); SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo(); if (etwsInfo != null) { cv.put(Telephony.CellBroadcasts.ETWS_WARNING_TYPE, etwsInfo.getWarningType()); } SmsCbCmasInfo cmasInfo = mSmsCbMessage.getCmasWarningInfo(); if (cmasInfo != null) { cv.put(Telephony.CellBroadcasts.CMAS_MESSAGE_CLASS, cmasInfo.getMessageClass()); cv.put(Telephony.CellBroadcasts.CMAS_CATEGORY, cmasInfo.getCategory()); cv.put(Telephony.CellBroadcasts.CMAS_RESPONSE_TYPE, cmasInfo.getResponseType()); cv.put(Telephony.CellBroadcasts.CMAS_SEVERITY, cmasInfo.getSeverity()); cv.put(Telephony.CellBroadcasts.CMAS_URGENCY, cmasInfo.getUrgency()); cv.put(Telephony.CellBroadcasts.CMAS_CERTAINTY, cmasInfo.getCertainty()); } return cv; } /** * Set or clear the "read message" flag. * @param isRead true if the message has been read; false if not */ public void setIsRead(boolean isRead) { mIsRead = isRead; } @UnsupportedAppUsage public String getLanguageCode() { return mSmsCbMessage.getLanguageCode(); } @UnsupportedAppUsage public int getServiceCategory() { return mSmsCbMessage.getServiceCategory(); } @UnsupportedAppUsage public long getDeliveryTime() { return mDeliveryTime; } @UnsupportedAppUsage public String getMessageBody() { return mSmsCbMessage.getMessageBody(); } @UnsupportedAppUsage public boolean isRead() { return mIsRead; } @UnsupportedAppUsage public int getSerialNumber() { return mSmsCbMessage.getSerialNumber(); } public SmsCbCmasInfo getCmasWarningInfo() { return mSmsCbMessage.getCmasWarningInfo(); } @UnsupportedAppUsage public SmsCbEtwsInfo getEtwsWarningInfo() { return mSmsCbMessage.getEtwsWarningInfo(); } /** * Return whether the broadcast is an emergency (PWS) message type. * This includes lower priority test messages and Amber alerts. * * All public alerts show the flashing warning icon in the dialog, * but only emergency alerts play the alert sound and speak the message. * * @return true if the message is PWS type; false otherwise */ public boolean isPublicAlertMessage() { return mSmsCbMessage.isEmergencyMessage(); } /** * Returns whether the broadcast is an emergency (PWS) message type, * including test messages and AMBER alerts. * * @return true if the message is PWS type (ETWS or CMAS) */ @UnsupportedAppUsage public boolean isEmergencyAlertMessage() { return mSmsCbMessage.isEmergencyMessage(); } /** * Return whether the broadcast is an ETWS emergency message type. * @return true if the message is ETWS emergency type; false otherwise */ @UnsupportedAppUsage public boolean isEtwsMessage() { return mSmsCbMessage.isEtwsMessage(); } /** * Return whether the broadcast is a CMAS emergency message type. * @return true if the message is CMAS emergency type; false otherwise */ @UnsupportedAppUsage public boolean isCmasMessage() { return mSmsCbMessage.isCmasMessage(); } /** * Return the CMAS message class. * @return the CMAS message class, e.g. {@link SmsCbCmasInfo#CMAS_CLASS_SEVERE_THREAT}, or * {@link SmsCbCmasInfo#CMAS_CLASS_UNKNOWN} if this is not a CMAS alert */ public int getCmasMessageClass() { if (mSmsCbMessage.isCmasMessage()) { return mSmsCbMessage.getCmasWarningInfo().getMessageClass(); } else { return SmsCbCmasInfo.CMAS_CLASS_UNKNOWN; } } /** * Return whether the broadcast is an ETWS popup alert. * This method checks the message ID and the message code. * @return true if the message indicates an ETWS popup alert */ public boolean isEtwsPopupAlert() { SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo(); return etwsInfo != null && etwsInfo.isPopupAlert(); } /** * Return whether the broadcast is an ETWS emergency user alert. * This method checks the message ID and the message code. * @return true if the message indicates an ETWS emergency user alert */ public boolean isEtwsEmergencyUserAlert() { SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo(); return etwsInfo != null && etwsInfo.isEmergencyUserAlert(); } /** * Return whether the broadcast is an ETWS test message. * @return true if the message is an ETWS test message; false otherwise */ public boolean isEtwsTestMessage() { SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo(); return etwsInfo != null && etwsInfo.getWarningType() == SmsCbEtwsInfo.ETWS_WARNING_TYPE_TEST_MESSAGE; } /** * Return the abbreviated date string for the message delivery time. * @param context the context object * @return a String to use in the broadcast list UI */ public String getDateString(Context context) { int flags = DateUtils.FORMAT_NO_NOON_MIDNIGHT | DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_CAP_AMPM; return DateUtils.formatDateTime(context, mDeliveryTime, flags); } /** * Return the date string for the message delivery time, suitable for text-to-speech. * @param context the context object * @return a String for populating the list item AccessibilityEvent for TTS */ @UnsupportedAppUsage public String getSpokenDateString(Context context) { int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE; return DateUtils.formatDateTime(context, mDeliveryTime, flags); } } src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java +1 −1 Original line number Diff line number Diff line Loading @@ -113,7 +113,7 @@ public class CdmaInboundSmsHandler extends InboundSmsHandler { log("Broadcast type message"); String plmn = TelephonyManager.from(mContext).getNetworkOperatorForPhone(mPhone.getPhoneId()); SmsCbMessage cbMessage = sms.parseBroadcastSms(plmn); SmsCbMessage cbMessage = sms.parseBroadcastSms(plmn, mPhone.getPhoneId()); if (cbMessage != null) { mCellBroadcastHandler.dispatchSmsMessage(cbMessage); } else { Loading src/java/com/android/internal/telephony/gsm/GsmCellBroadcastHandler.java +2 −1 Original line number Diff line number Diff line Loading @@ -308,7 +308,8 @@ public class GsmCellBroadcastHandler extends CellBroadcastHandler { } } return GsmSmsCbMessage.createSmsCbMessage(mContext, header, location, pdus); return GsmSmsCbMessage.createSmsCbMessage(mContext, header, location, pdus, mPhone.getPhoneId()); } catch (RuntimeException e) { loge("Error in decoding SMS CB pdu", e); Loading tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsCbTest.java +10 −10 Original line number Diff line number Diff line Loading @@ -353,7 +353,7 @@ public class CdmaSmsCbTest extends AndroidTestCase { SmsMessage msg = createBroadcastSmsMessage(123, 456, BearerData.PRIORITY_NORMAL, BearerData.LANGUAGE_ENGLISH, encoding, TEST_TEXT); SmsCbMessage cbMessage = msg.parseBroadcastSms(""); SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0); verifyCbValues(cbMessage); assertEquals(123, cbMessage.getServiceCategory()); assertEquals(456, cbMessage.getSerialNumber()); Loading Loading @@ -385,7 +385,7 @@ public class CdmaSmsCbTest extends AndroidTestCase { SmsMessage msg = createBroadcastSmsMessage(987, 654, -1, -1, UserData.ENCODING_IS91_EXTENDED_PROTOCOL, IS91_TEXT); SmsCbMessage cbMessage = msg.parseBroadcastSms(""); SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0); verifyCbValues(cbMessage); assertEquals(987, cbMessage.getServiceCategory()); assertEquals(654, cbMessage.getSerialNumber()); Loading @@ -402,7 +402,7 @@ public class CdmaSmsCbTest extends AndroidTestCase { serviceCategory, 1234, BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH, UserData.ENCODING_7BIT_ASCII, body, -1, -1, -1, -1, -1); SmsCbMessage cbMessage = msg.parseBroadcastSms(""); SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0); verifyCbValues(cbMessage); assertEquals(serviceCategory, cbMessage.getServiceCategory()); assertEquals(1234, cbMessage.getSerialNumber()); Loading Loading @@ -458,7 +458,7 @@ public class CdmaSmsCbTest extends AndroidTestCase { SmsCbCmasInfo.CMAS_RESPONSE_TYPE_MONITOR, SmsCbCmasInfo.CMAS_SEVERITY_SEVERE, SmsCbCmasInfo.CMAS_URGENCY_EXPECTED, SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY); SmsCbMessage cbMessage = msg.parseBroadcastSms(""); SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0); verifyCbValues(cbMessage); assertEquals(SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT, cbMessage.getServiceCategory()); Loading Loading @@ -488,7 +488,7 @@ public class CdmaSmsCbTest extends AndroidTestCase { 12345, BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH, 0x1F, EXTREME_ALERT, -1, -1, -1, -1, -1); SmsCbMessage cbMessage = msg.parseBroadcastSms(""); SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0); assertNull("expected null for unsupported charset", cbMessage); } Loading @@ -500,7 +500,7 @@ public class CdmaSmsCbTest extends AndroidTestCase { 67890, BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH, UserData.ENCODING_KOREAN, EXTREME_ALERT, -1, -1, -1, -1, -1); SmsCbMessage cbMessage = msg.parseBroadcastSms(""); SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0); assertNull("expected null for unsupported charset", cbMessage); } Loading @@ -513,7 +513,7 @@ public class CdmaSmsCbTest extends AndroidTestCase { BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH, UserData.ENCODING_7BIT_ASCII, null, -1, -1, -1, -1, -1); SmsCbMessage cbMessage = msg.parseBroadcastSms(""); SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0); verifyCbValues(cbMessage); assertEquals(SmsEnvelope.SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT, cbMessage.getServiceCategory()); Loading Loading @@ -547,7 +547,7 @@ public class CdmaSmsCbTest extends AndroidTestCase { int category = 0x0ff0 + r.nextInt(32); // half CMAS, half non-CMAS CdmaSmsMessage cdmaSmsMessage = createBroadcastParcel(category); SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, data); SmsCbMessage cbMessage = msg.parseBroadcastSms(""); SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0); // with random input, cbMessage will almost always be null (log when it isn't) if (cbMessage != null) { Rlog.d("CdmaSmsCbTest", "success: " + cbMessage); Loading Loading @@ -581,7 +581,7 @@ public class CdmaSmsCbTest extends AndroidTestCase { } SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, bos.toByteArray()); SmsCbMessage cbMessage = msg.parseBroadcastSms(""); SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0); } catch (Exception e) { Rlog.d("CdmaSmsCbTest", "exception thrown", e); fail("Exception in decoder at run " + run + " length " + len + ": " + e); Loading Loading @@ -744,7 +744,7 @@ public class CdmaSmsCbTest extends AndroidTestCase { CdmaSmsMessage cdmaSmsMessage = createBroadcastParcel(SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE); SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, CMAS_TEST_BEARER_DATA); SmsCbMessage cbMessage = msg.parseBroadcastSms(""); SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0); assertNotNull("expected non-null for bearer data", cbMessage); assertEquals("geoScope", cbMessage.getGeographicalScope(), 1); assertEquals("serialNumber", cbMessage.getSerialNumber(), 51072); Loading tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsCbTest.java +2 −1 Original line number Diff line number Diff line Loading @@ -43,7 +43,8 @@ public class GsmSmsCbTest extends AndroidTestCase { SmsCbHeader header = new SmsCbHeader(pdu); byte[][] pdus = new byte[1][]; pdus[0] = pdu; return GsmSmsCbMessage.createSmsCbMessage(getContext(), header, sTestLocation, pdus); return GsmSmsCbMessage.createSmsCbMessage(getContext(), header, sTestLocation, pdus, /* slotIndex */ 0); } catch (IllegalArgumentException e) { return null; } Loading Loading
src/java/android/telephony/CellBroadcastMessage.javadeleted 100644 → 0 +0 −452 Original line number Diff line number Diff line /* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package android.telephony; import android.annotation.UnsupportedAppUsage; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.os.Parcel; import android.os.Parcelable; import android.provider.Telephony; import android.text.format.DateUtils; /** * Application wrapper for {@link SmsCbMessage}. This is Parcelable so that * decoded broadcast message objects can be passed between running Services. * New broadcasts are received by the CellBroadcastReceiver app, which exports * the database of previously received broadcasts at "content://cellbroadcasts/". * The "android.permission.READ_CELL_BROADCASTS" permission is required to read * from the ContentProvider, and writes to the database are not allowed.<p> * * Use {@link #createFromCursor} to create CellBroadcastMessage objects from rows * in the database cursor returned by the ContentProvider. * * {@hide} */ public class CellBroadcastMessage implements Parcelable { /** Identifier for getExtra() when adding this object to an Intent. */ public static final String SMS_CB_MESSAGE_EXTRA = "com.android.cellbroadcastreceiver.SMS_CB_MESSAGE"; /** SmsCbMessage. */ private final SmsCbMessage mSmsCbMessage; private final long mDeliveryTime; private boolean mIsRead; /** * Indicates the subId * * @hide */ private int mSubId = 0; /** * set Subscription information * * @hide */ public void setSubId(int subId) { mSubId = subId; } /** * get Subscription information * * @hide */ public int getSubId() { return mSubId; } @UnsupportedAppUsage public CellBroadcastMessage(SmsCbMessage message) { mSmsCbMessage = message; mDeliveryTime = System.currentTimeMillis(); mIsRead = false; } private CellBroadcastMessage(SmsCbMessage message, long deliveryTime, boolean isRead) { mSmsCbMessage = message; mDeliveryTime = deliveryTime; mIsRead = isRead; } private CellBroadcastMessage(Parcel in) { mSmsCbMessage = new SmsCbMessage(in); mDeliveryTime = in.readLong(); mIsRead = (in.readInt() != 0); mSubId = in.readInt(); } /** Parcelable: no special flags. */ @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel out, int flags) { mSmsCbMessage.writeToParcel(out, flags); out.writeLong(mDeliveryTime); out.writeInt(mIsRead ? 1 : 0); out.writeInt(mSubId); } public static final Parcelable.Creator<CellBroadcastMessage> CREATOR = new Parcelable.Creator<CellBroadcastMessage>() { @Override public CellBroadcastMessage createFromParcel(Parcel in) { return new CellBroadcastMessage(in); } @Override public CellBroadcastMessage[] newArray(int size) { return new CellBroadcastMessage[size]; } }; /** * Create a CellBroadcastMessage from a row in the database. * @param cursor an open SQLite cursor pointing to the row to read * @return the new CellBroadcastMessage * @throws IllegalArgumentException if one of the required columns is missing */ @UnsupportedAppUsage public static CellBroadcastMessage createFromCursor(Cursor cursor) { int geoScope = cursor.getInt( cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.GEOGRAPHICAL_SCOPE)); int serialNum = cursor.getInt( cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.SERIAL_NUMBER)); int category = cursor.getInt( cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.SERVICE_CATEGORY)); String language = cursor.getString( cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.LANGUAGE_CODE)); String body = cursor.getString( cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.MESSAGE_BODY)); int format = cursor.getInt( cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.MESSAGE_FORMAT)); int priority = cursor.getInt( cursor.getColumnIndexOrThrow(Telephony.CellBroadcasts.MESSAGE_PRIORITY)); String plmn; int plmnColumn = cursor.getColumnIndex(Telephony.CellBroadcasts.PLMN); if (plmnColumn != -1 && !cursor.isNull(plmnColumn)) { plmn = cursor.getString(plmnColumn); } else { plmn = null; } int lac; int lacColumn = cursor.getColumnIndex(Telephony.CellBroadcasts.LAC); if (lacColumn != -1 && !cursor.isNull(lacColumn)) { lac = cursor.getInt(lacColumn); } else { lac = -1; } int cid; int cidColumn = cursor.getColumnIndex(Telephony.CellBroadcasts.CID); if (cidColumn != -1 && !cursor.isNull(cidColumn)) { cid = cursor.getInt(cidColumn); } else { cid = -1; } SmsCbLocation location = new SmsCbLocation(plmn, lac, cid); SmsCbEtwsInfo etwsInfo; int etwsWarningTypeColumn = cursor.getColumnIndex( Telephony.CellBroadcasts.ETWS_WARNING_TYPE); if (etwsWarningTypeColumn != -1 && !cursor.isNull(etwsWarningTypeColumn)) { int warningType = cursor.getInt(etwsWarningTypeColumn); etwsInfo = new SmsCbEtwsInfo(warningType, false, false, false, null); } else { etwsInfo = null; } SmsCbCmasInfo cmasInfo; int cmasMessageClassColumn = cursor.getColumnIndex( Telephony.CellBroadcasts.CMAS_MESSAGE_CLASS); if (cmasMessageClassColumn != -1 && !cursor.isNull(cmasMessageClassColumn)) { int messageClass = cursor.getInt(cmasMessageClassColumn); int cmasCategory; int cmasCategoryColumn = cursor.getColumnIndex( Telephony.CellBroadcasts.CMAS_CATEGORY); if (cmasCategoryColumn != -1 && !cursor.isNull(cmasCategoryColumn)) { cmasCategory = cursor.getInt(cmasCategoryColumn); } else { cmasCategory = SmsCbCmasInfo.CMAS_CATEGORY_UNKNOWN; } int responseType; int cmasResponseTypeColumn = cursor.getColumnIndex( Telephony.CellBroadcasts.CMAS_RESPONSE_TYPE); if (cmasResponseTypeColumn != -1 && !cursor.isNull(cmasResponseTypeColumn)) { responseType = cursor.getInt(cmasResponseTypeColumn); } else { responseType = SmsCbCmasInfo.CMAS_RESPONSE_TYPE_UNKNOWN; } int severity; int cmasSeverityColumn = cursor.getColumnIndex( Telephony.CellBroadcasts.CMAS_SEVERITY); if (cmasSeverityColumn != -1 && !cursor.isNull(cmasSeverityColumn)) { severity = cursor.getInt(cmasSeverityColumn); } else { severity = SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN; } int urgency; int cmasUrgencyColumn = cursor.getColumnIndex( Telephony.CellBroadcasts.CMAS_URGENCY); if (cmasUrgencyColumn != -1 && !cursor.isNull(cmasUrgencyColumn)) { urgency = cursor.getInt(cmasUrgencyColumn); } else { urgency = SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN; } int certainty; int cmasCertaintyColumn = cursor.getColumnIndex( Telephony.CellBroadcasts.CMAS_CERTAINTY); if (cmasCertaintyColumn != -1 && !cursor.isNull(cmasCertaintyColumn)) { certainty = cursor.getInt(cmasCertaintyColumn); } else { certainty = SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN; } cmasInfo = new SmsCbCmasInfo(messageClass, cmasCategory, responseType, severity, urgency, certainty); } else { cmasInfo = null; } SmsCbMessage msg = new SmsCbMessage(format, geoScope, serialNum, location, category, language, body, priority, etwsInfo, cmasInfo); long deliveryTime = cursor.getLong(cursor.getColumnIndexOrThrow( Telephony.CellBroadcasts.DELIVERY_TIME)); boolean isRead = (cursor.getInt(cursor.getColumnIndexOrThrow( Telephony.CellBroadcasts.MESSAGE_READ)) != 0); return new CellBroadcastMessage(msg, deliveryTime, isRead); } /** * Return a ContentValues object for insertion into the database. * @return a new ContentValues object containing this object's data */ @UnsupportedAppUsage public ContentValues getContentValues() { ContentValues cv = new ContentValues(16); SmsCbMessage msg = mSmsCbMessage; cv.put(Telephony.CellBroadcasts.GEOGRAPHICAL_SCOPE, msg.getGeographicalScope()); SmsCbLocation location = msg.getLocation(); if (location.getPlmn() != null) { cv.put(Telephony.CellBroadcasts.PLMN, location.getPlmn()); } if (location.getLac() != -1) { cv.put(Telephony.CellBroadcasts.LAC, location.getLac()); } if (location.getCid() != -1) { cv.put(Telephony.CellBroadcasts.CID, location.getCid()); } cv.put(Telephony.CellBroadcasts.SERIAL_NUMBER, msg.getSerialNumber()); cv.put(Telephony.CellBroadcasts.SERVICE_CATEGORY, msg.getServiceCategory()); cv.put(Telephony.CellBroadcasts.LANGUAGE_CODE, msg.getLanguageCode()); cv.put(Telephony.CellBroadcasts.MESSAGE_BODY, msg.getMessageBody()); cv.put(Telephony.CellBroadcasts.DELIVERY_TIME, mDeliveryTime); cv.put(Telephony.CellBroadcasts.MESSAGE_READ, mIsRead); cv.put(Telephony.CellBroadcasts.MESSAGE_FORMAT, msg.getMessageFormat()); cv.put(Telephony.CellBroadcasts.MESSAGE_PRIORITY, msg.getMessagePriority()); SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo(); if (etwsInfo != null) { cv.put(Telephony.CellBroadcasts.ETWS_WARNING_TYPE, etwsInfo.getWarningType()); } SmsCbCmasInfo cmasInfo = mSmsCbMessage.getCmasWarningInfo(); if (cmasInfo != null) { cv.put(Telephony.CellBroadcasts.CMAS_MESSAGE_CLASS, cmasInfo.getMessageClass()); cv.put(Telephony.CellBroadcasts.CMAS_CATEGORY, cmasInfo.getCategory()); cv.put(Telephony.CellBroadcasts.CMAS_RESPONSE_TYPE, cmasInfo.getResponseType()); cv.put(Telephony.CellBroadcasts.CMAS_SEVERITY, cmasInfo.getSeverity()); cv.put(Telephony.CellBroadcasts.CMAS_URGENCY, cmasInfo.getUrgency()); cv.put(Telephony.CellBroadcasts.CMAS_CERTAINTY, cmasInfo.getCertainty()); } return cv; } /** * Set or clear the "read message" flag. * @param isRead true if the message has been read; false if not */ public void setIsRead(boolean isRead) { mIsRead = isRead; } @UnsupportedAppUsage public String getLanguageCode() { return mSmsCbMessage.getLanguageCode(); } @UnsupportedAppUsage public int getServiceCategory() { return mSmsCbMessage.getServiceCategory(); } @UnsupportedAppUsage public long getDeliveryTime() { return mDeliveryTime; } @UnsupportedAppUsage public String getMessageBody() { return mSmsCbMessage.getMessageBody(); } @UnsupportedAppUsage public boolean isRead() { return mIsRead; } @UnsupportedAppUsage public int getSerialNumber() { return mSmsCbMessage.getSerialNumber(); } public SmsCbCmasInfo getCmasWarningInfo() { return mSmsCbMessage.getCmasWarningInfo(); } @UnsupportedAppUsage public SmsCbEtwsInfo getEtwsWarningInfo() { return mSmsCbMessage.getEtwsWarningInfo(); } /** * Return whether the broadcast is an emergency (PWS) message type. * This includes lower priority test messages and Amber alerts. * * All public alerts show the flashing warning icon in the dialog, * but only emergency alerts play the alert sound and speak the message. * * @return true if the message is PWS type; false otherwise */ public boolean isPublicAlertMessage() { return mSmsCbMessage.isEmergencyMessage(); } /** * Returns whether the broadcast is an emergency (PWS) message type, * including test messages and AMBER alerts. * * @return true if the message is PWS type (ETWS or CMAS) */ @UnsupportedAppUsage public boolean isEmergencyAlertMessage() { return mSmsCbMessage.isEmergencyMessage(); } /** * Return whether the broadcast is an ETWS emergency message type. * @return true if the message is ETWS emergency type; false otherwise */ @UnsupportedAppUsage public boolean isEtwsMessage() { return mSmsCbMessage.isEtwsMessage(); } /** * Return whether the broadcast is a CMAS emergency message type. * @return true if the message is CMAS emergency type; false otherwise */ @UnsupportedAppUsage public boolean isCmasMessage() { return mSmsCbMessage.isCmasMessage(); } /** * Return the CMAS message class. * @return the CMAS message class, e.g. {@link SmsCbCmasInfo#CMAS_CLASS_SEVERE_THREAT}, or * {@link SmsCbCmasInfo#CMAS_CLASS_UNKNOWN} if this is not a CMAS alert */ public int getCmasMessageClass() { if (mSmsCbMessage.isCmasMessage()) { return mSmsCbMessage.getCmasWarningInfo().getMessageClass(); } else { return SmsCbCmasInfo.CMAS_CLASS_UNKNOWN; } } /** * Return whether the broadcast is an ETWS popup alert. * This method checks the message ID and the message code. * @return true if the message indicates an ETWS popup alert */ public boolean isEtwsPopupAlert() { SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo(); return etwsInfo != null && etwsInfo.isPopupAlert(); } /** * Return whether the broadcast is an ETWS emergency user alert. * This method checks the message ID and the message code. * @return true if the message indicates an ETWS emergency user alert */ public boolean isEtwsEmergencyUserAlert() { SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo(); return etwsInfo != null && etwsInfo.isEmergencyUserAlert(); } /** * Return whether the broadcast is an ETWS test message. * @return true if the message is an ETWS test message; false otherwise */ public boolean isEtwsTestMessage() { SmsCbEtwsInfo etwsInfo = mSmsCbMessage.getEtwsWarningInfo(); return etwsInfo != null && etwsInfo.getWarningType() == SmsCbEtwsInfo.ETWS_WARNING_TYPE_TEST_MESSAGE; } /** * Return the abbreviated date string for the message delivery time. * @param context the context object * @return a String to use in the broadcast list UI */ public String getDateString(Context context) { int flags = DateUtils.FORMAT_NO_NOON_MIDNIGHT | DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_ABBREV_ALL | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_CAP_AMPM; return DateUtils.formatDateTime(context, mDeliveryTime, flags); } /** * Return the date string for the message delivery time, suitable for text-to-speech. * @param context the context object * @return a String for populating the list item AccessibilityEvent for TTS */ @UnsupportedAppUsage public String getSpokenDateString(Context context) { int flags = DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE; return DateUtils.formatDateTime(context, mDeliveryTime, flags); } }
src/java/com/android/internal/telephony/cdma/CdmaInboundSmsHandler.java +1 −1 Original line number Diff line number Diff line Loading @@ -113,7 +113,7 @@ public class CdmaInboundSmsHandler extends InboundSmsHandler { log("Broadcast type message"); String plmn = TelephonyManager.from(mContext).getNetworkOperatorForPhone(mPhone.getPhoneId()); SmsCbMessage cbMessage = sms.parseBroadcastSms(plmn); SmsCbMessage cbMessage = sms.parseBroadcastSms(plmn, mPhone.getPhoneId()); if (cbMessage != null) { mCellBroadcastHandler.dispatchSmsMessage(cbMessage); } else { Loading
src/java/com/android/internal/telephony/gsm/GsmCellBroadcastHandler.java +2 −1 Original line number Diff line number Diff line Loading @@ -308,7 +308,8 @@ public class GsmCellBroadcastHandler extends CellBroadcastHandler { } } return GsmSmsCbMessage.createSmsCbMessage(mContext, header, location, pdus); return GsmSmsCbMessage.createSmsCbMessage(mContext, header, location, pdus, mPhone.getPhoneId()); } catch (RuntimeException e) { loge("Error in decoding SMS CB pdu", e); Loading
tests/telephonytests/src/com/android/internal/telephony/cdma/CdmaSmsCbTest.java +10 −10 Original line number Diff line number Diff line Loading @@ -353,7 +353,7 @@ public class CdmaSmsCbTest extends AndroidTestCase { SmsMessage msg = createBroadcastSmsMessage(123, 456, BearerData.PRIORITY_NORMAL, BearerData.LANGUAGE_ENGLISH, encoding, TEST_TEXT); SmsCbMessage cbMessage = msg.parseBroadcastSms(""); SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0); verifyCbValues(cbMessage); assertEquals(123, cbMessage.getServiceCategory()); assertEquals(456, cbMessage.getSerialNumber()); Loading Loading @@ -385,7 +385,7 @@ public class CdmaSmsCbTest extends AndroidTestCase { SmsMessage msg = createBroadcastSmsMessage(987, 654, -1, -1, UserData.ENCODING_IS91_EXTENDED_PROTOCOL, IS91_TEXT); SmsCbMessage cbMessage = msg.parseBroadcastSms(""); SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0); verifyCbValues(cbMessage); assertEquals(987, cbMessage.getServiceCategory()); assertEquals(654, cbMessage.getSerialNumber()); Loading @@ -402,7 +402,7 @@ public class CdmaSmsCbTest extends AndroidTestCase { serviceCategory, 1234, BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH, UserData.ENCODING_7BIT_ASCII, body, -1, -1, -1, -1, -1); SmsCbMessage cbMessage = msg.parseBroadcastSms(""); SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0); verifyCbValues(cbMessage); assertEquals(serviceCategory, cbMessage.getServiceCategory()); assertEquals(1234, cbMessage.getSerialNumber()); Loading Loading @@ -458,7 +458,7 @@ public class CdmaSmsCbTest extends AndroidTestCase { SmsCbCmasInfo.CMAS_RESPONSE_TYPE_MONITOR, SmsCbCmasInfo.CMAS_SEVERITY_SEVERE, SmsCbCmasInfo.CMAS_URGENCY_EXPECTED, SmsCbCmasInfo.CMAS_CERTAINTY_LIKELY); SmsCbMessage cbMessage = msg.parseBroadcastSms(""); SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0); verifyCbValues(cbMessage); assertEquals(SmsEnvelope.SERVICE_CATEGORY_CMAS_EXTREME_THREAT, cbMessage.getServiceCategory()); Loading Loading @@ -488,7 +488,7 @@ public class CdmaSmsCbTest extends AndroidTestCase { 12345, BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH, 0x1F, EXTREME_ALERT, -1, -1, -1, -1, -1); SmsCbMessage cbMessage = msg.parseBroadcastSms(""); SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0); assertNull("expected null for unsupported charset", cbMessage); } Loading @@ -500,7 +500,7 @@ public class CdmaSmsCbTest extends AndroidTestCase { 67890, BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH, UserData.ENCODING_KOREAN, EXTREME_ALERT, -1, -1, -1, -1, -1); SmsCbMessage cbMessage = msg.parseBroadcastSms(""); SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0); assertNull("expected null for unsupported charset", cbMessage); } Loading @@ -513,7 +513,7 @@ public class CdmaSmsCbTest extends AndroidTestCase { BearerData.PRIORITY_EMERGENCY, BearerData.LANGUAGE_ENGLISH, UserData.ENCODING_7BIT_ASCII, null, -1, -1, -1, -1, -1); SmsCbMessage cbMessage = msg.parseBroadcastSms(""); SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0); verifyCbValues(cbMessage); assertEquals(SmsEnvelope.SERVICE_CATEGORY_CMAS_PRESIDENTIAL_LEVEL_ALERT, cbMessage.getServiceCategory()); Loading Loading @@ -547,7 +547,7 @@ public class CdmaSmsCbTest extends AndroidTestCase { int category = 0x0ff0 + r.nextInt(32); // half CMAS, half non-CMAS CdmaSmsMessage cdmaSmsMessage = createBroadcastParcel(category); SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, data); SmsCbMessage cbMessage = msg.parseBroadcastSms(""); SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0); // with random input, cbMessage will almost always be null (log when it isn't) if (cbMessage != null) { Rlog.d("CdmaSmsCbTest", "success: " + cbMessage); Loading Loading @@ -581,7 +581,7 @@ public class CdmaSmsCbTest extends AndroidTestCase { } SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, bos.toByteArray()); SmsCbMessage cbMessage = msg.parseBroadcastSms(""); SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0); } catch (Exception e) { Rlog.d("CdmaSmsCbTest", "exception thrown", e); fail("Exception in decoder at run " + run + " length " + len + ": " + e); Loading Loading @@ -744,7 +744,7 @@ public class CdmaSmsCbTest extends AndroidTestCase { CdmaSmsMessage cdmaSmsMessage = createBroadcastParcel(SmsEnvelope.SERVICE_CATEGORY_CMAS_TEST_MESSAGE); SmsMessage msg = createMessageFromParcel(cdmaSmsMessage, CMAS_TEST_BEARER_DATA); SmsCbMessage cbMessage = msg.parseBroadcastSms(""); SmsCbMessage cbMessage = msg.parseBroadcastSms("", 0); assertNotNull("expected non-null for bearer data", cbMessage); assertEquals("geoScope", cbMessage.getGeographicalScope(), 1); assertEquals("serialNumber", cbMessage.getSerialNumber(), 51072); Loading
tests/telephonytests/src/com/android/internal/telephony/gsm/GsmSmsCbTest.java +2 −1 Original line number Diff line number Diff line Loading @@ -43,7 +43,8 @@ public class GsmSmsCbTest extends AndroidTestCase { SmsCbHeader header = new SmsCbHeader(pdu); byte[][] pdus = new byte[1][]; pdus[0] = pdu; return GsmSmsCbMessage.createSmsCbMessage(getContext(), header, sTestLocation, pdus); return GsmSmsCbMessage.createSmsCbMessage(getContext(), header, sTestLocation, pdus, /* slotIndex */ 0); } catch (IllegalArgumentException e) { return null; } Loading