Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ae62b7b0 authored by Jake Hamby's avatar Jake Hamby
Browse files

resolved conflicts for merge of 66040bbb to gingerbread-plus-aosp

Change-Id: Id13750bc01c977ab06accf8ceabc95fe5dee703a
parents 67f09cd0 66040bbb
Loading
Loading
Loading
Loading
+15 −12
Original line number Diff line number Diff line
@@ -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;

@@ -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;
}
+118 −1
Original line number Diff line number Diff line
@@ -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;
@@ -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
@@ -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
@@ -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 +
+65 −57
Original line number Diff line number Diff line
@@ -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);
@@ -339,8 +340,6 @@ public abstract class IntRangeManager {
                }
            }
        }
            }
        }

        // append new range after existing IntRanges
        if (tryAddSingleRange(startId, endId, true)) {
@@ -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
@@ -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++) {
@@ -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);
@@ -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) {
+36 −2
Original line number Diff line number Diff line
@@ -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.
     */
@@ -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;

@@ -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;
@@ -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
@@ -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;
        }
    }

+46 −0
Original line number Diff line number Diff line
@@ -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
@@ -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