diff options
author | Jake Hamby <jhamby@google.com> | 2011-06-06 15:33:37 -0700 |
---|---|---|
committer | Jake Hamby <jhamby@google.com> | 2011-06-06 15:33:37 -0700 |
commit | 0cb17a52ee278e6fd1575a086dac155e11574287 (patch) | |
tree | bf5ab5f8a3309be1acca4ad67a460a13a5d5e40e | |
parent | 8e97372913c9236ffdee175c6da99dc00c22b3e1 (diff) | |
parent | ab79ee4adcbc6eb9fb9c509766753f65d3857739 (diff) | |
download | frameworks_base-0cb17a52ee278e6fd1575a086dac155e11574287.zip frameworks_base-0cb17a52ee278e6fd1575a086dac155e11574287.tar.gz frameworks_base-0cb17a52ee278e6fd1575a086dac155e11574287.tar.bz2 |
resolved conflicts for merge of ab79ee4a to gingerbread-plus-aosp
Change-Id: Ib885176060f65ef3286a24c7b9cae1a673666275
19 files changed, 1105 insertions, 156 deletions
diff --git a/core/java/android/provider/Telephony.java b/core/java/android/provider/Telephony.java index 62f66b6..2e480e8 100755 --- a/core/java/android/provider/Telephony.java +++ b/core/java/android/provider/Telephony.java @@ -542,7 +542,7 @@ public final class Telephony { * values:</p> * * <ul> - * <li><em>pdus</em> - An Object[] od byte[]s containing the PDUs + * <li><em>pdus</em> - An Object[] of byte[]s containing the PDUs * that make up the message.</li> * </ul> * @@ -586,6 +586,46 @@ public final class Telephony { "android.provider.Telephony.WAP_PUSH_RECEIVED"; /** + * Broadcast Action: A new Cell Broadcast message has been received + * by the device. The intent will have the following extra + * values:</p> + * + * <ul> + * <li><em>pdus</em> - An Object[] of byte[]s containing the PDUs + * that make up the message.</li> + * </ul> + * + * <p>The extra values can be extracted using + * {@link #getMessagesFromIntent(Intent)}.</p> + * + * <p>If a BroadcastReceiver encounters an error while processing + * this intent it should set the result code appropriately.</p> + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String SMS_CB_RECEIVED_ACTION = + "android.provider.Telephony.SMS_CB_RECEIVED"; + + /** + * Broadcast Action: A new Emergency Broadcast message has been received + * by the device. The intent will have the following extra + * values:</p> + * + * <ul> + * <li><em>pdus</em> - An Object[] of byte[]s containing the PDUs + * that make up the message.</li> + * </ul> + * + * <p>The extra values can be extracted using + * {@link #getMessagesFromIntent(Intent)}.</p> + * + * <p>If a BroadcastReceiver encounters an error while processing + * this intent it should set the result code appropriately.</p> + */ + @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) + public static final String SMS_EMERGENCY_CB_RECEIVED_ACTION = + "android.provider.Telephony.SMS_EMERGENCY_CB_RECEIVED"; + + /** * Broadcast Action: The SIM storage for SMS messages is full. If * space is not freed, messages targeted for the SIM (class 2) may * not be saved. @@ -618,7 +658,7 @@ public final class Telephony { * @param intent the intent to read from * @return an array of SmsMessages for the PDUs */ - public static final SmsMessage[] getMessagesFromIntent( + public static SmsMessage[] getMessagesFromIntent( Intent intent) { Object[] messages = (Object[]) intent.getSerializableExtra("pdus"); byte[][] pduObjs = new byte[messages.length][]; diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index c582eee..55cb6c8 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -150,6 +150,15 @@ android:label="@string/permlab_receiveMms" android:description="@string/permdesc_receiveMms" /> + <!-- Allows an application to receive emergency cell broadcast messages, + to record or display them to the user. Reserved for system apps. + @hide Pending API council approval --> + <permission android:name="android.permission.RECEIVE_EMERGENCY_BROADCAST" + android:permissionGroup="android.permission-group.MESSAGES" + android:protectionLevel="signatureOrSystem" + android:label="@string/permlab_receiveEmergencyBroadcast" + android:description="@string/permdesc_receiveEmergencyBroadcast" /> + <!-- Allows an application to read SMS messages. --> <permission android:name="android.permission.READ_SMS" android:permissionGroup="android.permission-group.MESSAGES" diff --git a/core/res/res/values/strings.xml b/core/res/res/values/strings.xml index ae3cd02..9a88ab3 100755 --- a/core/res/res/values/strings.xml +++ b/core/res/res/values/strings.xml @@ -427,6 +427,13 @@ your messages or delete them without showing them to you.</string> <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permlab_receiveEmergencyBroadcast">receive emergency broadcasts</string> + <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> + <string name="permdesc_receiveEmergencyBroadcast">Allows application to receive + and process emergency broadcast messages. This permission is only available + to system applications.</string> + + <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permlab_sendSms">send SMS messages</string> <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. --> <string name="permdesc_sendSms">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 0000000..1ac9ae3 --- /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 5608402..6da48d6 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 0ecd854..480186c 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -21,7 +21,6 @@ import android.os.RemoteException; import android.os.ServiceManager; import android.text.TextUtils; -import com.android.internal.telephony.EncodeException; import com.android.internal.telephony.ISms; import com.android.internal.telephony.IccConstants; import com.android.internal.telephony.SmsRawData; @@ -348,7 +347,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. * @@ -404,6 +403,68 @@ public final class SmsManager { } /** + * 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 <code>SmsMessage</code>s from a list of RawSmsData * records returned by <code>getAllMessagesFromIcc()</code> * diff --git a/telephony/java/com/android/internal/telephony/GsmAlphabet.java b/telephony/java/com/android/internal/telephony/GsmAlphabet.java index 57d73e3..8ea1f8d 100644 --- a/telephony/java/com/android/internal/telephony/GsmAlphabet.java +++ b/telephony/java/com/android/internal/telephony/GsmAlphabet.java @@ -83,6 +83,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) { @@ -96,12 +98,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 { @@ -130,6 +135,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) { @@ -152,6 +159,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) { @@ -171,6 +181,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) { @@ -241,6 +254,26 @@ public class GsmAlphabet { * 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. + * + * 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 * @param languageTable the 7 bit language table, or 0 for the default GSM alphabet * @param languageShiftTable the 7 bit single shift language table, or 0 for the default * GSM extension table @@ -446,6 +479,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) { @@ -494,6 +532,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) { @@ -512,11 +552,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; @@ -558,6 +600,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) { @@ -572,8 +616,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 90de5e1..735f986 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 5049249..54de508 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 0000000..889e2b1 --- /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]. + * <p>When a client is added, the IntRange may merge with one or more + * adjacent IntRanges to form a single combined IntRange. + * <p>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<ClientRange> 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<ClientRange>(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<ClientRange>(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<ClientRange>(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. + * <p>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. + * <p>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<IntRange> mRanges = new ArrayList<IntRange>(); + + 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<ClientRange> 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<IntRange> newRanges = new ArrayList<IntRange>(); + 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<IntRange> 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 index ad34550..69dd029 100755..100644 --- 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.provider.Telephony; import android.provider.Telephony.Sms.Intents; import android.provider.Settings; @@ -878,37 +876,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 - */ - protected 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 - */ - protected 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 - */ - protected abstract void setCellBroadcastConfig(int[] configValuesArray, Message response); - - /** * Send an acknowledge message. * @param success indicates that last message was successfully received. * @param result result code indicating any error @@ -1014,14 +981,21 @@ 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); + protected void dispatchBroadcastPdus(byte[][] pdus, boolean isEmergencyMessage) { + if (isEmergencyMessage) { + Intent intent = new Intent(Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION); + intent.putExtra("pdus", pdus); + if (Config.LOGD) + Log.d(TAG, "Dispatching " + pdus.length + " emergency SMS CB pdus"); - if (Config.LOGD) - Log.d(TAG, "Dispatching " + pdus.length + " SMS CB pdus"); + dispatch(intent, "android.permission.RECEIVE_EMERGENCY_BROADCAST"); + } else { + Intent intent = new Intent(Intents.SMS_CB_RECEIVED_ACTION); + intent.putExtra("pdus", pdus); + if (Config.LOGD) + 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 3890a98..1efae21 100755 --- a/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java +++ b/telephony/java/com/android/internal/telephony/cdma/CDMAPhone.java @@ -1163,7 +1163,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(); } /** @@ -1172,7 +1173,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(); } /** @@ -1181,7 +1183,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(); } private static final String IS683A_FEATURE_CODE = "*228"; diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java index d6fc134..45b58e5 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java @@ -265,7 +265,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); @@ -468,21 +468,6 @@ final class CdmaSMSDispatcher extends SMSDispatcher { } } - /** {@inheritDoc} */ - protected void activateCellBroadcastSms(int activate, Message response) { - mCm.setCdmaBroadcastActivation((activate == 0), response); - } - - /** {@inheritDoc} */ - protected void getCellBroadcastSmsConfig(Message response) { - mCm.getCdmaBroadcastConfig(response); - } - - /** {@inheritDoc} */ - protected 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 f4a6d11..7b42b06 100644 --- a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java +++ b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java @@ -204,6 +204,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 3959c67..c5be856 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java +++ b/telephony/java/com/android/internal/telephony/gsm/GSMPhone.java @@ -1471,16 +1471,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 index 14ad59a..de769d1 100755..100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java @@ -350,6 +350,7 @@ final class GsmSMSDispatcher extends SMSDispatcher { * * @param tracker holds the multipart Sms tracker ready to be sent */ + @Override protected void sendMultipartSms (SmsTracker tracker) { ArrayList<String> parts; ArrayList<PendingIntent> sentIntents; @@ -370,6 +371,7 @@ final class GsmSMSDispatcher extends SMSDispatcher { } /** {@inheritDoc} */ + @Override protected void acknowledgeLastIncomingSms(boolean success, int result, Message response){ // FIXME unit test leaves cm == null. this should change if (mCm != null) { @@ -377,27 +379,6 @@ final class GsmSMSDispatcher extends SMSDispatcher { } } - /** {@inheritDoc} */ - protected 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} */ - protected 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} */ - protected 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: @@ -489,9 +470,10 @@ final class GsmSMSDispatcher extends SMSDispatcher { } // This map holds incomplete concatenated messages waiting for assembly - private HashMap<SmsCbConcatInfo, byte[][]> mSmsCbPageMap = + private final HashMap<SmsCbConcatInfo, byte[][]> mSmsCbPageMap = new HashMap<SmsCbConcatInfo, byte[][]>(); + @Override protected void handleBroadcastSms(AsyncResult ar) { try { byte[][] pdus = null; @@ -503,9 +485,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()); } @@ -550,7 +532,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 e55596b..6e87f21 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<Integer, HashSet<String>> mCellBroadcastSubscriptions = new HashMap<Integer, HashSet<String>>(); + 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; @@ -212,7 +216,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(); @@ -222,30 +234,22 @@ public class SimSmsInterfaceManager extends IccSmsInterfaceManager { String client = context.getPackageManager().getNameForUid( Binder.getCallingUid()); - HashSet<String> clients = mCellBroadcastSubscriptions.get(messageIdentifier); - - if (clients == null) { - // This is a new message identifier - clients = new HashSet<String>(); - 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(); @@ -255,39 +259,56 @@ public class SimSmsInterfaceManager extends IccSmsInterfaceManager { String client = context.getPackageManager().getNameForUid( Binder.getCallingUid()); - HashSet<String> 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<Integer> messageIdentifiers = mCellBroadcastSubscriptions.keySet(); + class CellBroadcastRangeManager extends IntRangeManager { + private ArrayList<SmsBroadcastConfigInfo> mConfigList = + new ArrayList<SmsBroadcastConfigInfo>(); + + /** + * 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(); + } - if (messageIdentifiers.size() > 0) { - SmsBroadcastConfigInfo[] configs = - new SmsBroadcastConfigInfo[messageIdentifiers.size()]; - int i = 0; + /** + * 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)); + } - for (int messageIdentifier : messageIdentifiers) { - configs[i++] = new SmsBroadcastConfigInfo(messageIdentifier, messageIdentifier, - SMS_CB_CODE_SCHEME_MIN, SMS_CB_CODE_SCHEME_MAX, true); + /** + * 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); } } @@ -313,7 +334,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); @@ -331,6 +352,7 @@ public class SimSmsInterfaceManager extends IccSmsInterfaceManager { return mSuccess; } + @Override protected void log(String msg) { Log.d(LOG_TAG, "[SimSmsInterfaceManager] " + msg); } diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsBroadcastConfigInfo.java b/telephony/java/com/android/internal/telephony/gsm/SmsBroadcastConfigInfo.java index 45f50bc..66e7ce0 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 <fromServiceId, toServiceId> - * and <fromCodeScheme, toCodeScheme>are not accepted, while true means accepted. + * selected false means message types specified in {@code <fromServiceId, toServiceId>} + * and {@code <fromCodeScheme, toCodeScheme>} 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 0945a38..ea4d684 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 + '}'; + } } |