summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean-Baptiste Queru <jbq@google.com>2010-11-22 15:59:04 -0800
committerAndroid Code Review <code-review@android.com>2010-11-22 15:59:04 -0800
commite4ae7fc3eb6776881d1707483d3f0c93443040d4 (patch)
tree5fa337d866dacaa0a470e0f586260bc599292ad8
parentb2b0db2d5c89ae8824a4cb5d7944bae3a1dc8bf4 (diff)
parent95bc625e294e14a651321b5172f31406cdebbefb (diff)
downloadbase-e4ae7fc3eb6776881d1707483d3f0c93443040d4.tar.gz
Merge "Enabling cell broadcast (SMS-CB) support in the platform."
-rw-r--r--telephony/java/android/telephony/SmsCbMessage.java267
-rw-r--r--telephony/java/android/telephony/SmsManager.java62
-rw-r--r--telephony/java/com/android/internal/telephony/ISms.aidl26
-rw-r--r--telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java8
-rw-r--r--telephony/java/com/android/internal/telephony/SMSDispatcher.java19
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java5
-rw-r--r--telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java12
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java166
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java139
-rw-r--r--telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java59
-rw-r--r--telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java302
11 files changed, 1064 insertions, 1 deletions
diff --git a/telephony/java/android/telephony/SmsCbMessage.java b/telephony/java/android/telephony/SmsCbMessage.java
new file mode 100644
index 000000000000..35432754e304
--- /dev/null
+++ b/telephony/java/android/telephony/SmsCbMessage.java
@@ -0,0 +1,267 @@
+/*
+ * 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 = "";
+ }
+ }
+}
diff --git a/telephony/java/android/telephony/SmsManager.java b/telephony/java/android/telephony/SmsManager.java
index f5e9751b9571..0ecd8543d841 100644
--- a/telephony/java/android/telephony/SmsManager.java
+++ b/telephony/java/android/telephony/SmsManager.java
@@ -341,7 +341,67 @@ public final class SmsManager {
}
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
diff --git a/telephony/java/com/android/internal/telephony/ISms.aidl b/telephony/java/com/android/internal/telephony/ISms.aidl
index 65bad962e2fb..90de5e1c9a7d 100644
--- a/telephony/java/com/android/internal/telephony/ISms.aidl
+++ b/telephony/java/com/android/internal/telephony/ISms.aidl
@@ -144,4 +144,30 @@ interface ISms {
in List<String> parts, in List<PendingIntent> sentIntents,
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);
+
}
diff --git a/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java b/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java
index 1910a9c8d67d..5049249053ba 100644
--- a/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java
+++ b/telephony/java/com/android/internal/telephony/IccSmsInterfaceManagerProxy.java
@@ -68,4 +68,12 @@ public class IccSmsInterfaceManagerProxy extends ISms.Stub {
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);
+ }
+
}
diff --git a/telephony/java/com/android/internal/telephony/SMSDispatcher.java b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
index ca526a521dc3..55e8450c23e5 100644
--- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java
@@ -116,6 +116,9 @@ public abstract class SMSDispatcher extends Handler {
/** Radio is ON */
static final protected int EVENT_RADIO_ON = 12;
+ /** New broadcast SMS */
+ static final protected int EVENT_NEW_BROADCAST_SMS = 13;
+
protected Phone mPhone;
protected Context mContext;
protected ContentResolver mResolver;
@@ -399,6 +402,9 @@ public abstract class SMSDispatcher extends Handler {
mCm.reportSmsMemoryStatus(mStorageAvailable,
obtainMessage(EVENT_REPORT_MEMORY_STATUS_DONE));
}
+
+ case EVENT_NEW_BROADCAST_SMS:
+ handleBroadcastSms((AsyncResult)msg.obj);
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");
+ }
+
}
diff --git a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
index ed93aea18988..8b2ea9b469a9 100644
--- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java
@@ -488,6 +488,11 @@ final class CdmaSMSDispatcher extends SMSDispatcher {
mCm.setCdmaBroadcastConfig(configValuesArray, response);
}
+ protected void handleBroadcastSms(AsyncResult ar) {
+ // Not supported
+ Log.e(TAG, "Error! Not implemented for CDMA.");
+ }
+
private int resultToCause(int rc) {
switch (rc) {
case Activity.RESULT_OK:
diff --git a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
index cfcfd98b6840..422c1e2652b0 100644
--- a/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/cdma/RuimSmsInterfaceManager.java
@@ -191,6 +191,18 @@ public class RuimSmsInterfaceManager extends IccSmsInterfaceManager {
return mSms;
}
+ public boolean enableCellBroadcast(int messageIdentifier) {
+ // Not implemented
+ Log.e(LOG_TAG, "Error! Not implemented for CDMA.");
+ return false;
+ }
+
+ public boolean disableCellBroadcast(int messageIdentifier) {
+ // 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/GsmSMSDispatcher.java b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
index d7205169f7af..438c81106f8e 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java
@@ -22,21 +22,27 @@ import android.app.PendingIntent.CanceledException;
import android.content.Intent;
import android.os.AsyncResult;
import android.os.Message;
+import android.os.SystemProperties;
import android.provider.Telephony.Sms.Intents;
import android.telephony.ServiceState;
+import android.telephony.SmsCbMessage;
+import android.telephony.gsm.GsmCellLocation;
import android.util.Config;
import android.util.Log;
import com.android.internal.telephony.IccUtils;
import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails;
import com.android.internal.telephony.gsm.SmsMessage;
+import com.android.internal.telephony.BaseCommands;
import com.android.internal.telephony.CommandsInterface;
import com.android.internal.telephony.SMSDispatcher;
import com.android.internal.telephony.SmsHeader;
import com.android.internal.telephony.SmsMessageBase;
+import com.android.internal.telephony.TelephonyProperties;
import java.util.ArrayList;
import java.util.HashMap;
+import java.util.Iterator;
import static android.telephony.SmsMessage.MessageClass;
@@ -48,6 +54,8 @@ final class GsmSMSDispatcher extends SMSDispatcher {
GsmSMSDispatcher(GSMPhone phone) {
super(phone);
mGsmPhone = phone;
+
+ ((BaseCommands)mCm).setOnNewGsmBroadcastSms(this, EVENT_NEW_BROADCAST_SMS, null);
}
/**
@@ -384,4 +392,162 @@ final class GsmSMSDispatcher extends SMSDispatcher {
return CommandsInterface.GSM_SMS_FAIL_CAUSE_UNSPECIFIED_ERROR;
}
}
+
+ /**
+ * Holds all info about a message page needed to assemble a complete
+ * concatenated message
+ */
+ private static final class SmsCbConcatInfo {
+ private final SmsCbHeader mHeader;
+
+ private final String mPlmn;
+
+ private final int mLac;
+
+ private final int mCid;
+
+ public SmsCbConcatInfo(SmsCbHeader header, String plmn, int lac, int cid) {
+ mHeader = header;
+ mPlmn = plmn;
+ mLac = lac;
+ mCid = cid;
+ }
+
+ @Override
+ public int hashCode() {
+ return mHeader.messageIdentifier * 31 + mHeader.updateNumber;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj instanceof SmsCbConcatInfo) {
+ SmsCbConcatInfo other = (SmsCbConcatInfo)obj;
+
+ // Two pages match if all header attributes (except the page
+ // index) are identical, and both pages belong to the same
+ // location (which is also determined by the scope parameter)
+ if (mHeader.geographicalScope == other.mHeader.geographicalScope
+ && mHeader.messageCode == other.mHeader.messageCode
+ && mHeader.updateNumber == other.mHeader.updateNumber
+ && mHeader.messageIdentifier == other.mHeader.messageIdentifier
+ && mHeader.dataCodingScheme == other.mHeader.dataCodingScheme
+ && mHeader.nrOfPages == other.mHeader.nrOfPages) {
+ return matchesLocation(other.mPlmn, other.mLac, other.mCid);
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * Checks if this concatenation info matches the given location. The
+ * granularity of the match depends on the geographical scope.
+ *
+ * @param plmn PLMN
+ * @param lac Location area code
+ * @param cid Cell ID
+ * @return true if matching, false otherwise
+ */
+ public boolean matchesLocation(String plmn, int lac, int cid) {
+ switch (mHeader.geographicalScope) {
+ case SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE:
+ case SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE:
+ if (mCid != cid) {
+ return false;
+ }
+ // deliberate fall-through
+ case SmsCbMessage.GEOGRAPHICAL_SCOPE_LA_WIDE:
+ if (mLac != lac) {
+ return false;
+ }
+ // deliberate fall-through
+ case SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE:
+ return mPlmn != null && mPlmn.equals(plmn);
+ }
+
+ return false;
+ }
+ }
+
+ // This map holds incomplete concatenated messages waiting for assembly
+ private HashMap<SmsCbConcatInfo, byte[][]> mSmsCbPageMap =
+ new HashMap<SmsCbConcatInfo, byte[][]>();
+
+ protected void handleBroadcastSms(AsyncResult ar) {
+ try {
+ byte[][] pdus = null;
+ byte[] receivedPdu = (byte[])ar.result;
+
+ if (Config.LOGD) {
+ for (int i = 0; i < receivedPdu.length; i += 8) {
+ StringBuilder sb = new StringBuilder("SMS CB pdu data: ");
+ for (int j = i; j < i + 8 && j < receivedPdu.length; j++) {
+ int b = receivedPdu[j] & 0xff;
+ if (b < 0x10) {
+ sb.append("0");
+ }
+ sb.append(Integer.toHexString(b)).append(" ");
+ }
+ Log.d(TAG, sb.toString());
+ }
+ }
+
+ SmsCbHeader header = new SmsCbHeader(receivedPdu);
+ String plmn = SystemProperties.get(TelephonyProperties.PROPERTY_OPERATOR_NUMERIC);
+ GsmCellLocation cellLocation = (GsmCellLocation)mGsmPhone.getCellLocation();
+ int lac = cellLocation.getLac();
+ int cid = cellLocation.getCid();
+
+ if (header.nrOfPages > 1) {
+ // Multi-page message
+ SmsCbConcatInfo concatInfo = new SmsCbConcatInfo(header, plmn, lac, cid);
+
+ // Try to find other pages of the same message
+ pdus = mSmsCbPageMap.get(concatInfo);
+
+ if (pdus == null) {
+ // This it the first page of this message, make room for all
+ // pages and keep until complete
+ pdus = new byte[header.nrOfPages][];
+
+ mSmsCbPageMap.put(concatInfo, pdus);
+ }
+
+ // Page parameter is one-based
+ pdus[header.pageIndex - 1] = receivedPdu;
+
+ for (int i = 0; i < pdus.length; i++) {
+ if (pdus[i] == null) {
+ // Still missing pages, exit
+ return;
+ }
+ }
+
+ // Message complete, remove and dispatch
+ mSmsCbPageMap.remove(concatInfo);
+ } else {
+ // Single page message
+ pdus = new byte[1][];
+ pdus[0] = receivedPdu;
+ }
+
+ dispatchBroadcastPdus(pdus);
+
+ // Remove messages that are out of scope to prevent the map from
+ // growing indefinitely, containing incomplete messages that were
+ // never assembled
+ Iterator<SmsCbConcatInfo> iter = mSmsCbPageMap.keySet().iterator();
+
+ while (iter.hasNext()) {
+ SmsCbConcatInfo info = iter.next();
+
+ if (!info.matchesLocation(plmn, lac, cid)) {
+ iter.remove();
+ }
+ }
+ } catch (RuntimeException e) {
+ Log.e(TAG, "Error in decoding SMS CB pdu", e);
+ }
+ }
+
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
index 2028ca4a78a8..a5e837838c9d 100644
--- a/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
+++ b/telephony/java/com/android/internal/telephony/gsm/SimSmsInterfaceManager.java
@@ -17,7 +17,9 @@
package com.android.internal.telephony.gsm;
import android.content.Context;
+import android.content.pm.PackageManager;
import android.os.AsyncResult;
+import android.os.Binder;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
@@ -29,7 +31,10 @@ import com.android.internal.telephony.SmsRawData;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashMap;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
import static android.telephony.SmsManager.STATUS_ON_ICC_FREE;
@@ -44,9 +49,15 @@ public class SimSmsInterfaceManager extends IccSmsInterfaceManager {
private final Object mLock = new Object();
private boolean mSuccess;
private List<SmsRawData> mSms;
+ private HashMap<Integer, HashSet<String>> mCellBroadcastSubscriptions =
+ new HashMap<Integer, HashSet<String>>();
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;
+ private static final int EVENT_SET_BROADCAST_CONFIG_DONE = 4;
+ private static final int SMS_CB_CODE_SCHEME_MIN = 0;
+ private static final int SMS_CB_CODE_SCHEME_MAX = 255;
Handler mHandler = new Handler() {
@Override
@@ -74,6 +85,14 @@ public class SimSmsInterfaceManager extends IccSmsInterfaceManager {
mLock.notifyAll();
}
break;
+ case EVENT_SET_BROADCAST_ACTIVATION_DONE:
+ case EVENT_SET_BROADCAST_CONFIG_DONE:
+ ar = (AsyncResult) msg.obj;
+ synchronized (mLock) {
+ mSuccess = (ar.exception == null);
+ mLock.notifyAll();
+ }
+ break;
}
}
};
@@ -191,6 +210,126 @@ public class SimSmsInterfaceManager extends IccSmsInterfaceManager {
return mSms;
}
+ public boolean enableCellBroadcast(int messageIdentifier) {
+ if (DBG) log("enableCellBroadcast");
+
+ Context context = mPhone.getContext();
+
+ context.enforceCallingPermission(
+ "android.permission.RECEIVE_SMS",
+ "Enabling cell broadcast SMS");
+
+ 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;
+ }
+ }
+
+ clients.add(client);
+
+ if (DBG)
+ log("Added cell broadcast subscription for MID " + messageIdentifier
+ + " from client " + client);
+
+ return true;
+ }
+
+ public boolean disableCellBroadcast(int messageIdentifier) {
+ if (DBG) log("disableCellBroadcast");
+
+ Context context = mPhone.getContext();
+
+ context.enforceCallingPermission(
+ "android.permission.RECEIVE_SMS",
+ "Disabling cell broadcast SMS");
+
+ 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;
+ }
+
+ return false;
+ }
+
+ private boolean updateCellBroadcastConfig() {
+ Set<Integer> messageIdentifiers = mCellBroadcastSubscriptions.keySet();
+
+ if (messageIdentifiers.size() > 0) {
+ SmsBroadcastConfigInfo[] configs =
+ new SmsBroadcastConfigInfo[messageIdentifiers.size()];
+ int i = 0;
+
+ for (int messageIdentifier : messageIdentifiers) {
+ configs[i++] = new SmsBroadcastConfigInfo(messageIdentifier, messageIdentifier,
+ SMS_CB_CODE_SCHEME_MIN, SMS_CB_CODE_SCHEME_MAX, true);
+ }
+
+ return setCellBroadcastConfig(configs) && setCellBroadcastActivation(true);
+ } else {
+ return setCellBroadcastActivation(false);
+ }
+ }
+
+ private boolean setCellBroadcastConfig(SmsBroadcastConfigInfo[] configs) {
+ if (DBG)
+ log("Calling setGsmBroadcastConfig with " + configs.length + " configurations");
+
+ synchronized (mLock) {
+ Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_CONFIG_DONE);
+
+ mSuccess = false;
+ mPhone.mCM.setGsmBroadcastConfig(configs, response);
+
+ try {
+ mLock.wait();
+ } catch (InterruptedException e) {
+ log("interrupted while trying to set cell broadcast config");
+ }
+ }
+
+ return mSuccess;
+ }
+
+ private boolean setCellBroadcastActivation(boolean activate) {
+ if (DBG)
+ log("Calling setCellBroadcastActivation(" + activate + ")");
+
+ synchronized (mLock) {
+ Message response = mHandler.obtainMessage(EVENT_SET_BROADCAST_ACTIVATION_DONE);
+
+ mSuccess = false;
+ mPhone.mCM.setGsmBroadcastActivation(activate, response);
+
+ try {
+ mLock.wait();
+ } catch (InterruptedException e) {
+ log("interrupted while trying to set cell broadcast activation");
+ }
+ }
+
+ return mSuccess;
+ }
+
protected void log(String msg) {
Log.d(LOG_TAG, "[SimSmsInterfaceManager] " + msg);
}
diff --git a/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
new file mode 100644
index 000000000000..5f27cfc3d22f
--- /dev/null
+++ b/telephony/java/com/android/internal/telephony/gsm/SmsCbHeader.java
@@ -0,0 +1,59 @@
+/*
+ * 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 com.android.internal.telephony.gsm;
+
+public class SmsCbHeader {
+ public static final int PDU_HEADER_LENGTH = 6;
+
+ public final int geographicalScope;
+
+ public final int messageCode;
+
+ public final int updateNumber;
+
+ public final int messageIdentifier;
+
+ public final int dataCodingScheme;
+
+ public final int pageIndex;
+
+ public final int nrOfPages;
+
+ public SmsCbHeader(byte[] pdu) throws IllegalArgumentException {
+ if (pdu == null || pdu.length < PDU_HEADER_LENGTH) {
+ throw new IllegalArgumentException("Illegal PDU");
+ }
+
+ geographicalScope = (pdu[0] & 0xc0) >> 6;
+ messageCode = ((pdu[0] & 0x3f) << 4) | ((pdu[1] & 0xf0) >> 4);
+ updateNumber = pdu[1] & 0x0f;
+ messageIdentifier = (pdu[2] << 8) | pdu[3];
+ dataCodingScheme = pdu[4];
+
+ // Check for invalid page parameter
+ int pageIndex = (pdu[5] & 0xf0) >> 4;
+ int nrOfPages = pdu[5] & 0x0f;
+
+ if (pageIndex == 0 || nrOfPages == 0 || pageIndex > nrOfPages) {
+ pageIndex = 1;
+ nrOfPages = 1;
+ }
+
+ this.pageIndex = pageIndex;
+ this.nrOfPages = nrOfPages;
+ }
+}
diff --git a/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java
new file mode 100644
index 000000000000..7136ea072c06
--- /dev/null
+++ b/telephony/tests/telephonytests/src/com/android/internal/telephony/GsmSmsCbTest.java
@@ -0,0 +1,302 @@
+/*
+ * 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 com.android.internal.telephony;
+
+import android.telephony.SmsCbMessage;
+import android.test.AndroidTestCase;
+
+/**
+ * Test cases for basic SmsCbMessage operations
+ */
+public class GsmSmsCbTest extends AndroidTestCase {
+
+ private void doTestGeographicalScopeValue(byte[] pdu, byte b, int expectedGs) {
+ pdu[0] = b;
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected geographical scope decoded", expectedGs, msg
+ .getGeographicalScope());
+ }
+
+ public void testCreateNullPdu() {
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(null);
+
+ assertNull("createFromPdu(byte[] with null pdu should return null", msg);
+ }
+
+ public void testCreateTooShortPdu() {
+ byte[] pdu = new byte[4];
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertNull("createFromPdu(byte[] with too short pdu should return null", msg);
+ }
+
+ public void testGetGeographicalScope() {
+ byte[] pdu = {
+ (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x40, (byte)0x11, (byte)0x41,
+ (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6,
+ (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70,
+ (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5,
+ (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69,
+ (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+ (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75,
+ (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69,
+ (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00
+ };
+
+ doTestGeographicalScopeValue(pdu, (byte)0x00,
+ SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE_IMMEDIATE);
+ doTestGeographicalScopeValue(pdu, (byte)0x40, SmsCbMessage.GEOGRAPHICAL_SCOPE_PLMN_WIDE);
+ doTestGeographicalScopeValue(pdu, (byte)0x80, SmsCbMessage.GEOGRAPHICAL_SCOPE_LA_WIDE);
+ doTestGeographicalScopeValue(pdu, (byte)0xC0, SmsCbMessage.GEOGRAPHICAL_SCOPE_CELL_WIDE);
+ }
+
+ public void testGetMessageBody7Bit() {
+ byte[] pdu = {
+ (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x40, (byte)0x11, (byte)0x41,
+ (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6,
+ (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70,
+ (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5,
+ (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69,
+ (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+ (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75,
+ (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69,
+ (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00
+ };
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected 7-bit string decoded",
+ "A GSM default alphabet message with carriage return padding",
+ msg.getMessageBody());
+ }
+
+ public void testGetMessageBody7BitFull() {
+ byte[] pdu = {
+ (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x40, (byte)0x11, (byte)0x41,
+ (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6,
+ (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70,
+ (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5,
+ (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xC4, (byte)0xE5,
+ (byte)0xB4, (byte)0xFB, (byte)0x0C, (byte)0x2A, (byte)0xE3, (byte)0xC3, (byte)0x63,
+ (byte)0x3A, (byte)0x3B, (byte)0x0F, (byte)0xCA, (byte)0xCD, (byte)0x40, (byte)0x63,
+ (byte)0x74, (byte)0x58, (byte)0x1E, (byte)0x1E, (byte)0xD3, (byte)0xCB, (byte)0xF2,
+ (byte)0x39, (byte)0x88, (byte)0xFD, (byte)0x76, (byte)0x9F, (byte)0x59, (byte)0xA0,
+ (byte)0x76, (byte)0x39, (byte)0xEC, (byte)0x4E, (byte)0xBB, (byte)0xCF, (byte)0x20,
+ (byte)0x3A, (byte)0xBA, (byte)0x2C, (byte)0x2F, (byte)0x83, (byte)0xD2, (byte)0x73,
+ (byte)0x90, (byte)0xFB, (byte)0x0D, (byte)0x82, (byte)0x87, (byte)0xC9, (byte)0xE4,
+ (byte)0xB4, (byte)0xFB, (byte)0x1C, (byte)0x02
+ };
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals(
+ "Unexpected 7-bit string decoded",
+ "A GSM default alphabet message being exactly 93 characters long, " +
+ "meaning there is no padding!",
+ msg.getMessageBody());
+ }
+
+ public void testGetMessageBody7BitWithLanguage() {
+ byte[] pdu = {
+ (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x04, (byte)0x11, (byte)0x41,
+ (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6,
+ (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70,
+ (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5,
+ (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69,
+ (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+ (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75,
+ (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69,
+ (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00
+ };
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected 7-bit string decoded",
+ "A GSM default alphabet message with carriage return padding",
+ msg.getMessageBody());
+
+ assertEquals("Unexpected language indicator decoded", "es", msg.getLanguageCode());
+ }
+
+ public void testGetMessageBody7BitWithLanguageInBody() {
+ byte[] pdu = {
+ (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x10, (byte)0x11, (byte)0x73,
+ (byte)0x7B, (byte)0x23, (byte)0x08, (byte)0x3A, (byte)0x4E, (byte)0x9B, (byte)0x20,
+ (byte)0x72, (byte)0xD9, (byte)0x1C, (byte)0xAE, (byte)0xB3, (byte)0xE9, (byte)0xA0,
+ (byte)0x30, (byte)0x1B, (byte)0x8E, (byte)0x0E, (byte)0x8B, (byte)0xCB, (byte)0x74,
+ (byte)0x50, (byte)0xBB, (byte)0x3C, (byte)0x9F, (byte)0x87, (byte)0xCF, (byte)0x65,
+ (byte)0xD0, (byte)0x3D, (byte)0x4D, (byte)0x47, (byte)0x83, (byte)0xC6, (byte)0x61,
+ (byte)0xB9, (byte)0x3C, (byte)0x1D, (byte)0x3E, (byte)0x97, (byte)0x41, (byte)0xF2,
+ (byte)0x32, (byte)0xBD, (byte)0x2E, (byte)0x77, (byte)0x83, (byte)0xE0, (byte)0x61,
+ (byte)0x32, (byte)0x39, (byte)0xED, (byte)0x3E, (byte)0x37, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00
+ };
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected 7-bit string decoded",
+ "A GSM default alphabet message with carriage return padding",
+ msg.getMessageBody());
+
+ assertEquals("Unexpected language indicator decoded", "sv", msg.getLanguageCode());
+ }
+
+ public void testGetMessageBody8Bit() {
+ byte[] pdu = {
+ (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x44, (byte)0x11, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45, (byte)0x46, (byte)0x47, (byte)0x41,
+ (byte)0x42, (byte)0x43, (byte)0x44, (byte)0x45
+ };
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("8-bit message body should be empty", "", msg.getMessageBody());
+ }
+
+ public void testGetMessageBodyUcs2() {
+ byte[] pdu = {
+ (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x48, (byte)0x11, (byte)0x00,
+ (byte)0x41, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x55, (byte)0x00, (byte)0x43,
+ (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x32, (byte)0x00, (byte)0x20, (byte)0x00,
+ (byte)0x6D, (byte)0x00, (byte)0x65, (byte)0x00, (byte)0x73, (byte)0x00, (byte)0x73,
+ (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x67, (byte)0x00, (byte)0x65, (byte)0x00,
+ (byte)0x20, (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x6F, (byte)0x00, (byte)0x6E,
+ (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x69, (byte)0x00,
+ (byte)0x6E, (byte)0x00, (byte)0x69, (byte)0x00, (byte)0x6E, (byte)0x00, (byte)0x67,
+ (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x20, (byte)0x04,
+ (byte)0x34, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x68,
+ (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x72, (byte)0x00, (byte)0x61, (byte)0x00,
+ (byte)0x63, (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x65, (byte)0x00, (byte)0x72,
+ (byte)0x00, (byte)0x0D, (byte)0x00, (byte)0x0D
+ };
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected 7-bit string decoded",
+ "A UCS2 message containing a \u0434 character", msg.getMessageBody());
+ }
+
+ public void testGetMessageBodyUcs2WithLanguageInBody() {
+ byte[] pdu = {
+ (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x11, (byte)0x11, (byte)0x78,
+ (byte)0x3C, (byte)0x00, (byte)0x41, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x55,
+ (byte)0x00, (byte)0x43, (byte)0x00, (byte)0x53, (byte)0x00, (byte)0x32, (byte)0x00,
+ (byte)0x20, (byte)0x00, (byte)0x6D, (byte)0x00, (byte)0x65, (byte)0x00, (byte)0x73,
+ (byte)0x00, (byte)0x73, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x67, (byte)0x00,
+ (byte)0x65, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x6F,
+ (byte)0x00, (byte)0x6E, (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x61, (byte)0x00,
+ (byte)0x69, (byte)0x00, (byte)0x6E, (byte)0x00, (byte)0x69, (byte)0x00, (byte)0x6E,
+ (byte)0x00, (byte)0x67, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x61, (byte)0x00,
+ (byte)0x20, (byte)0x04, (byte)0x34, (byte)0x00, (byte)0x20, (byte)0x00, (byte)0x63,
+ (byte)0x00, (byte)0x68, (byte)0x00, (byte)0x61, (byte)0x00, (byte)0x72, (byte)0x00,
+ (byte)0x61, (byte)0x00, (byte)0x63, (byte)0x00, (byte)0x74, (byte)0x00, (byte)0x65,
+ (byte)0x00, (byte)0x72, (byte)0x00, (byte)0x0D
+ };
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected 7-bit string decoded",
+ "A UCS2 message containing a \u0434 character", msg.getMessageBody());
+
+ assertEquals("Unexpected language indicator decoded", "xx", msg.getLanguageCode());
+ }
+
+ public void testGetMessageIdentifier() {
+ byte[] pdu = {
+ (byte)0xC0, (byte)0x00, (byte)0x30, (byte)0x39, (byte)0x40, (byte)0x11, (byte)0x41,
+ (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6,
+ (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70,
+ (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5,
+ (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69,
+ (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+ (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75,
+ (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69,
+ (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00
+ };
+
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected message identifier decoded", 12345, msg.getMessageIdentifier());
+ }
+
+ public void testGetMessageCode() {
+ byte[] pdu = {
+ (byte)0x2A, (byte)0xA5, (byte)0x30, (byte)0x39, (byte)0x40, (byte)0x11, (byte)0x41,
+ (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6,
+ (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70,
+ (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5,
+ (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69,
+ (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+ (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75,
+ (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69,
+ (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00
+ };
+
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected message code decoded", 682, msg.getMessageCode());
+ }
+
+ public void testGetUpdateNumber() {
+ byte[] pdu = {
+ (byte)0x2A, (byte)0xA5, (byte)0x30, (byte)0x39, (byte)0x40, (byte)0x11, (byte)0x41,
+ (byte)0xD0, (byte)0x71, (byte)0xDA, (byte)0x04, (byte)0x91, (byte)0xCB, (byte)0xE6,
+ (byte)0x70, (byte)0x9D, (byte)0x4D, (byte)0x07, (byte)0x85, (byte)0xD9, (byte)0x70,
+ (byte)0x74, (byte)0x58, (byte)0x5C, (byte)0xA6, (byte)0x83, (byte)0xDA, (byte)0xE5,
+ (byte)0xF9, (byte)0x3C, (byte)0x7C, (byte)0x2E, (byte)0x83, (byte)0xEE, (byte)0x69,
+ (byte)0x3A, (byte)0x1A, (byte)0x34, (byte)0x0E, (byte)0xCB, (byte)0xE5, (byte)0xE9,
+ (byte)0xF0, (byte)0xB9, (byte)0x0C, (byte)0x92, (byte)0x97, (byte)0xE9, (byte)0x75,
+ (byte)0xB9, (byte)0x1B, (byte)0x04, (byte)0x0F, (byte)0x93, (byte)0xC9, (byte)0x69,
+ (byte)0xF7, (byte)0xB9, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x68, (byte)0x34, (byte)0x1A, (byte)0x8D,
+ (byte)0x46, (byte)0xA3, (byte)0xD1, (byte)0x00
+ };
+
+ SmsCbMessage msg = SmsCbMessage.createFromPdu(pdu);
+
+ assertEquals("Unexpected update number decoded", 5, msg.getUpdateNumber());
+ }
+}