Allows application to send SMS
diff --git a/telephony/java/android/telephony/SmsCbConstants.java b/telephony/java/android/telephony/SmsCbConstants.java
new file mode 100644
index 0000000000000..1ac9ae3fa0162
--- /dev/null
+++ b/telephony/java/android/telephony/SmsCbConstants.java
@@ -0,0 +1,114 @@
+/*
+ * 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;
+
+/**
+ * Constants used in SMS Cell Broadcast messages.
+ *
+ * {@hide}
+ */
+public interface SmsCbConstants {
+ /** Cell wide immediate geographical scope */
+ public static final int GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE = 0;
+
+ /** PLMN wide geographical scope */
+ public static final int GEOGRAPHICAL_SCOPE_PLMN_WIDE = 1;
+
+ /** Location / service area wide geographical scope */
+ public static final int GEOGRAPHICAL_SCOPE_LA_WIDE = 2;
+
+ /** Cell wide geographical scope */
+ public static final int GEOGRAPHICAL_SCOPE_CELL_WIDE = 3;
+
+ /** Start of PWS Message Identifier range (includes ETWS and CMAS). */
+ public static final int MESSAGE_ID_PWS_FIRST_IDENTIFIER = 0x1100;
+
+ /** Bitmask for messages of ETWS type (including future extensions). */
+ public static final int MESSAGE_ID_ETWS_TYPE_MASK = 0xFFF8;
+
+ /** Value for messages of ETWS type after applying {@link #MESSAGE_ID_ETWS_TYPE_MASK}. */
+ public static final int MESSAGE_ID_ETWS_TYPE = 0x1100;
+
+ /** ETWS Message Identifier for earthquake warning message. */
+ public static final int MESSAGE_ID_ETWS_EARTHQUAKE_WARNING = 0x1100;
+
+ /** ETWS Message Identifier for tsunami warning message. */
+ public static final int MESSAGE_ID_ETWS_TSUNAMI_WARNING = 0x1101;
+
+ /** ETWS Message Identifier for earthquake and tsunami combined warning message. */
+ public static final int MESSAGE_ID_ETWS_EARTHQUAKE_AND_TSUNAMI_WARNING = 0x1102;
+
+ /** ETWS Message Identifier for test message. */
+ public static final int MESSAGE_ID_ETWS_TEST_MESSAGE = 0x1103;
+
+ /** ETWS Message Identifier for messages related to other emergency types. */
+ public static final int MESSAGE_ID_ETWS_OTHER_EMERGENCY_TYPE = 0x1104;
+
+ /** Start of CMAS Message Identifier range. */
+ public static final int MESSAGE_ID_CMAS_FIRST_IDENTIFIER = 0x1112;
+
+ /** CMAS Message Identifier for Presidential Level alerts. */
+ public static final int MESSAGE_ID_CMAS_ALERT_PRESIDENTIAL_LEVEL = 0x1112;
+
+ /** CMAS Message Identifier for Extreme alerts, Urgency=Immediate, Certainty=Observed. */
+ public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_OBSERVED = 0x1113;
+
+ /** CMAS Message Identifier for Extreme alerts, Urgency=Immediate, Certainty=Likely. */
+ public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_IMMEDIATE_LIKELY = 0x1114;
+
+ /** CMAS Message Identifier for Extreme alerts, Urgency=Expected, Certainty=Observed. */
+ public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_OBSERVED = 0x1115;
+
+ /** CMAS Message Identifier for Extreme alerts, Urgency=Expected, Certainty=Likely. */
+ public static final int MESSAGE_ID_CMAS_ALERT_EXTREME_EXPECTED_LIKELY = 0x1116;
+
+ /** CMAS Message Identifier for Severe alerts, Urgency=Immediate, Certainty=Observed. */
+ public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_OBSERVED = 0x1117;
+
+ /** CMAS Message Identifier for Severe alerts, Urgency=Immediate, Certainty=Likely. */
+ public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_IMMEDIATE_LIKELY = 0x1118;
+
+ /** CMAS Message Identifier for Severe alerts, Urgency=Expected, Certainty=Observed. */
+ public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_OBSERVED = 0x1119;
+
+ /** CMAS Message Identifier for Severe alerts, Urgency=Expected, Certainty=Likely. */
+ public static final int MESSAGE_ID_CMAS_ALERT_SEVERE_EXPECTED_LIKELY = 0x111A;
+
+ /** CMAS Message Identifier for Child Abduction Emergency (Amber Alert). */
+ public static final int MESSAGE_ID_CMAS_ALERT_CHILD_ABDUCTION_EMERGENCY = 0x111B;
+
+ /** CMAS Message Identifier for the Required Monthly Test. */
+ public static final int MESSAGE_ID_CMAS_ALERT_REQUIRED_MONTHLY_TEST = 0x111C;
+
+ /** CMAS Message Identifier for CMAS Exercise. */
+ public static final int MESSAGE_ID_CMAS_ALERT_EXERCISE = 0x111D;
+
+ /** CMAS Message Identifier for operator defined use. */
+ public static final int MESSAGE_ID_CMAS_ALERT_OPERATOR_DEFINED_USE = 0x111E;
+
+ /** End of CMAS Message Identifier range (including future extensions). */
+ public static final int MESSAGE_ID_CMAS_LAST_IDENTIFIER = 0x112F;
+
+ /** End of PWS Message Identifier range (includes ETWS, CMAS, and future extensions). */
+ public static final int MESSAGE_ID_PWS_LAST_IDENTIFIER = 0x18FF;
+
+ /** ETWS message code flag to activate the popup display. */
+ public static final int MESSAGE_CODE_ETWS_ACTIVATE_POPUP = 0x100;
+
+ /** ETWS message code flag to activate the emergency user alert. */
+ public static final int MESSAGE_CODE_ETWS_EMERGENCY_USER_ALERT = 0x200;
+}
diff --git a/telephony/java/android/telephony/SmsCbMessage.java b/telephony/java/android/telephony/SmsCbMessage.java
index 5608402b5f3eb..6da48d6864f80 100644
--- a/telephony/java/android/telephony/SmsCbMessage.java
+++ b/telephony/java/android/telephony/SmsCbMessage.java
@@ -333,4 +333,10 @@ public class SmsCbMessage {
return body;
}
+
+ @Override
+ public String toString() {
+ return "SmsCbMessage{" + mHeader.toString() + ", language=" + mLanguage +
+ ", body=\"" + mBody + "\"}";
+ }
}
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index 6a346af37bd50..5bdc146792f35 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -345,7 +345,7 @@ public final class SmsManager {
* message identifier. Note that if two different clients enable the same
* message identifier, they must both disable it for the device to stop
* receiving those messages. All received messages will be broadcast in an
- * intent with the action "android.provider.telephony.SMS_CB_RECEIVED".
+ * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED".
* Note: This call is blocking, callers may want to avoid calling it from
* the main thread of an application.
*
@@ -400,6 +400,68 @@ public final class SmsManager {
return success;
}
+ /**
+ * Enable reception of cell broadcast (SMS-CB) messages with the given
+ * message identifier range. Note that if two different clients enable the same
+ * message identifier, they must both disable it for the device to stop
+ * receiving those messages. All received messages will be broadcast in an
+ * intent with the action "android.provider.Telephony.SMS_CB_RECEIVED".
+ * Note: This call is blocking, callers may want to avoid calling it from
+ * the main thread of an application.
+ *
+ * @param startMessageId first message identifier as specified in TS 23.041
+ * @param endMessageId last message identifier as specified in TS 23.041
+ * @return true if successful, false otherwise
+ * @see #disableCellBroadcastRange(int, int)
+ *
+ * {@hide}
+ */
+ public boolean enableCellBroadcastRange(int startMessageId, int endMessageId) {
+ boolean success = false;
+
+ try {
+ ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+ if (iccISms != null) {
+ success = iccISms.enableCellBroadcastRange(startMessageId, endMessageId);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return success;
+ }
+
+ /**
+ * Disable reception of cell broadcast (SMS-CB) messages with the given
+ * message identifier range. Note that if two different clients enable the same
+ * message identifier, they must both disable it for the device to stop
+ * receiving those messages.
+ * Note: This call is blocking, callers may want to avoid calling it from
+ * the main thread of an application.
+ *
+ * @param startMessageId first message identifier as specified in TS 23.041
+ * @param endMessageId last message identifier as specified in TS 23.041
+ * @return true if successful, false otherwise
+ *
+ * @see #enableCellBroadcastRange(int, int)
+ *
+ * {@hide}
+ */
+ public boolean disableCellBroadcastRange(int startMessageId, int endMessageId) {
+ boolean success = false;
+
+ try {
+ ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
+ if (iccISms != null) {
+ success = iccISms.disableCellBroadcastRange(startMessageId, endMessageId);
+ }
+ } catch (RemoteException ex) {
+ // ignore it
+ }
+
+ return success;
+ }
+
/**
* Create a list of SmsMessages from a list of RawSmsData
* records returned by getAllMessagesFromIcc()
diff --git a/telephony/java/com/android/internal/telephony/GsmAlphabet.java b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
index c7c91e4d9e993..a3f4f1fe2f22d 100644
--- a/telephony/java/com/android/internal/telephony/GsmAlphabet.java
+++ b/telephony/java/com/android/internal/telephony/GsmAlphabet.java
@@ -86,6 +86,8 @@ public class GsmAlphabet {
* GSM_EXTENDED_ESCAPE if this character is in the extended table.
* In this case, you must call charToGsmExtended() for the value
* that should follow GSM_EXTENDED_ESCAPE in the GSM alphabet string.
+ * @param c the character to convert
+ * @return the GSM 7 bit table index for the specified character
*/
public static int
charToGsm(char c) {
@@ -99,12 +101,15 @@ public class GsmAlphabet {
/**
* Converts a char to a GSM 7 bit table index.
+ * Returns GSM_EXTENDED_ESCAPE if this character is in the extended table.
+ * In this case, you must call charToGsmExtended() for the value that
+ * should follow GSM_EXTENDED_ESCAPE in the GSM alphabet string.
+ *
+ * @param c the character to convert
* @param throwException If true, throws EncodeException on invalid char.
* If false, returns GSM alphabet ' ' char.
- *
- * Returns GSM_EXTENDED_ESCAPE if this character is in the extended table
- * In this case, you must call charToGsmExtended() for the value that
- * should follow GSM_EXTENDED_ESCAPE in the GSM alphabet string
+ * @throws EncodeException encode error when throwException is true
+ * @return the GSM 7 bit table index for the specified character
*/
public static int
charToGsm(char c, boolean throwException) throws EncodeException {
@@ -133,6 +138,8 @@ public class GsmAlphabet {
* Converts a char to an extended GSM 7 bit table index.
* Extended chars should be escaped with GSM_EXTENDED_ESCAPE.
* Returns ' ' in GSM alphabet if there's no possible match.
+ * @param c the character to convert
+ * @return the GSM 7 bit extended table index for the specified character
*/
public static int
charToGsmExtended(char c) {
@@ -155,6 +162,9 @@ public class GsmAlphabet {
* gsmExtendedToChar().
*
* If an unmappable value is passed (one greater than 127), ' ' is returned.
+ *
+ * @param gsmChar the GSM 7 bit table index to convert
+ * @return the decoded character
*/
public static char
gsmToChar(int gsmChar) {
@@ -174,6 +184,9 @@ public class GsmAlphabet {
*
* If an unmappable value is passed, the character from the GSM 7 bit
* default table will be used (table 6.2.1.1 of TS 23.038).
+ *
+ * @param gsmChar the GSM 7 bit extended table index to convert
+ * @return the decoded character
*/
public static char
gsmExtendedToChar(int gsmChar) {
@@ -232,6 +245,26 @@ public class GsmAlphabet {
return ret;
}
+ /**
+ * Converts a String into a byte array containing
+ * the 7-bit packed GSM Alphabet representation of the string.
+ *
+ * Unencodable chars are encoded as spaces
+ *
+ * Byte 0 in the returned byte array is the count of septets used
+ * The returned byte array is the minimum size required to store
+ * the packed septets. The returned array cannot contain more than 255
+ * septets.
+ *
+ * @param data the data string to encode
+ * @return the encoded string
+ * @throws EncodeException if String is too large to encode
+ */
+ public static byte[] stringToGsm7BitPacked(String data)
+ throws EncodeException {
+ return stringToGsm7BitPacked(data, 0, true, 0, 0);
+ }
+
/**
* Converts a String into a byte array containing
* the 7-bit packed GSM Alphabet representation of the string.
@@ -449,6 +482,11 @@ public class GsmAlphabet {
*
* Field may be padded with trailing 0xff's. The decode stops
* at the first 0xff encountered.
+ *
+ * @param data the byte array to decode
+ * @param offset array offset for the first character to decode
+ * @param length the number of bytes to decode
+ * @return the decoded string
*/
public static String
gsm8BitUnpackedToString(byte[] data, int offset, int length) {
@@ -532,6 +570,8 @@ public class GsmAlphabet {
/**
* Convert a string into an 8-bit unpacked GSM alphabet byte array.
* Always uses GSM default 7-bit alphabet and extension table.
+ * @param s the string to encode
+ * @return the 8-bit GSM encoded byte array for the string
*/
public static byte[]
stringToGsm8BitPacked(String s) {
@@ -550,11 +590,13 @@ public class GsmAlphabet {
/**
* Write a String into a GSM 8-bit unpacked field of
- * @param length size at @param offset in @param dest
- *
* Field is padded with 0xff's, string is truncated if necessary
+ *
+ * @param s the string to encode
+ * @param dest the destination byte array
+ * @param offset the starting offset for the encoded string
+ * @param length the maximum number of bytes to write
*/
-
public static void
stringToGsm8BitUnpackedField(String s, byte dest[], int offset, int length) {
int outByteIndex = offset;
@@ -596,6 +638,8 @@ public class GsmAlphabet {
/**
* Returns the count of 7-bit GSM alphabet characters
* needed to represent this character. Counts unencodable char as 1 septet.
+ * @param c the character to examine
+ * @return the number of septets for this character
*/
public static int
countGsmSeptets(char c) {
@@ -610,8 +654,11 @@ public class GsmAlphabet {
/**
* Returns the count of 7-bit GSM alphabet characters
* needed to represent this character using the default 7 bit GSM alphabet.
+ * @param c the character to examine
* @param throwsException If true, throws EncodeException if unencodable
- * char. Otherwise, counts invalid char as 1 septet
+ * char. Otherwise, counts invalid char as 1 septet.
+ * @return the number of septets for this character
+ * @throws EncodeException the character can't be encoded and throwsException is true
*/
public static int
countGsmSeptets(char c, boolean throwsException) throws EncodeException {
diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl
index 90de5e1c9a7d6..735f986917bc1 100644
--- a/telephony/java/com/android/internal/telephony/ISms.aidl
+++ b/telephony/java/com/android/internal/telephony/ISms.aidl
@@ -170,4 +170,32 @@ interface ISms {
*/
boolean disableCellBroadcast(int messageIdentifier);
+ /**
+ * Enable reception of cell broadcast (SMS-CB) messages with the given
+ * message identifier range. Note that if two different clients enable
+ * a message identifier range, they must both disable it for the device
+ * to stop receiving those messages.
+ *
+ * @param startMessageId first message identifier as specified in TS 23.041
+ * @param endMessageId last message identifier as specified in TS 23.041
+ * @return true if successful, false otherwise
+ *
+ * @see #disableCellBroadcastRange(int, int)
+ */
+ boolean enableCellBroadcastRange(int startMessageId, int endMessageId);
+
+ /**
+ * Disable reception of cell broadcast (SMS-CB) messages with the given
+ * message identifier range. Note that if two different clients enable
+ * a message identifier range, they must both disable it for the device
+ * to stop receiving those messages.
+ *
+ * @param startMessageId first message identifier as specified in TS 23.041
+ * @param endMessageId last message identifier as specified in TS 23.041
+ * @return true if successful, false otherwise
+ *
+ * @see #enableCellBroadcastRange(int, int)
+ */
+ boolean disableCellBroadcastRange(int startMessageId, int endMessageId);
+
}
diff --git a/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java b/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java
index 5049249053ba8..54de508c04dee 100644
--- a/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java
+++ b/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java
@@ -76,4 +76,13 @@ public class IccSmsInterfaceManagerProxy extends ISms.Stub {
return mIccSmsInterfaceManager.disableCellBroadcast(messageIdentifier);
}
+ public boolean enableCellBroadcastRange(int startMessageId, int endMessageId)
+ throws android.os.RemoteException {
+ return mIccSmsInterfaceManager.enableCellBroadcastRange(startMessageId, endMessageId);
+ }
+
+ public boolean disableCellBroadcastRange(int startMessageId, int endMessageId)
+ throws android.os.RemoteException {
+ return mIccSmsInterfaceManager.disableCellBroadcastRange(startMessageId, endMessageId);
+ }
}
diff --git a/telephony/java/com/android/internal/telephony/IntRangeManager.java b/telephony/java/com/android/internal/telephony/IntRangeManager.java
new file mode 100644
index 0000000000000..889e2b1cde36d
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/IntRangeManager.java
@@ -0,0 +1,560 @@
+/*
+ * 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 com.android.internal.telephony;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+
+/**
+ * Clients can enable reception of SMS-CB messages for specific ranges of
+ * message identifiers (channels). This class keeps track of the currently
+ * enabled message identifiers and calls abstract methods to update the
+ * radio when the range of enabled message identifiers changes.
+ *
+ * An update is a call to {@link #startUpdate} followed by zero or more
+ * calls to {@link #addRange} followed by a call to {@link #finishUpdate}.
+ * Calls to {@link #enableRange} and {@link #disableRange} will perform
+ * an incremental update operation if the enabled ranges have changed.
+ * A full update operation (i.e. after a radio reset) can be performed
+ * by a call to {@link #updateRanges}.
+ *
+ * Clients are identified by String (the name associated with the User ID
+ * of the caller) so that a call to remove a range can be mapped to the
+ * client that enabled that range (or else rejected).
+ */
+public abstract class IntRangeManager {
+
+ /**
+ * Initial capacity for IntRange clients array list. There will be
+ * few cell broadcast listeners on a typical device, so this can be small.
+ */
+ private static final int INITIAL_CLIENTS_ARRAY_SIZE = 4;
+
+ /**
+ * One or more clients forming the continuous range [startId, endId].
+ * When a client is added, the IntRange may merge with one or more
+ * adjacent IntRanges to form a single combined IntRange.
+ *
When a client is removed, the IntRange may divide into several
+ * non-contiguous IntRanges.
+ */
+ private class IntRange {
+ int startId;
+ int endId;
+ // sorted by earliest start id
+ final ArrayList clients;
+
+ /**
+ * Create a new IntRange with a single client.
+ * @param startId the first id included in the range
+ * @param endId the last id included in the range
+ * @param client the client requesting the enabled range
+ */
+ IntRange(int startId, int endId, String client) {
+ this.startId = startId;
+ this.endId = endId;
+ clients = new ArrayList(INITIAL_CLIENTS_ARRAY_SIZE);
+ clients.add(new ClientRange(startId, endId, client));
+ }
+
+ /**
+ * Create a new IntRange for an existing ClientRange.
+ * @param clientRange the initial ClientRange to add
+ */
+ IntRange(ClientRange clientRange) {
+ startId = clientRange.startId;
+ endId = clientRange.endId;
+ clients = new ArrayList(INITIAL_CLIENTS_ARRAY_SIZE);
+ clients.add(clientRange);
+ }
+
+ /**
+ * Create a new IntRange from an existing IntRange. This is used for
+ * removing a ClientRange, because new IntRanges may need to be created
+ * for any gaps that open up after the ClientRange is removed. A copy
+ * is made of the elements of the original IntRange preceding the element
+ * that is being removed. The following elements will be added to this
+ * IntRange or to a new IntRange when a gap is found.
+ * @param intRange the original IntRange to copy elements from
+ * @param numElements the number of elements to copy from the original
+ */
+ IntRange(IntRange intRange, int numElements) {
+ this.startId = intRange.startId;
+ this.endId = intRange.endId;
+ this.clients = new ArrayList(intRange.clients.size());
+ for (int i=0; i < numElements; i++) {
+ this.clients.add(intRange.clients.get(i));
+ }
+ }
+
+ /**
+ * Insert new ClientRange in order by start id.
+ * If the new ClientRange is known to be sorted before or after the
+ * existing ClientRanges, or at a particular index, it can be added
+ * to the clients array list directly, instead of via this method.
+ *
Note that this can be changed from linear to binary search if the
+ * number of clients grows large enough that it would make a difference.
+ * @param range the new ClientRange to insert
+ */
+ void insert(ClientRange range) {
+ int len = clients.size();
+ for (int i=0; i < len; i++) {
+ ClientRange nextRange = clients.get(i);
+ if (range.startId <= nextRange.startId) {
+ // ignore duplicate ranges from the same client
+ if (!range.equals(nextRange)) {
+ clients.add(i, range);
+ }
+ return;
+ }
+ }
+ clients.add(range); // append to end of list
+ }
+ }
+
+ /**
+ * The message id range for a single client.
+ */
+ private class ClientRange {
+ final int startId;
+ final int endId;
+ final String client;
+
+ ClientRange(int startId, int endId, String client) {
+ this.startId = startId;
+ this.endId = endId;
+ this.client = client;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o != null && o instanceof ClientRange) {
+ ClientRange other = (ClientRange) o;
+ return startId == other.startId &&
+ endId == other.endId &&
+ client.equals(other.client);
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ public int hashCode() {
+ return (startId * 31 + endId) * 31 + client.hashCode();
+ }
+ }
+
+ /**
+ * List of integer ranges, one per client, sorted by start id.
+ */
+ private ArrayList mRanges = new ArrayList();
+
+ protected IntRangeManager() {}
+
+ /**
+ * Enable a range for the specified client and update ranges
+ * if necessary. If {@link #finishUpdate} returns failure,
+ * false is returned and the range is not added.
+ *
+ * @param startId the first id included in the range
+ * @param endId the last id included in the range
+ * @param client the client requesting the enabled range
+ * @return true if successful, false otherwise
+ */
+ public synchronized boolean enableRange(int startId, int endId, String client) {
+ int len = mRanges.size();
+
+ // empty range list: add the initial IntRange
+ if (len == 0) {
+ if (tryAddSingleRange(startId, endId, true)) {
+ mRanges.add(new IntRange(startId, endId, client));
+ return true;
+ } else {
+ return false; // failed to update radio
+ }
+ }
+
+ for (int startIndex = 0; startIndex < len; startIndex++) {
+ IntRange range = mRanges.get(startIndex);
+ if (startId < range.startId) {
+ // test if new range completely precedes this range
+ // note that [1, 4] and [5, 6] coalesce to [1, 6]
+ if ((endId + 1) < range.startId) {
+ // insert new int range before previous first range
+ if (tryAddSingleRange(startId, endId, true)) {
+ mRanges.add(startIndex, new IntRange(startId, endId, client));
+ return true;
+ } else {
+ return false; // failed to update radio
+ }
+ } else if (endId <= range.endId) {
+ // extend the start of this range
+ if (tryAddSingleRange(startId, range.startId - 1, true)) {
+ range.startId = startId;
+ range.clients.add(0, new ClientRange(startId, endId, client));
+ return true;
+ } else {
+ return false; // failed to update radio
+ }
+ } else {
+ // find last range that can coalesce into the new combined range
+ for (int endIndex = startIndex+1; endIndex < len; endIndex++) {
+ IntRange endRange = mRanges.get(endIndex);
+ if ((endId + 1) < endRange.startId) {
+ // try to add entire new range
+ if (tryAddSingleRange(startId, endId, true)) {
+ range.startId = startId;
+ range.endId = endId;
+ // insert new ClientRange before existing ranges
+ range.clients.add(0, new ClientRange(startId, endId, client));
+ // coalesce range with following ranges up to endIndex-1
+ // remove each range after adding its elements, so the index
+ // of the next range to join is always startIndex+1.
+ // i is the index if no elements were removed: we only care
+ // about the number of loop iterations, not the value of i.
+ int joinIndex = startIndex + 1;
+ for (int i = joinIndex; i < endIndex; i++) {
+ IntRange joinRange = mRanges.get(joinIndex);
+ range.clients.addAll(joinRange.clients);
+ mRanges.remove(joinRange);
+ }
+ return true;
+ } else {
+ return false; // failed to update radio
+ }
+ } else if (endId <= endRange.endId) {
+ // add range from start id to start of last overlapping range,
+ // values from endRange.startId to endId are already enabled
+ if (tryAddSingleRange(startId, endRange.startId - 1, true)) {
+ range.startId = startId;
+ range.endId = endRange.endId;
+ // insert new ClientRange before existing ranges
+ range.clients.add(0, new ClientRange(startId, endId, client));
+ // coalesce range with following ranges up to endIndex
+ // remove each range after adding its elements, so the index
+ // of the next range to join is always startIndex+1.
+ // i is the index if no elements were removed: we only care
+ // about the number of loop iterations, not the value of i.
+ int joinIndex = startIndex + 1;
+ for (int i = joinIndex; i <= endIndex; i++) {
+ IntRange joinRange = mRanges.get(joinIndex);
+ range.clients.addAll(joinRange.clients);
+ mRanges.remove(joinRange);
+ }
+ return true;
+ } else {
+ return false; // failed to update radio
+ }
+ }
+ }
+
+ // endId extends past all existing IntRanges: combine them all together
+ if (tryAddSingleRange(startId, endId, true)) {
+ range.startId = startId;
+ range.endId = endId;
+ // insert new ClientRange before existing ranges
+ range.clients.add(0, new ClientRange(startId, endId, client));
+ // coalesce range with following ranges up to len-1
+ // remove each range after adding its elements, so the index
+ // of the next range to join is always startIndex+1.
+ // i is the index if no elements were removed: we only care
+ // about the number of loop iterations, not the value of i.
+ int joinIndex = startIndex + 1;
+ for (int i = joinIndex; i < len; i++) {
+ IntRange joinRange = mRanges.get(joinIndex);
+ range.clients.addAll(joinRange.clients);
+ mRanges.remove(joinRange);
+ }
+ return true;
+ } else {
+ return false; // failed to update radio
+ }
+ }
+ } else if ((startId + 1) <= range.endId) {
+ if (endId <= range.endId) {
+ // completely contained in existing range; no radio changes
+ range.insert(new ClientRange(startId, endId, client));
+ return true;
+ } else {
+ // find last range that can coalesce into the new combined range
+ for (int endIndex = startIndex+1; endIndex < len; endIndex++) {
+ IntRange endRange = mRanges.get(endIndex);
+ if ((endId + 1) < endRange.startId) {
+ // add range from range.endId+1 to endId,
+ // values from startId to range.endId are already enabled
+ if (tryAddSingleRange(range.endId + 1, endId, true)) {
+ range.endId = endId;
+ // insert new ClientRange in place
+ range.insert(new ClientRange(startId, endId, client));
+ // coalesce range with following ranges up to endIndex-1
+ // remove each range after adding its elements, so the index
+ // of the next range to join is always startIndex+1.
+ // i is the index if no elements were removed: we only care
+ // about the number of loop iterations, not the value of i.
+ int joinIndex = startIndex + 1;
+ for (int i = joinIndex; i < endIndex; i++) {
+ IntRange joinRange = mRanges.get(joinIndex);
+ range.clients.addAll(joinRange.clients);
+ mRanges.remove(joinRange);
+ }
+ return true;
+ } else {
+ return false; // failed to update radio
+ }
+ } else if (endId <= endRange.endId) {
+ // add range from range.endId+1 to start of last overlapping range,
+ // values from endRange.startId to endId are already enabled
+ if (tryAddSingleRange(range.endId + 1, endRange.startId - 1, true)) {
+ range.endId = endRange.endId;
+ // insert new ClientRange in place
+ range.insert(new ClientRange(startId, endId, client));
+ // coalesce range with following ranges up to endIndex
+ // remove each range after adding its elements, so the index
+ // of the next range to join is always startIndex+1.
+ // i is the index if no elements were removed: we only care
+ // about the number of loop iterations, not the value of i.
+ int joinIndex = startIndex + 1;
+ for (int i = joinIndex; i <= endIndex; i++) {
+ IntRange joinRange = mRanges.get(joinIndex);
+ range.clients.addAll(joinRange.clients);
+ mRanges.remove(joinRange);
+ }
+ return true;
+ } else {
+ return false; // failed to update radio
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // append new range after existing IntRanges
+ if (tryAddSingleRange(startId, endId, true)) {
+ mRanges.add(new IntRange(startId, endId, client));
+ return true;
+ } else {
+ return false; // failed to update radio
+ }
+ }
+
+ /**
+ * Disable a range for the specified client and update ranges
+ * if necessary. If {@link #finishUpdate} returns failure,
+ * false is returned and the range is not removed.
+ *
+ * @param startId the first id included in the range
+ * @param endId the last id included in the range
+ * @param client the client requesting to disable the range
+ * @return true if successful, false otherwise
+ */
+ public synchronized boolean disableRange(int startId, int endId, String client) {
+ int len = mRanges.size();
+
+ for (int i=0; i < len; i++) {
+ IntRange range = mRanges.get(i);
+ if (startId < range.startId) {
+ return false; // not found
+ } else if (endId <= range.endId) {
+ // found the IntRange that encloses the client range, if any
+ // search for it in the clients list
+ ArrayList clients = range.clients;
+
+ // handle common case of IntRange containing one ClientRange
+ int crLength = clients.size();
+ if (crLength == 1) {
+ ClientRange cr = clients.get(0);
+ if (cr.startId == startId && cr.endId == endId && cr.client.equals(client)) {
+ // disable range in radio then remove the entire IntRange
+ if (tryAddSingleRange(startId, endId, false)) {
+ mRanges.remove(i);
+ return true;
+ } else {
+ return false; // failed to update radio
+ }
+ } else {
+ return false; // not found
+ }
+ }
+
+ // several ClientRanges: remove one, potentially splitting into many IntRanges.
+ // Save the original start and end id for the original IntRange
+ // in case the radio update fails and we have to revert it. If the
+ // update succeeds, we remove the client range and insert the new IntRanges.
+ int largestEndId = Integer.MIN_VALUE; // largest end identifier found
+ boolean updateStarted = false;
+
+ for (int crIndex=0; crIndex < crLength; crIndex++) {
+ ClientRange cr = clients.get(crIndex);
+ if (cr.startId == startId && cr.endId == endId && cr.client.equals(client)) {
+ // found the ClientRange to remove, check if it's the last in the list
+ if (crIndex == crLength - 1) {
+ if (range.endId == largestEndId) {
+ // no channels to remove from radio; return success
+ clients.remove(crIndex);
+ return true;
+ } else {
+ // disable the channels at the end and lower the end id
+ if (tryAddSingleRange(largestEndId + 1, range.endId, false)) {
+ clients.remove(crIndex);
+ range.endId = largestEndId;
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ // copy the IntRange so that we can remove elements and modify the
+ // start and end id's in the copy, leaving the original unmodified
+ // until after the radio update succeeds
+ IntRange rangeCopy = new IntRange(range, crIndex);
+
+ if (crIndex == 0) {
+ // removing the first ClientRange, so we may need to increase
+ // the start id of the IntRange.
+ // We know there are at least two ClientRanges in the list,
+ // so clients.get(1) should always succeed.
+ int nextStartId = clients.get(1).startId;
+ if (nextStartId != range.startId) {
+ startUpdate();
+ updateStarted = true;
+ addRange(range.startId, nextStartId - 1, false);
+ rangeCopy.startId = nextStartId;
+ }
+ }
+
+ // go through remaining ClientRanges, creating new IntRanges when
+ // there is a gap in the sequence. After radio update succeeds,
+ // remove the original IntRange and append newRanges to mRanges.
+ // Otherwise, leave the original IntRange in mRanges and return false.
+ ArrayList newRanges = new ArrayList();
+ newRanges.add(rangeCopy);
+
+ IntRange currentRange = rangeCopy;
+ for (int nextIndex = crIndex + 1; nextIndex < crLength; nextIndex++) {
+ ClientRange nextCr = clients.get(nextIndex);
+ if (nextCr.startId > largestEndId + 1) {
+ if (!updateStarted) {
+ startUpdate();
+ updateStarted = true;
+ }
+ addRange(largestEndId + 1, nextCr.startId - 1, false);
+ currentRange.endId = largestEndId;
+ currentRange = new IntRange(nextCr);
+ } else {
+ currentRange.clients.add(nextCr);
+ }
+ if (nextCr.endId > largestEndId) {
+ largestEndId = nextCr.endId;
+ }
+ }
+
+ if (updateStarted) {
+ if (!finishUpdate()) {
+ return false; // failed to update radio
+ } else {
+ // remove the original IntRange and insert newRanges in place.
+ mRanges.remove(crIndex);
+ mRanges.addAll(crIndex, newRanges);
+ return true;
+ }
+ } else {
+ return true;
+ }
+ } else {
+ // not the ClientRange to remove; save highest end ID seen so far
+ if (cr.endId > largestEndId) {
+ largestEndId = cr.endId;
+ }
+ }
+ }
+ }
+ }
+
+ return false; // not found
+ }
+
+ /**
+ * Perform a complete update operation (enable all ranges). Useful
+ * after a radio reset. Calls {@link #startUpdate}, followed by zero or
+ * more calls to {@link #addRange}, followed by {@link #finishUpdate}.
+ * @return true if successful, false otherwise
+ */
+ public boolean updateRanges() {
+ startUpdate();
+ Iterator iterator = mRanges.iterator();
+ if (iterator.hasNext()) {
+ IntRange range = iterator.next();
+ int start = range.startId;
+ int end = range.endId;
+ // accumulate ranges of [startId, endId]
+ while (iterator.hasNext()) {
+ IntRange nextNode = iterator.next();
+ // [startIdA, endIdA], [endIdA + 1, endIdB] -> [startIdA, endIdB]
+ if (nextNode.startId <= (end + 1)) {
+ if (nextNode.endId > end) {
+ end = nextNode.endId;
+ }
+ } else {
+ addRange(start, end, true);
+ start = nextNode.startId;
+ end = nextNode.endId;
+ }
+ }
+ // add final range
+ addRange(start, end, true);
+ }
+ return finishUpdate();
+ }
+
+ /**
+ * Enable or disable a single range of message identifiers.
+ * @param startId the first id included in the range
+ * @param endId the last id included in the range
+ * @param selected true to enable range, false to disable range
+ * @return true if successful, false otherwise
+ */
+ private boolean tryAddSingleRange(int startId, int endId, boolean selected) {
+ startUpdate();
+ addRange(startId, endId, selected);
+ return finishUpdate();
+ }
+
+ /**
+ * Called when the list of enabled ranges has changed. This will be
+ * followed by zero or more calls to {@link #addRange} followed by
+ * a call to {@link #finishUpdate}.
+ */
+ protected abstract void startUpdate();
+
+ /**
+ * Called after {@link #startUpdate} to indicate a range of enabled
+ * or disabled values.
+ *
+ * @param startId the first id included in the range
+ * @param endId the last id included in the range
+ * @param selected true to enable range, false to disable range
+ */
+ protected abstract void addRange(int startId, int endId, boolean selected);
+
+ /**
+ * Called to indicate the end of a range update started by the
+ * previous call to {@link #startUpdate}.
+ * @return true if successful, false otherwise
+ */
+ protected abstract boolean finishUpdate();
+}
diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
old mode 100755
new mode 100644
index 8efc9aa4bd25f..76e719c3c8954
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -32,11 +32,9 @@ import android.database.Cursor;
import android.database.SQLException;
import android.net.Uri;
import android.os.AsyncResult;
-import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
-import android.os.StatFs;
import android.os.SystemProperties;
import android.provider.Telephony;
import android.provider.Telephony.Sms.Intents;
@@ -907,37 +905,6 @@ public abstract class SMSDispatcher extends Handler {
*/
protected abstract void sendMultipartSms (SmsTracker tracker);
- /**
- * Activate or deactivate cell broadcast SMS.
- *
- * @param activate
- * 0 = activate, 1 = deactivate
- * @param response
- * Callback message is empty on completion
- */
- public abstract void activateCellBroadcastSms(int activate, Message response);
-
- /**
- * Query the current configuration of cell broadcast SMS.
- *
- * @param response
- * Callback message contains the configuration from the modem on completion
- * @see #setCellBroadcastConfig
- */
- public abstract void getCellBroadcastSmsConfig(Message response);
-
- /**
- * Configure cell broadcast SMS.
- *
- * @param configValuesArray
- * The first element defines the number of triples that follow.
- * A triple is made up of the service category, the language identifier
- * and a boolean that specifies whether the category is set active.
- * @param response
- * Callback message is empty on completion
- */
- public abstract void setCellBroadcastConfig(int[] configValuesArray, Message response);
-
/**
* Send an acknowledge message.
* @param success indicates that last message was successfully received.
@@ -1065,14 +1032,17 @@ public abstract class SMSDispatcher extends Handler {
protected abstract void handleBroadcastSms(AsyncResult ar);
- protected void dispatchBroadcastPdus(byte[][] pdus) {
- Intent intent = new Intent("android.provider.telephony.SMS_CB_RECEIVED");
- intent.putExtra("pdus", pdus);
-
- if (false)
+ protected void dispatchBroadcastPdus(byte[][] pdus, boolean isEmergencyMessage) {
+ if (isEmergencyMessage) {
+ Intent intent = new Intent(Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION);
+ intent.putExtra("pdus", pdus);
+ Log.d(TAG, "Dispatching " + pdus.length + " emergency SMS CB pdus");
+ dispatch(intent, "android.permission.RECEIVE_EMERGENCY_BROADCAST");
+ } else {
+ Intent intent = new Intent(Intents.SMS_CB_RECEIVED_ACTION);
+ intent.putExtra("pdus", pdus);
Log.d(TAG, "Dispatching " + pdus.length + " SMS CB pdus");
-
- dispatch(intent, "android.permission.RECEIVE_SMS");
+ dispatch(intent, "android.permission.RECEIVE_SMS");
+ }
}
-
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
index a283062cc674c..bac15a6901d3b 100755
--- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java
@@ -1121,7 +1121,8 @@ public class CDMAPhone extends PhoneBase {
* @param response Callback message is empty on completion
*/
public void activateCellBroadcastSms(int activate, Message response) {
- mSMS.activateCellBroadcastSms(activate, response);
+ Log.e(LOG_TAG, "[CDMAPhone] activateCellBroadcastSms() is obsolete; use SmsManager");
+ response.sendToTarget();
}
/**
@@ -1130,7 +1131,8 @@ public class CDMAPhone extends PhoneBase {
* @param response Callback message is empty on completion
*/
public void getCellBroadcastSmsConfig(Message response) {
- mSMS.getCellBroadcastSmsConfig(response);
+ Log.e(LOG_TAG, "[CDMAPhone] getCellBroadcastSmsConfig() is obsolete; use SmsManager");
+ response.sendToTarget();
}
/**
@@ -1139,7 +1141,8 @@ public class CDMAPhone extends PhoneBase {
* @param response Callback message is empty on completion
*/
public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response) {
- mSMS.setCellBroadcastConfig(configValuesArray, response);
+ Log.e(LOG_TAG, "[CDMAPhone] setCellBroadcastSmsConfig() is obsolete; use SmsManager");
+ response.sendToTarget();
}
/**
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index cf6b78ab020de..29349db646d81 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -273,7 +273,7 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
if (cursorCount != totalSegments - 1) {
// We don't have all the parts yet, store this one away
ContentValues values = new ContentValues();
- values.put("date", new Long(0));
+ values.put("date", (long) 0);
values.put("pdu", HexDump.toHexString(pdu, index, pdu.length - index));
values.put("address", address);
values.put("reference_number", referenceNumber);
@@ -482,24 +482,6 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
}
}
- /** {@inheritDoc} */
- @Override
- public void activateCellBroadcastSms(int activate, Message response) {
- mCm.setCdmaBroadcastActivation((activate == 0), response);
- }
-
- /** {@inheritDoc} */
- @Override
- public void getCellBroadcastSmsConfig(Message response) {
- mCm.getCdmaBroadcastConfig(response);
- }
-
- /** {@inheritDoc} */
- @Override
- public void setCellBroadcastConfig(int[] configValuesArray, Message response) {
- mCm.setCdmaBroadcastConfig(configValuesArray, response);
- }
-
protected void handleBroadcastSms(AsyncResult ar) {
// Not supported
Log.e(TAG, "Error! Not implemented for CDMA.");
diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
index 29f3bc1942a8a..9cd059d3e3973 100644
--- a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
@@ -203,6 +203,18 @@ public class RuimSmsInterfaceManager extends IccSmsInterfaceManager {
return false;
}
+ public boolean enableCellBroadcastRange(int startMessageId, int endMessageId) {
+ // Not implemented
+ Log.e(LOG_TAG, "Error! Not implemented for CDMA.");
+ return false;
+ }
+
+ public boolean disableCellBroadcastRange(int startMessageId, int endMessageId) {
+ // Not implemented
+ Log.e(LOG_TAG, "Error! Not implemented for CDMA.");
+ return false;
+ }
+
protected void log(String msg) {
Log.d(LOG_TAG, "[RuimSmsInterfaceManager] " + msg);
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
index 3ccc03d680b8a..48debd403a700 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java
@@ -1427,16 +1427,35 @@ public class GSMPhone extends PhoneBase {
return this.mIccFileHandler;
}
+ /**
+ * Activate or deactivate cell broadcast SMS.
+ *
+ * @param activate 0 = activate, 1 = deactivate
+ * @param response Callback message is empty on completion
+ */
public void activateCellBroadcastSms(int activate, Message response) {
- Log.e(LOG_TAG, "Error! This functionality is not implemented for GSM.");
+ Log.e(LOG_TAG, "[GSMPhone] activateCellBroadcastSms() is obsolete; use SmsManager");
+ response.sendToTarget();
}
+ /**
+ * Query the current configuration of cdma cell broadcast SMS.
+ *
+ * @param response Callback message is empty on completion
+ */
public void getCellBroadcastSmsConfig(Message response) {
- Log.e(LOG_TAG, "Error! This functionality is not implemented for GSM.");
+ Log.e(LOG_TAG, "[GSMPhone] getCellBroadcastSmsConfig() is obsolete; use SmsManager");
+ response.sendToTarget();
}
- public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response){
- Log.e(LOG_TAG, "Error! This functionality is not implemented for GSM.");
+ /**
+ * Configure cdma cell broadcast SMS.
+ *
+ * @param response Callback message is empty on completion
+ */
+ public void setCellBroadcastSmsConfig(int[] configValuesArray, Message response) {
+ Log.e(LOG_TAG, "[GSMPhone] setCellBroadcastSmsConfig() is obsolete; use SmsManager");
+ response.sendToTarget();
}
public boolean isCspPlmnEnabled() {
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
old mode 100755
new mode 100644
index b021967c61129..52ca453047140
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -391,30 +391,6 @@ final class GsmSMSDispatcher extends SMSDispatcher {
}
}
- /** {@inheritDoc} */
- @Override
- public void activateCellBroadcastSms(int activate, Message response) {
- // Unless CBS is implemented for GSM, this point should be unreachable.
- Log.e(TAG, "Error! The functionality cell broadcast sms is not implemented for GSM.");
- response.recycle();
- }
-
- /** {@inheritDoc} */
- @Override
- public void getCellBroadcastSmsConfig(Message response){
- // Unless CBS is implemented for GSM, this point should be unreachable.
- Log.e(TAG, "Error! The functionality cell broadcast sms is not implemented for GSM.");
- response.recycle();
- }
-
- /** {@inheritDoc} */
- @Override
- public void setCellBroadcastConfig(int[] configValuesArray, Message response) {
- // Unless CBS is implemented for GSM, this point should be unreachable.
- Log.e(TAG, "Error! The functionality cell broadcast sms is not implemented for GSM.");
- response.recycle();
- }
-
private int resultToCause(int rc) {
switch (rc) {
case Activity.RESULT_OK:
@@ -506,9 +482,10 @@ final class GsmSMSDispatcher extends SMSDispatcher {
}
// This map holds incomplete concatenated messages waiting for assembly
- private HashMap mSmsCbPageMap =
+ private final HashMap mSmsCbPageMap =
new HashMap();
+ @Override
protected void handleBroadcastSms(AsyncResult ar) {
try {
byte[][] pdus = null;
@@ -520,9 +497,9 @@ final class GsmSMSDispatcher extends SMSDispatcher {
for (int j = i; j < i + 8 && j < receivedPdu.length; j++) {
int b = receivedPdu[j] & 0xff;
if (b < 0x10) {
- sb.append("0");
+ sb.append('0');
}
- sb.append(Integer.toHexString(b)).append(" ");
+ sb.append(Integer.toHexString(b)).append(' ');
}
Log.d(TAG, sb.toString());
}
@@ -567,7 +544,8 @@ final class GsmSMSDispatcher extends SMSDispatcher {
pdus[0] = receivedPdu;
}
- dispatchBroadcastPdus(pdus);
+ boolean isEmergencyMessage = SmsCbHeader.isEmergencyMessage(header.messageIdentifier);
+ dispatchBroadcastPdus(pdus, isEmergencyMessage);
// Remove messages that are out of scope to prevent the map from
// growing indefinitely, containing incomplete messages that were
diff --git a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
index 5abba6e9fe8ca..8d0e5d3eefda6 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
@@ -27,6 +27,7 @@ import android.util.Log;
import com.android.internal.telephony.IccConstants;
import com.android.internal.telephony.IccSmsInterfaceManager;
import com.android.internal.telephony.IccUtils;
+import com.android.internal.telephony.IntRangeManager;
import com.android.internal.telephony.SMSDispatcher;
import com.android.internal.telephony.SmsRawData;
@@ -53,6 +54,9 @@ public class SimSmsInterfaceManager extends IccSmsInterfaceManager {
private HashMap> mCellBroadcastSubscriptions =
new HashMap>();
+ private CellBroadcastRangeManager mCellBroadcastRangeManager =
+ new CellBroadcastRangeManager();
+
private static final int EVENT_LOAD_DONE = 1;
private static final int EVENT_UPDATE_DONE = 2;
private static final int EVENT_SET_BROADCAST_ACTIVATION_DONE = 3;
@@ -213,7 +217,15 @@ public class SimSmsInterfaceManager extends IccSmsInterfaceManager {
}
public boolean enableCellBroadcast(int messageIdentifier) {
- if (DBG) log("enableCellBroadcast");
+ return enableCellBroadcastRange(messageIdentifier, messageIdentifier);
+ }
+
+ public boolean disableCellBroadcast(int messageIdentifier) {
+ return disableCellBroadcastRange(messageIdentifier, messageIdentifier);
+ }
+
+ public boolean enableCellBroadcastRange(int startMessageId, int endMessageId) {
+ if (DBG) log("enableCellBroadcastRange");
Context context = mPhone.getContext();
@@ -223,30 +235,22 @@ public class SimSmsInterfaceManager extends IccSmsInterfaceManager {
String client = context.getPackageManager().getNameForUid(
Binder.getCallingUid());
- HashSet clients = mCellBroadcastSubscriptions.get(messageIdentifier);
- if (clients == null) {
- // This is a new message identifier
- clients = new HashSet();
- mCellBroadcastSubscriptions.put(messageIdentifier, clients);
-
- if (!updateCellBroadcastConfig()) {
- mCellBroadcastSubscriptions.remove(messageIdentifier);
- return false;
- }
+ if (!mCellBroadcastRangeManager.enableRange(startMessageId, endMessageId, client)) {
+ log("Failed to add cell broadcast subscription for MID range " + startMessageId
+ + " to " + endMessageId + " from client " + client);
+ return false;
}
- clients.add(client);
-
if (DBG)
- log("Added cell broadcast subscription for MID " + messageIdentifier
- + " from client " + client);
+ log("Added cell broadcast subscription for MID range " + startMessageId
+ + " to " + endMessageId + " from client " + client);
return true;
}
- public boolean disableCellBroadcast(int messageIdentifier) {
- if (DBG) log("disableCellBroadcast");
+ public boolean disableCellBroadcastRange(int startMessageId, int endMessageId) {
+ if (DBG) log("disableCellBroadcastRange");
Context context = mPhone.getContext();
@@ -256,39 +260,56 @@ public class SimSmsInterfaceManager extends IccSmsInterfaceManager {
String client = context.getPackageManager().getNameForUid(
Binder.getCallingUid());
- HashSet clients = mCellBroadcastSubscriptions.get(messageIdentifier);
- if (clients != null && clients.remove(client)) {
- if (DBG)
- log("Removed cell broadcast subscription for MID " + messageIdentifier
- + " from client " + client);
-
- if (clients.isEmpty()) {
- mCellBroadcastSubscriptions.remove(messageIdentifier);
- updateCellBroadcastConfig();
- }
- return true;
+ if (!mCellBroadcastRangeManager.disableRange(startMessageId, endMessageId, client)) {
+ log("Failed to remove cell broadcast subscription for MID range " + startMessageId
+ + " to " + endMessageId + " from client " + client);
+ return false;
}
- return false;
+ if (DBG)
+ log("Removed cell broadcast subscription for MID range " + startMessageId
+ + " to " + endMessageId + " from client " + client);
+
+ return true;
}
- private boolean updateCellBroadcastConfig() {
- Set messageIdentifiers = mCellBroadcastSubscriptions.keySet();
+ class CellBroadcastRangeManager extends IntRangeManager {
+ private ArrayList mConfigList =
+ new ArrayList();
- if (messageIdentifiers.size() > 0) {
- SmsBroadcastConfigInfo[] configs =
- new SmsBroadcastConfigInfo[messageIdentifiers.size()];
- int i = 0;
+ /**
+ * Called when the list of enabled ranges has changed. This will be
+ * followed by zero or more calls to {@link #addRange} followed by
+ * a call to {@link #finishUpdate}.
+ */
+ protected void startUpdate() {
+ mConfigList.clear();
+ }
- for (int messageIdentifier : messageIdentifiers) {
- configs[i++] = new SmsBroadcastConfigInfo(messageIdentifier, messageIdentifier,
- SMS_CB_CODE_SCHEME_MIN, SMS_CB_CODE_SCHEME_MAX, true);
+ /**
+ * Called after {@link #startUpdate} to indicate a range of enabled
+ * values.
+ * @param startId the first id included in the range
+ * @param endId the last id included in the range
+ */
+ protected void addRange(int startId, int endId, boolean selected) {
+ mConfigList.add(new SmsBroadcastConfigInfo(startId, endId,
+ SMS_CB_CODE_SCHEME_MIN, SMS_CB_CODE_SCHEME_MAX, selected));
+ }
+
+ /**
+ * Called to indicate the end of a range update started by the
+ * previous call to {@link #startUpdate}.
+ */
+ protected boolean finishUpdate() {
+ if (mConfigList.isEmpty()) {
+ return setCellBroadcastActivation(false);
+ } else {
+ SmsBroadcastConfigInfo[] configs =
+ mConfigList.toArray(new SmsBroadcastConfigInfo[mConfigList.size()]);
+ return setCellBroadcastConfig(configs) && setCellBroadcastActivation(true);
}
-
- return setCellBroadcastConfig(configs) && setCellBroadcastActivation(true);
- } else {
- return setCellBroadcastActivation(false);
}
}
@@ -314,7 +335,7 @@ public class SimSmsInterfaceManager extends IccSmsInterfaceManager {
private boolean setCellBroadcastActivation(boolean activate) {
if (DBG)
- log("Calling setCellBroadcastActivation(" + activate + ")");
+ log("Calling setCellBroadcastActivation(" + activate + ')');
synchronized (mLock) {
Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_ACTIVATION_DONE);
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsBroadcastConfigInfo.java b/telephony/java/com/android/internal/telephony/gsm/SmsBroadcastConfigInfo.java
index 45f50bc49fb18..66e7ce0783cfd 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsBroadcastConfigInfo.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsBroadcastConfigInfo.java
@@ -30,11 +30,11 @@ package com.android.internal.telephony.gsm;
* and 9.4.4.2.3 for UMTS.
* All other values can be treated as empty CBM data coding scheme.
*
- * selected false means message types specified in
- * and are not accepted, while true means accepted.
+ * selected false means message types specified in {@code }
+ * and {@code } are not accepted, while true means accepted.
*
*/
-public class SmsBroadcastConfigInfo {
+public final class SmsBroadcastConfigInfo {
private int fromServiceId;
private int toServiceId;
private int fromCodeScheme;
@@ -46,11 +46,11 @@ public class SmsBroadcastConfigInfo {
*/
public SmsBroadcastConfigInfo(int fromId, int toId, int fromScheme,
int toScheme, boolean selected) {
- setFromServiceId(fromId);
- setToServiceId(toId);
- setFromCodeScheme(fromScheme);
- setToCodeScheme(toScheme);
- this.setSelected(selected);
+ fromServiceId = fromId;
+ toServiceId = toId;
+ fromCodeScheme = fromScheme;
+ toCodeScheme = toScheme;
+ this.selected = selected;
}
/**
@@ -126,8 +126,8 @@ public class SmsBroadcastConfigInfo {
@Override
public String toString() {
return "SmsBroadcastConfigInfo: Id [" +
- getFromServiceId() + "," + getToServiceId() + "] Code [" +
- getFromCodeScheme() + "," + getToCodeScheme() + "] " +
- (isSelected() ? "ENABLED" : "DISABLED");
+ fromServiceId + ',' + toServiceId + "] Code [" +
+ fromCodeScheme + ',' + toCodeScheme + "] " +
+ (selected ? "ENABLED" : "DISABLED");
}
-}
\ No newline at end of file
+}
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
index 0945a3812530e..ea4d68417e401 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
@@ -16,7 +16,9 @@
package com.android.internal.telephony.gsm;
-public class SmsCbHeader {
+import android.telephony.SmsCbConstants;
+
+public class SmsCbHeader implements SmsCbConstants {
/**
* Length of SMS-CB header
*/
@@ -107,4 +109,72 @@ public class SmsCbHeader {
nrOfPages = 1;
}
}
+
+ /**
+ * Return whether the specified message ID is an emergency (PWS) message type.
+ * This method is static and takes an argument so that it can be used by
+ * CellBroadcastReceiver, which stores message ID's in SQLite rather than PDU.
+ * @param id the message identifier to check
+ * @return true if the message is emergency type; false otherwise
+ */
+ public static boolean isEmergencyMessage(int id) {
+ return id >= MESSAGE_ID_PWS_FIRST_IDENTIFIER && id <= MESSAGE_ID_PWS_LAST_IDENTIFIER;
+ }
+
+ /**
+ * Return whether the specified message ID is an ETWS emergency message type.
+ * This method is static and takes an argument so that it can be used by
+ * CellBroadcastReceiver, which stores message ID's in SQLite rather than PDU.
+ * @param id the message identifier to check
+ * @return true if the message is ETWS emergency type; false otherwise
+ */
+ public static boolean isEtwsMessage(int id) {
+ return (id & MESSAGE_ID_ETWS_TYPE_MASK) == MESSAGE_ID_ETWS_TYPE;
+ }
+
+ /**
+ * Return whether the specified message ID is a CMAS emergency message type.
+ * This method is static and takes an argument so that it can be used by
+ * CellBroadcastReceiver, which stores message ID's in SQLite rather than PDU.
+ * @param id the message identifier to check
+ * @return true if the message is CMAS emergency type; false otherwise
+ */
+ public static boolean isCmasMessage(int id) {
+ return id >= MESSAGE_ID_CMAS_FIRST_IDENTIFIER && id <= MESSAGE_ID_CMAS_LAST_IDENTIFIER;
+ }
+
+ /**
+ * Return whether the specified message code indicates an ETWS popup alert.
+ * This method is static and takes an argument so that it can be used by
+ * CellBroadcastReceiver, which stores message codes in SQLite rather than PDU.
+ * This method assumes that the message ID has already been checked for ETWS type.
+ *
+ * @param messageCode the message code to check
+ * @return true if the message code indicates a popup alert should be displayed
+ */
+ public static boolean isEtwsPopupAlert(int messageCode) {
+ return (messageCode & MESSAGE_CODE_ETWS_ACTIVATE_POPUP) != 0;
+ }
+
+ /**
+ * Return whether the specified message code indicates an ETWS emergency user alert.
+ * This method is static and takes an argument so that it can be used by
+ * CellBroadcastReceiver, which stores message codes in SQLite rather than PDU.
+ * This method assumes that the message ID has already been checked for ETWS type.
+ *
+ * @param messageCode the message code to check
+ * @return true if the message code indicates an emergency user alert
+ */
+ public static boolean isEtwsEmergencyUserAlert(int messageCode) {
+ return (messageCode & MESSAGE_CODE_ETWS_EMERGENCY_USER_ALERT) != 0;
+ }
+
+ @Override
+ public String toString() {
+ return "SmsCbHeader{GS=" + geographicalScope + ", messageCode=0x" +
+ Integer.toHexString(messageCode) + ", updateNumber=" + updateNumber +
+ ", messageIdentifier=0x" + Integer.toHexString(messageIdentifier) +
+ ", DCS=0x" + Integer.toHexString(dataCodingScheme) +
+ ", page " + pageIndex + " of " + nrOfPages + '}';
+ }
}