Loading telephony/java/android/telephony/SmsCbConstants.java +15 −12 Original line number Diff line number Diff line Loading @@ -22,18 +22,6 @@ package android.telephony; * {@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; Loading Loading @@ -111,4 +99,19 @@ public interface SmsCbConstants { /** ETWS message code flag to activate the emergency user alert. */ public static final int MESSAGE_CODE_ETWS_EMERGENCY_USER_ALERT = 0x200; /** ETWS warning type value for earthquake. */ public static final int ETWS_WARNING_TYPE_EARTHQUAKE = 0x00; /** ETWS warning type value for tsunami. */ public static final int ETWS_WARNING_TYPE_TSUNAMI = 0x01; /** ETWS warning type value for earthquake and tsunami. */ public static final int ETWS_WARNING_TYPE_EARTHQUAKE_AND_TSUNAMI = 0x02; /** ETWS warning type value for test broadcast. */ public static final int ETWS_WARNING_TYPE_TEST = 0x03; /** ETWS warning type value for other notifications. */ public static final int ETWS_WARNING_TYPE_OTHER = 0x04; } telephony/java/android/telephony/SmsCbMessage.java +118 −1 Original line number Diff line number Diff line Loading @@ -16,9 +16,11 @@ package android.telephony; import android.text.format.Time; import android.util.Log; import com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.IccUtils; import com.android.internal.telephony.gsm.SmsCbHeader; import java.io.UnsupportedEncodingException; Loading Loading @@ -93,10 +95,27 @@ public class SmsCbMessage { private String mBody; /** Timestamp of ETWS primary notification with security. */ private long mPrimaryNotificationTimestamp; /** 43 byte digital signature of ETWS primary notification with security. */ private byte[] mPrimaryNotificationDigitalSignature; private SmsCbMessage(byte[] pdu) throws IllegalArgumentException { mHeader = new SmsCbHeader(pdu); if (mHeader.format == SmsCbHeader.FORMAT_ETWS_PRIMARY) { mBody = "ETWS"; // ETWS primary notification with security is 56 octets in length if (pdu.length >= SmsCbHeader.PDU_LENGTH_ETWS) { mPrimaryNotificationTimestamp = getTimestampMillis(pdu); mPrimaryNotificationDigitalSignature = new byte[43]; // digital signature starts after 6 byte header and 7 byte timestamp System.arraycopy(pdu, 13, mPrimaryNotificationDigitalSignature, 0, 43); } } else { parseBody(pdu); } } /** * Return the geographical scope of this message, one of Loading Loading @@ -156,6 +175,55 @@ public class SmsCbMessage { return mHeader.updateNumber; } /** * Get the format of this message. * @return {@link SmsCbHeader#FORMAT_GSM}, {@link SmsCbHeader#FORMAT_UMTS}, or * {@link SmsCbHeader#FORMAT_ETWS_PRIMARY} */ public int getMessageFormat() { return mHeader.format; } /** * For ETWS primary notifications, return the emergency user alert flag. * @return true to notify terminal to activate emergency user alert; false otherwise */ public boolean getEtwsEmergencyUserAlert() { return mHeader.etwsEmergencyUserAlert; } /** * For ETWS primary notifications, return the popup flag. * @return true to notify terminal to activate display popup; false otherwise */ public boolean getEtwsPopup() { return mHeader.etwsPopup; } /** * For ETWS primary notifications, return the warning type. * @return a value such as {@link SmsCbConstants#ETWS_WARNING_TYPE_EARTHQUAKE} */ public int getEtwsWarningType() { return mHeader.etwsWarningType; } /** * For ETWS primary notifications, return the Warning-Security-Information timestamp. * @return a timestamp in System.currentTimeMillis() format. */ public long getEtwsSecurityTimestamp() { return mPrimaryNotificationTimestamp; } /** * For ETWS primary notifications, return the 43 byte digital signature. * @return a byte array containing a copy of the digital signature */ public byte[] getEtwsSecuritySignature() { return mPrimaryNotificationDigitalSignature.clone(); } /** * Parse and unpack the body text according to the encoding in the DCS. * After completing successfully this method will have assigned the body Loading Loading @@ -334,6 +402,55 @@ public class SmsCbMessage { return body; } /** * Parses an ETWS primary notification timestamp and returns a currentTimeMillis()-style * timestamp. Copied from com.android.internal.telephony.gsm.SmsMessage. * @param pdu the ETWS primary notification PDU to decode * @return the UTC timestamp from the Warning-Security-Information parameter */ private long getTimestampMillis(byte[] pdu) { // Timestamp starts after CB header, in pdu[6] int year = IccUtils.gsmBcdByteToInt(pdu[6]); int month = IccUtils.gsmBcdByteToInt(pdu[7]); int day = IccUtils.gsmBcdByteToInt(pdu[8]); int hour = IccUtils.gsmBcdByteToInt(pdu[9]); int minute = IccUtils.gsmBcdByteToInt(pdu[10]); int second = IccUtils.gsmBcdByteToInt(pdu[11]); // For the timezone, the most significant bit of the // least significant nibble is the sign byte // (meaning the max range of this field is 79 quarter-hours, // which is more than enough) byte tzByte = pdu[12]; // Mask out sign bit. int timezoneOffset = IccUtils.gsmBcdByteToInt((byte) (tzByte & (~0x08))); timezoneOffset = ((tzByte & 0x08) == 0) ? timezoneOffset : -timezoneOffset; Time time = new Time(Time.TIMEZONE_UTC); // It's 2006. Should I really support years < 2000? time.year = year >= 90 ? year + 1900 : year + 2000; time.month = month - 1; time.monthDay = day; time.hour = hour; time.minute = minute; time.second = second; // Timezone offset is in quarter hours. return time.toMillis(true) - (timezoneOffset * 15 * 60 * 1000); } /** * Append text to the message body. This is used to concatenate multi-page GSM broadcasts. * @param body the text to append to this message */ public void appendToBody(String body) { mBody = mBody + body; } @Override public String toString() { return "SmsCbMessage{" + mHeader.toString() + ", language=" + mLanguage + Loading telephony/java/com/android/internal/telephony/IntRangeManager.java +65 −57 Original line number Diff line number Diff line Loading @@ -290,44 +290,45 @@ public abstract class IntRangeManager { 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) { int endIndex = startIndex; for (int testIndex = startIndex+1; testIndex < len; testIndex++) { IntRange testRange = mRanges.get(testIndex); if ((endId + 1) < testRange.startId) { break; } else { endIndex = testIndex; } } // no adjacent IntRanges to combine if (endIndex == startIndex) { // 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; } // get last range to coalesce into start range IntRange endRange = mRanges.get(endIndex); // Values from startId to range.endId have already been enabled. // if endId > endRange.endId, then enable range from range.endId+1 to endId, // else enable range from range.endId+1 to endRange.startId-1, because // values from endRange.startId to endId have already been added. int newRangeEndId = (endId <= endRange.endId) ? endRange.startId - 1 : endId; if (tryAddSingleRange(range.endId + 1, newRangeEndId, true)) { range.endId = endId; // insert new ClientRange in place range.insert(new ClientRange(startId, endId, client)); // coalesce range with following ranges up to endIndex // 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 // of the next range to join is always startIndex+1 (joinIndex). // i is the index if no elements had been 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++) { for (int i = joinIndex; i < endIndex; i++) { IntRange joinRange = mRanges.get(joinIndex); range.clients.addAll(joinRange.clients); mRanges.remove(joinRange); Loading @@ -339,8 +340,6 @@ public abstract class IntRangeManager { } } } } } // append new range after existing IntRanges if (tryAddSingleRange(startId, endId, true)) { Loading Loading @@ -435,6 +434,8 @@ public abstract class IntRangeManager { addRange(range.startId, nextStartId - 1, false); rangeCopy.startId = nextStartId; } // init largestEndId largestEndId = clients.get(1).endId; } // go through remaining ClientRanges, creating new IntRanges when Loading @@ -442,7 +443,6 @@ public abstract class IntRangeManager { // 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++) { Loading @@ -454,6 +454,7 @@ public abstract class IntRangeManager { } addRange(largestEndId + 1, nextCr.startId - 1, false); currentRange.endId = largestEndId; newRanges.add(currentRange); currentRange = new IntRange(nextCr); } else { currentRange.clients.add(nextCr); Loading @@ -463,18 +464,25 @@ public abstract class IntRangeManager { } } if (updateStarted) { if (!finishUpdate()) { // remove any channels between largestEndId and endId if (largestEndId < endId) { if (!updateStarted) { startUpdate(); updateStarted = true; } addRange(largestEndId + 1, endId, false); currentRange.endId = largestEndId; } newRanges.add(currentRange); if (updateStarted && !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 { // replace the original IntRange with newRanges mRanges.remove(i); mRanges.addAll(i, newRanges); return true; } } else { // not the ClientRange to remove; save highest end ID seen so far if (cr.endId > largestEndId) { Loading telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java +36 −2 Original line number Diff line number Diff line Loading @@ -34,6 +34,11 @@ public class SmsCbHeader implements SmsCbConstants { */ public static final int FORMAT_UMTS = 2; /** * GSM pdu format, as defined in 3gpp TS 23.041, section 9.4.1.3 */ public static final int FORMAT_ETWS_PRIMARY = 3; /** * Message type value as defined in 3gpp TS 25.324, section 11.1. */ Loading @@ -42,7 +47,12 @@ public class SmsCbHeader implements SmsCbConstants { /** * Length of GSM pdus */ private static final int PDU_LENGTH_GSM = 88; public static final int PDU_LENGTH_GSM = 88; /** * Maximum length of ETWS primary message GSM pdus */ public static final int PDU_LENGTH_ETWS = 56; public final int geographicalScope; Loading @@ -60,12 +70,30 @@ public class SmsCbHeader implements SmsCbConstants { public final int format; public final boolean etwsEmergencyUserAlert; public final boolean etwsPopup; public final int etwsWarningType; public SmsCbHeader(byte[] pdu) throws IllegalArgumentException { if (pdu == null || pdu.length < PDU_HEADER_LENGTH) { throw new IllegalArgumentException("Illegal PDU"); } if (pdu.length <= PDU_LENGTH_GSM) { if (pdu.length <= PDU_LENGTH_ETWS) { format = FORMAT_ETWS_PRIMARY; geographicalScope = -1; //not applicable messageCode = -1; updateNumber = -1; messageIdentifier = ((pdu[2] & 0xff) << 8) | (pdu[3] & 0xff); dataCodingScheme = -1; pageIndex = -1; nrOfPages = -1; etwsEmergencyUserAlert = (pdu[4] & 0x1) != 0; etwsPopup = (pdu[5] & 0x80) != 0; etwsWarningType = (pdu[4] & 0xfe) >> 1; } else if (pdu.length <= PDU_LENGTH_GSM) { // GSM pdus are no more than 88 bytes format = FORMAT_GSM; geographicalScope = (pdu[0] & 0xc0) >> 6; Loading @@ -85,6 +113,9 @@ public class SmsCbHeader implements SmsCbConstants { this.pageIndex = pageIndex; this.nrOfPages = nrOfPages; etwsEmergencyUserAlert = false; etwsPopup = false; etwsWarningType = -1; } else { // UMTS pdus are always at least 90 bytes since the payload includes // a number-of-pages octet and also one length octet per page Loading @@ -107,6 +138,9 @@ public class SmsCbHeader implements SmsCbConstants { // actual payload may contain several pages. pageIndex = 1; nrOfPages = 1; etwsEmergencyUserAlert = false; etwsPopup = false; etwsWarningType = -1; } } Loading telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java +46 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.internal.telephony; import android.telephony.SmsCbMessage; import android.test.AndroidTestCase; import android.util.Log; /** * Test cases for basic SmsCbMessage operations Loading Loading @@ -663,4 +664,49 @@ public class GsmSmsCbTest extends AndroidTestCase { assertEquals("Unexpected update number decoded", 5, msg.getUpdateNumber()); } /* ETWS Test message including header */ private static final byte[] etwsMessageNormal = IccUtils.hexStringToBytes("000011001101" + "0D0A5BAE57CE770C531790E85C716CBF3044573065B930675730" + "9707767A751F30025F37304463FA308C306B5099304830664E0B30553044FF086C178C615E81FF09" + "0000000000000000000000000000"); private static final byte[] etwsMessageCancel = IccUtils.hexStringToBytes("000011001101" + "0D0A5148307B3069002800310030003A0035" + "00320029306E7DCA602557309707901F5831309253D66D883057307E3059FF086C178C615E81FF09" + "00000000000000000000000000000000000000000000"); private static final byte[] etwsMessageTest = IccUtils.hexStringToBytes("000011031101" + "0D0A5BAE57CE770C531790E85C716CBF3044" + "573065B9306757309707300263FA308C306B5099304830664E0B30553044FF086C178C615E81FF09" + "00000000000000000000000000000000000000000000"); // FIXME: add example of ETWS primary notification PDU public void testEtwsMessageNormal() { SmsCbMessage msg = SmsCbMessage.createFromPdu(etwsMessageNormal); Log.d("GsmSmsCbTest", msg.toString()); assertEquals("GS mismatch", 0, msg.getGeographicalScope()); assertEquals("message code mismatch", 0, msg.getMessageCode()); assertEquals("update number mismatch", 0, msg.getUpdateNumber()); assertEquals("message ID mismatch", 0x1100, msg.getMessageIdentifier()); } public void testEtwsMessageCancel() { SmsCbMessage msg = SmsCbMessage.createFromPdu(etwsMessageCancel); Log.d("GsmSmsCbTest", msg.toString()); assertEquals("GS mismatch", 0, msg.getGeographicalScope()); assertEquals("message code mismatch", 0, msg.getMessageCode()); assertEquals("update number mismatch", 0, msg.getUpdateNumber()); assertEquals("message ID mismatch", 0x1100, msg.getMessageIdentifier()); } public void testEtwsMessageTest() { SmsCbMessage msg = SmsCbMessage.createFromPdu(etwsMessageTest); Log.d("GsmSmsCbTest", msg.toString()); assertEquals("GS mismatch", 0, msg.getGeographicalScope()); assertEquals("message code mismatch", 0, msg.getMessageCode()); assertEquals("update number mismatch", 0, msg.getUpdateNumber()); assertEquals("message ID mismatch", 0x1103, msg.getMessageIdentifier()); } } Loading
telephony/java/android/telephony/SmsCbConstants.java +15 −12 Original line number Diff line number Diff line Loading @@ -22,18 +22,6 @@ package android.telephony; * {@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; Loading Loading @@ -111,4 +99,19 @@ public interface SmsCbConstants { /** ETWS message code flag to activate the emergency user alert. */ public static final int MESSAGE_CODE_ETWS_EMERGENCY_USER_ALERT = 0x200; /** ETWS warning type value for earthquake. */ public static final int ETWS_WARNING_TYPE_EARTHQUAKE = 0x00; /** ETWS warning type value for tsunami. */ public static final int ETWS_WARNING_TYPE_TSUNAMI = 0x01; /** ETWS warning type value for earthquake and tsunami. */ public static final int ETWS_WARNING_TYPE_EARTHQUAKE_AND_TSUNAMI = 0x02; /** ETWS warning type value for test broadcast. */ public static final int ETWS_WARNING_TYPE_TEST = 0x03; /** ETWS warning type value for other notifications. */ public static final int ETWS_WARNING_TYPE_OTHER = 0x04; }
telephony/java/android/telephony/SmsCbMessage.java +118 −1 Original line number Diff line number Diff line Loading @@ -16,9 +16,11 @@ package android.telephony; import android.text.format.Time; import android.util.Log; import com.android.internal.telephony.GsmAlphabet; import com.android.internal.telephony.IccUtils; import com.android.internal.telephony.gsm.SmsCbHeader; import java.io.UnsupportedEncodingException; Loading Loading @@ -93,10 +95,27 @@ public class SmsCbMessage { private String mBody; /** Timestamp of ETWS primary notification with security. */ private long mPrimaryNotificationTimestamp; /** 43 byte digital signature of ETWS primary notification with security. */ private byte[] mPrimaryNotificationDigitalSignature; private SmsCbMessage(byte[] pdu) throws IllegalArgumentException { mHeader = new SmsCbHeader(pdu); if (mHeader.format == SmsCbHeader.FORMAT_ETWS_PRIMARY) { mBody = "ETWS"; // ETWS primary notification with security is 56 octets in length if (pdu.length >= SmsCbHeader.PDU_LENGTH_ETWS) { mPrimaryNotificationTimestamp = getTimestampMillis(pdu); mPrimaryNotificationDigitalSignature = new byte[43]; // digital signature starts after 6 byte header and 7 byte timestamp System.arraycopy(pdu, 13, mPrimaryNotificationDigitalSignature, 0, 43); } } else { parseBody(pdu); } } /** * Return the geographical scope of this message, one of Loading Loading @@ -156,6 +175,55 @@ public class SmsCbMessage { return mHeader.updateNumber; } /** * Get the format of this message. * @return {@link SmsCbHeader#FORMAT_GSM}, {@link SmsCbHeader#FORMAT_UMTS}, or * {@link SmsCbHeader#FORMAT_ETWS_PRIMARY} */ public int getMessageFormat() { return mHeader.format; } /** * For ETWS primary notifications, return the emergency user alert flag. * @return true to notify terminal to activate emergency user alert; false otherwise */ public boolean getEtwsEmergencyUserAlert() { return mHeader.etwsEmergencyUserAlert; } /** * For ETWS primary notifications, return the popup flag. * @return true to notify terminal to activate display popup; false otherwise */ public boolean getEtwsPopup() { return mHeader.etwsPopup; } /** * For ETWS primary notifications, return the warning type. * @return a value such as {@link SmsCbConstants#ETWS_WARNING_TYPE_EARTHQUAKE} */ public int getEtwsWarningType() { return mHeader.etwsWarningType; } /** * For ETWS primary notifications, return the Warning-Security-Information timestamp. * @return a timestamp in System.currentTimeMillis() format. */ public long getEtwsSecurityTimestamp() { return mPrimaryNotificationTimestamp; } /** * For ETWS primary notifications, return the 43 byte digital signature. * @return a byte array containing a copy of the digital signature */ public byte[] getEtwsSecuritySignature() { return mPrimaryNotificationDigitalSignature.clone(); } /** * Parse and unpack the body text according to the encoding in the DCS. * After completing successfully this method will have assigned the body Loading Loading @@ -334,6 +402,55 @@ public class SmsCbMessage { return body; } /** * Parses an ETWS primary notification timestamp and returns a currentTimeMillis()-style * timestamp. Copied from com.android.internal.telephony.gsm.SmsMessage. * @param pdu the ETWS primary notification PDU to decode * @return the UTC timestamp from the Warning-Security-Information parameter */ private long getTimestampMillis(byte[] pdu) { // Timestamp starts after CB header, in pdu[6] int year = IccUtils.gsmBcdByteToInt(pdu[6]); int month = IccUtils.gsmBcdByteToInt(pdu[7]); int day = IccUtils.gsmBcdByteToInt(pdu[8]); int hour = IccUtils.gsmBcdByteToInt(pdu[9]); int minute = IccUtils.gsmBcdByteToInt(pdu[10]); int second = IccUtils.gsmBcdByteToInt(pdu[11]); // For the timezone, the most significant bit of the // least significant nibble is the sign byte // (meaning the max range of this field is 79 quarter-hours, // which is more than enough) byte tzByte = pdu[12]; // Mask out sign bit. int timezoneOffset = IccUtils.gsmBcdByteToInt((byte) (tzByte & (~0x08))); timezoneOffset = ((tzByte & 0x08) == 0) ? timezoneOffset : -timezoneOffset; Time time = new Time(Time.TIMEZONE_UTC); // It's 2006. Should I really support years < 2000? time.year = year >= 90 ? year + 1900 : year + 2000; time.month = month - 1; time.monthDay = day; time.hour = hour; time.minute = minute; time.second = second; // Timezone offset is in quarter hours. return time.toMillis(true) - (timezoneOffset * 15 * 60 * 1000); } /** * Append text to the message body. This is used to concatenate multi-page GSM broadcasts. * @param body the text to append to this message */ public void appendToBody(String body) { mBody = mBody + body; } @Override public String toString() { return "SmsCbMessage{" + mHeader.toString() + ", language=" + mLanguage + Loading
telephony/java/com/android/internal/telephony/IntRangeManager.java +65 −57 Original line number Diff line number Diff line Loading @@ -290,44 +290,45 @@ public abstract class IntRangeManager { 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) { int endIndex = startIndex; for (int testIndex = startIndex+1; testIndex < len; testIndex++) { IntRange testRange = mRanges.get(testIndex); if ((endId + 1) < testRange.startId) { break; } else { endIndex = testIndex; } } // no adjacent IntRanges to combine if (endIndex == startIndex) { // 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; } // get last range to coalesce into start range IntRange endRange = mRanges.get(endIndex); // Values from startId to range.endId have already been enabled. // if endId > endRange.endId, then enable range from range.endId+1 to endId, // else enable range from range.endId+1 to endRange.startId-1, because // values from endRange.startId to endId have already been added. int newRangeEndId = (endId <= endRange.endId) ? endRange.startId - 1 : endId; if (tryAddSingleRange(range.endId + 1, newRangeEndId, true)) { range.endId = endId; // insert new ClientRange in place range.insert(new ClientRange(startId, endId, client)); // coalesce range with following ranges up to endIndex // 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 // of the next range to join is always startIndex+1 (joinIndex). // i is the index if no elements had been 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++) { for (int i = joinIndex; i < endIndex; i++) { IntRange joinRange = mRanges.get(joinIndex); range.clients.addAll(joinRange.clients); mRanges.remove(joinRange); Loading @@ -339,8 +340,6 @@ public abstract class IntRangeManager { } } } } } // append new range after existing IntRanges if (tryAddSingleRange(startId, endId, true)) { Loading Loading @@ -435,6 +434,8 @@ public abstract class IntRangeManager { addRange(range.startId, nextStartId - 1, false); rangeCopy.startId = nextStartId; } // init largestEndId largestEndId = clients.get(1).endId; } // go through remaining ClientRanges, creating new IntRanges when Loading @@ -442,7 +443,6 @@ public abstract class IntRangeManager { // 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++) { Loading @@ -454,6 +454,7 @@ public abstract class IntRangeManager { } addRange(largestEndId + 1, nextCr.startId - 1, false); currentRange.endId = largestEndId; newRanges.add(currentRange); currentRange = new IntRange(nextCr); } else { currentRange.clients.add(nextCr); Loading @@ -463,18 +464,25 @@ public abstract class IntRangeManager { } } if (updateStarted) { if (!finishUpdate()) { // remove any channels between largestEndId and endId if (largestEndId < endId) { if (!updateStarted) { startUpdate(); updateStarted = true; } addRange(largestEndId + 1, endId, false); currentRange.endId = largestEndId; } newRanges.add(currentRange); if (updateStarted && !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 { // replace the original IntRange with newRanges mRanges.remove(i); mRanges.addAll(i, newRanges); return true; } } else { // not the ClientRange to remove; save highest end ID seen so far if (cr.endId > largestEndId) { Loading
telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java +36 −2 Original line number Diff line number Diff line Loading @@ -34,6 +34,11 @@ public class SmsCbHeader implements SmsCbConstants { */ public static final int FORMAT_UMTS = 2; /** * GSM pdu format, as defined in 3gpp TS 23.041, section 9.4.1.3 */ public static final int FORMAT_ETWS_PRIMARY = 3; /** * Message type value as defined in 3gpp TS 25.324, section 11.1. */ Loading @@ -42,7 +47,12 @@ public class SmsCbHeader implements SmsCbConstants { /** * Length of GSM pdus */ private static final int PDU_LENGTH_GSM = 88; public static final int PDU_LENGTH_GSM = 88; /** * Maximum length of ETWS primary message GSM pdus */ public static final int PDU_LENGTH_ETWS = 56; public final int geographicalScope; Loading @@ -60,12 +70,30 @@ public class SmsCbHeader implements SmsCbConstants { public final int format; public final boolean etwsEmergencyUserAlert; public final boolean etwsPopup; public final int etwsWarningType; public SmsCbHeader(byte[] pdu) throws IllegalArgumentException { if (pdu == null || pdu.length < PDU_HEADER_LENGTH) { throw new IllegalArgumentException("Illegal PDU"); } if (pdu.length <= PDU_LENGTH_GSM) { if (pdu.length <= PDU_LENGTH_ETWS) { format = FORMAT_ETWS_PRIMARY; geographicalScope = -1; //not applicable messageCode = -1; updateNumber = -1; messageIdentifier = ((pdu[2] & 0xff) << 8) | (pdu[3] & 0xff); dataCodingScheme = -1; pageIndex = -1; nrOfPages = -1; etwsEmergencyUserAlert = (pdu[4] & 0x1) != 0; etwsPopup = (pdu[5] & 0x80) != 0; etwsWarningType = (pdu[4] & 0xfe) >> 1; } else if (pdu.length <= PDU_LENGTH_GSM) { // GSM pdus are no more than 88 bytes format = FORMAT_GSM; geographicalScope = (pdu[0] & 0xc0) >> 6; Loading @@ -85,6 +113,9 @@ public class SmsCbHeader implements SmsCbConstants { this.pageIndex = pageIndex; this.nrOfPages = nrOfPages; etwsEmergencyUserAlert = false; etwsPopup = false; etwsWarningType = -1; } else { // UMTS pdus are always at least 90 bytes since the payload includes // a number-of-pages octet and also one length octet per page Loading @@ -107,6 +138,9 @@ public class SmsCbHeader implements SmsCbConstants { // actual payload may contain several pages. pageIndex = 1; nrOfPages = 1; etwsEmergencyUserAlert = false; etwsPopup = false; etwsWarningType = -1; } } Loading
telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java +46 −0 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package com.android.internal.telephony; import android.telephony.SmsCbMessage; import android.test.AndroidTestCase; import android.util.Log; /** * Test cases for basic SmsCbMessage operations Loading Loading @@ -663,4 +664,49 @@ public class GsmSmsCbTest extends AndroidTestCase { assertEquals("Unexpected update number decoded", 5, msg.getUpdateNumber()); } /* ETWS Test message including header */ private static final byte[] etwsMessageNormal = IccUtils.hexStringToBytes("000011001101" + "0D0A5BAE57CE770C531790E85C716CBF3044573065B930675730" + "9707767A751F30025F37304463FA308C306B5099304830664E0B30553044FF086C178C615E81FF09" + "0000000000000000000000000000"); private static final byte[] etwsMessageCancel = IccUtils.hexStringToBytes("000011001101" + "0D0A5148307B3069002800310030003A0035" + "00320029306E7DCA602557309707901F5831309253D66D883057307E3059FF086C178C615E81FF09" + "00000000000000000000000000000000000000000000"); private static final byte[] etwsMessageTest = IccUtils.hexStringToBytes("000011031101" + "0D0A5BAE57CE770C531790E85C716CBF3044" + "573065B9306757309707300263FA308C306B5099304830664E0B30553044FF086C178C615E81FF09" + "00000000000000000000000000000000000000000000"); // FIXME: add example of ETWS primary notification PDU public void testEtwsMessageNormal() { SmsCbMessage msg = SmsCbMessage.createFromPdu(etwsMessageNormal); Log.d("GsmSmsCbTest", msg.toString()); assertEquals("GS mismatch", 0, msg.getGeographicalScope()); assertEquals("message code mismatch", 0, msg.getMessageCode()); assertEquals("update number mismatch", 0, msg.getUpdateNumber()); assertEquals("message ID mismatch", 0x1100, msg.getMessageIdentifier()); } public void testEtwsMessageCancel() { SmsCbMessage msg = SmsCbMessage.createFromPdu(etwsMessageCancel); Log.d("GsmSmsCbTest", msg.toString()); assertEquals("GS mismatch", 0, msg.getGeographicalScope()); assertEquals("message code mismatch", 0, msg.getMessageCode()); assertEquals("update number mismatch", 0, msg.getUpdateNumber()); assertEquals("message ID mismatch", 0x1100, msg.getMessageIdentifier()); } public void testEtwsMessageTest() { SmsCbMessage msg = SmsCbMessage.createFromPdu(etwsMessageTest); Log.d("GsmSmsCbTest", msg.toString()); assertEquals("GS mismatch", 0, msg.getGeographicalScope()); assertEquals("message code mismatch", 0, msg.getMessageCode()); assertEquals("update number mismatch", 0, msg.getUpdateNumber()); assertEquals("message ID mismatch", 0x1103, msg.getMessageIdentifier()); } }