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

Commit 95bc625e authored by Henrik Hall's avatar Henrik Hall Committed by Johan Redestig
Browse files

Enabling cell broadcast (SMS-CB) support in the platform.

Adding a simple API enabling applications to control SMS-CB reception.
Implementing parsing, assembly and dispatching of SMS-CB messages over GSM.

Change-Id: Iee841605a45a3af60c7602af175056afb03a38da
parent 14ac9546
Loading
Loading
Loading
Loading
+267 −0
Original line number Original line Diff line number Diff line
/*
 * Copyright (C) 2010 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.telephony;

import com.android.internal.telephony.GsmAlphabet;
import com.android.internal.telephony.gsm.SmsCbHeader;

import java.io.UnsupportedEncodingException;

/**
 * Describes an SMS-CB message.
 *
 * {@hide}
 */
public class SmsCbMessage {

    /**
     * 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;

    /**
     * Create an instance of this class from a received PDU
     *
     * @param pdu PDU bytes
     * @return An instance of this class, or null if invalid pdu
     */
    public static SmsCbMessage createFromPdu(byte[] pdu) {
        try {
            return new SmsCbMessage(pdu);
        } catch (IllegalArgumentException e) {
            return null;
        }
    }

    /**
     * Languages in the 0000xxxx DCS group as defined in 3GPP TS 23.038, section 5.
     */
    private static final String[] LANGUAGE_CODES_GROUP_0 = {
            "de", "en", "it", "fr", "es", "nl", "sv", "da", "pt", "fi", "no", "el", "tr", "hu",
            "pl", null
    };

    /**
     * Languages in the 0010xxxx DCS group as defined in 3GPP TS 23.038, section 5.
     */
    private static final String[] LANGUAGE_CODES_GROUP_2 = {
            "cs", "he", "ar", "ru", "is", null, null, null, null, null, null, null, null, null,
            null, null
    };

    private static final char CARRIAGE_RETURN = 0x0d;

    private SmsCbHeader mHeader;

    private String mLanguage;

    private String mBody;

    private SmsCbMessage(byte[] pdu) throws IllegalArgumentException {
        mHeader = new SmsCbHeader(pdu);
        parseBody(pdu);
    }

    /**
     * Return the geographical scope of this message, one of
     * {@link #GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE},
     * {@link #GEOGRAPHICAL_SCOPE_PLMN_WIDE},
     * {@link #GEOGRAPHICAL_SCOPE_LA_WIDE},
     * {@link #GEOGRAPHICAL_SCOPE_CELL_WIDE}
     *
     * @return Geographical scope
     */
    public int getGeographicalScope() {
        return mHeader.geographicalScope;
    }

    /**
     * Get the ISO-639-1 language code for this message, or null if unspecified
     *
     * @return Language code
     */
    public String getLanguageCode() {
        return mLanguage;
    }

    /**
     * Get the body of this message, or null if no body available
     *
     * @return Body, or null
     */
    public String getMessageBody() {
        return mBody;
    }

    /**
     * Get the message identifier of this message (0-65535)
     *
     * @return Message identifier
     */
    public int getMessageIdentifier() {
        return mHeader.messageIdentifier;
    }

    /**
     * Get the message code of this message (0-1023)
     *
     * @return Message code
     */
    public int getMessageCode() {
        return mHeader.messageCode;
    }

    /**
     * Get the update number of this message (0-15)
     *
     * @return Update number
     */
    public int getUpdateNumber() {
        return mHeader.updateNumber;
    }

    private void parseBody(byte[] pdu) {
        int encoding;
        boolean hasLanguageIndicator = false;

        // Extract encoding and language from DCS, as defined in 3gpp TS 23.038,
        // section 5.
        switch ((mHeader.dataCodingScheme & 0xf0) >> 4) {
            case 0x00:
                encoding = SmsMessage.ENCODING_7BIT;
                mLanguage = LANGUAGE_CODES_GROUP_0[mHeader.dataCodingScheme & 0x0f];
                break;

            case 0x01:
                hasLanguageIndicator = true;
                if ((mHeader.dataCodingScheme & 0x0f) == 0x01) {
                    encoding = SmsMessage.ENCODING_16BIT;
                } else {
                    encoding = SmsMessage.ENCODING_7BIT;
                }
                break;

            case 0x02:
                encoding = SmsMessage.ENCODING_7BIT;
                mLanguage = LANGUAGE_CODES_GROUP_2[mHeader.dataCodingScheme & 0x0f];
                break;

            case 0x03:
                encoding = SmsMessage.ENCODING_7BIT;
                break;

            case 0x04:
            case 0x05:
                switch ((mHeader.dataCodingScheme & 0x0c) >> 2) {
                    case 0x01:
                        encoding = SmsMessage.ENCODING_8BIT;
                        break;

                    case 0x02:
                        encoding = SmsMessage.ENCODING_16BIT;
                        break;

                    case 0x00:
                    default:
                        encoding = SmsMessage.ENCODING_7BIT;
                        break;
                }
                break;

            case 0x06:
            case 0x07:
                // Compression not supported
            case 0x09:
                // UDH structure not supported
            case 0x0e:
                // Defined by the WAP forum not supported
                encoding = SmsMessage.ENCODING_UNKNOWN;
                break;

            case 0x0f:
                if (((mHeader.dataCodingScheme & 0x04) >> 2) == 0x01) {
                    encoding = SmsMessage.ENCODING_8BIT;
                } else {
                    encoding = SmsMessage.ENCODING_7BIT;
                }
                break;

            default:
                // Reserved values are to be treated as 7-bit
                encoding = SmsMessage.ENCODING_7BIT;
                break;
        }

        switch (encoding) {
            case SmsMessage.ENCODING_7BIT:
                mBody = GsmAlphabet.gsm7BitPackedToString(pdu, SmsCbHeader.PDU_HEADER_LENGTH,
                        (pdu.length - SmsCbHeader.PDU_HEADER_LENGTH) * 8 / 7);

                if (hasLanguageIndicator && mBody != null && mBody.length() > 2) {
                    mLanguage = mBody.substring(0, 2);
                    mBody = mBody.substring(3);
                }
                break;

            case SmsMessage.ENCODING_16BIT:
                int offset = SmsCbHeader.PDU_HEADER_LENGTH;

                if (hasLanguageIndicator && pdu.length >= SmsCbHeader.PDU_HEADER_LENGTH + 2) {
                    mLanguage = GsmAlphabet.gsm7BitPackedToString(pdu,
                            SmsCbHeader.PDU_HEADER_LENGTH, 2);
                    offset += 2;
                }

                try {
                    mBody = new String(pdu, offset, (pdu.length & 0xfffe) - offset, "utf-16");
                } catch (UnsupportedEncodingException e) {
                    // Eeeek
                }
                break;

            default:
                break;
        }

        if (mBody != null) {
            // Remove trailing carriage return
            for (int i = mBody.length() - 1; i >= 0; i--) {
                if (mBody.charAt(i) != CARRIAGE_RETURN) {
                    mBody = mBody.substring(0, i + 1);
                    break;
                }
            }
        } else {
            mBody = "";
        }
    }
}
+61 −1
Original line number Original line Diff line number Diff line
@@ -343,6 +343,66 @@ public final class SmsManager {
        return createMessageListFromRawRecords(records);
        return createMessageListFromRawRecords(records);
    }
    }


    /**
     * Enable reception of cell broadcast (SMS-CB) messages with the given
     * 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".
     * Note: This call is blocking, callers may want to avoid calling it from
     * the main thread of an application.
     *
     * @param messageIdentifier Message identifier as specified in TS 23.041
     * @return true if successful, false otherwise
     * @see #disableCellBroadcast(int)
     *
     * {@hide}
     */
    public boolean enableCellBroadcast(int messageIdentifier) {
        boolean success = false;

        try {
            ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
            if (iccISms != null) {
                success = iccISms.enableCellBroadcast(messageIdentifier);
            }
        } catch (RemoteException ex) {
            // ignore it
        }

        return success;
    }

    /**
     * Disable reception of cell broadcast (SMS-CB) messages with the given
     * 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.
     * Note: This call is blocking, callers may want to avoid calling it from
     * the main thread of an application.
     *
     * @param messageIdentifier Message identifier as specified in TS 23.041
     * @return true if successful, false otherwise
     *
     * @see #enableCellBroadcast(int)
     *
     * {@hide}
     */
    public boolean disableCellBroadcast(int messageIdentifier) {
        boolean success = false;

        try {
            ISms iccISms = ISms.Stub.asInterface(ServiceManager.getService("isms"));
            if (iccISms != null) {
                success = iccISms.disableCellBroadcast(messageIdentifier);
            }
        } catch (RemoteException ex) {
            // ignore it
        }

        return success;
    }

    /**
    /**
     * Create a list of <code>SmsMessage</code>s from a list of RawSmsData
     * Create a list of <code>SmsMessage</code>s from a list of RawSmsData
     * records returned by <code>getAllMessagesFromIcc()</code>
     * records returned by <code>getAllMessagesFromIcc()</code>
+26 −0
Original line number Original line Diff line number Diff line
@@ -144,4 +144,30 @@ interface ISms {
            in List<String> parts, in List<PendingIntent> sentIntents,
            in List<String> parts, in List<PendingIntent> sentIntents,
            in List<PendingIntent> deliveryIntents);
            in List<PendingIntent> deliveryIntents);


    /**
     * Enable reception of cell broadcast (SMS-CB) messages with the given
     * 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.
     *
     * @param messageIdentifier Message identifier as specified in TS 23.041
     * @return true if successful, false otherwise
     *
     * @see #disableCellBroadcast(int)
     */
    boolean enableCellBroadcast(int messageIdentifier);

    /**
     * Disable reception of cell broadcast (SMS-CB) messages with the given
     * 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.
     *
     * @param messageIdentifier Message identifier as specified in TS 23.041
     * @return true if successful, false otherwise
     *
     * @see #enableCellBroadcast(int)
     */
    boolean disableCellBroadcast(int messageIdentifier);

}
}
+8 −0
Original line number Original line Diff line number Diff line
@@ -68,4 +68,12 @@ public class IccSmsInterfaceManagerProxy extends ISms.Stub {
                parts, sentIntents, deliveryIntents);
                parts, sentIntents, deliveryIntents);
    }
    }


    public boolean enableCellBroadcast(int messageIdentifier) throws android.os.RemoteException {
        return mIccSmsInterfaceManager.enableCellBroadcast(messageIdentifier);
    }

    public boolean disableCellBroadcast(int messageIdentifier) throws android.os.RemoteException {
        return mIccSmsInterfaceManager.disableCellBroadcast(messageIdentifier);
    }

}
}
+19 −0
Original line number Original line Diff line number Diff line
@@ -116,6 +116,9 @@ public abstract class SMSDispatcher extends Handler {
    /** Radio is ON */
    /** Radio is ON */
    static final protected int EVENT_RADIO_ON = 12;
    static final protected int EVENT_RADIO_ON = 12;


    /** New broadcast SMS */
    static final protected int EVENT_NEW_BROADCAST_SMS = 13;

    protected Phone mPhone;
    protected Phone mPhone;
    protected Context mContext;
    protected Context mContext;
    protected ContentResolver mResolver;
    protected ContentResolver mResolver;
@@ -399,6 +402,9 @@ public abstract class SMSDispatcher extends Handler {
                mCm.reportSmsMemoryStatus(mStorageAvailable,
                mCm.reportSmsMemoryStatus(mStorageAvailable,
                        obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
                        obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
            }
            }

        case EVENT_NEW_BROADCAST_SMS:
            handleBroadcastSms((AsyncResult)msg.obj);
            break;
            break;
        }
        }
    }
    }
@@ -995,4 +1001,17 @@ public abstract class SMSDispatcher extends Handler {
            }
            }


        };
        };

    protected abstract void handleBroadcastSms(AsyncResult ar);

    protected void dispatchBroadcastPdus(byte[][] pdus) {
        Intent intent = new Intent("android.provider.telephony.SMS_CB_RECEIVED");
        intent.putExtra("pdus", pdus);

        if (Config.LOGD)
            Log.d(TAG, "Dispatching " + pdus.length + " SMS CB pdus");

        dispatch(intent, "android.permission.RECEIVE_SMS");
    }

}
}
Loading