diff options
Diffstat (limited to 'telephony')
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 0000000..3543275 --- /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 953696b..6a346af 100644 --- a/telephony/java/android/telephony/SmsManager.java +++ b/telephony/java/android/telephony/SmsManager.java @@ -338,7 +338,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 65bad96..90de5e1 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 1910a9c..5049249 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 3a7ce47..ec49a19 100644 --- a/telephony/java/com/android/internal/telephony/SMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/SMSDispatcher.java @@ -112,6 +112,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; @@ -390,6 +393,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; } } @@ -985,4 +991,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 37a33bc..53555d8 100644 --- a/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/cdma/CdmaSMSDispatcher.java @@ -494,6 +494,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 d84b6ab..29f3bc1 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 ed7066b..70f8f86 100644 --- a/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java +++ b/telephony/java/com/android/internal/telephony/gsm/GsmSMSDispatcher.java @@ -22,20 +22,26 @@ 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.BaseCommands; import com.android.internal.telephony.CommandsInterface; import com.android.internal.telephony.IccUtils; import com.android.internal.telephony.SMSDispatcher; import com.android.internal.telephony.SmsHeader; import com.android.internal.telephony.SmsMessageBase; import com.android.internal.telephony.SmsMessageBase.TextEncodingDetails; +import com.android.internal.telephony.TelephonyProperties; import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; import static android.telephony.SmsMessage.MessageClass; @@ -47,6 +53,8 @@ final class GsmSMSDispatcher extends SMSDispatcher { GsmSMSDispatcher(GSMPhone phone) { super(phone); mGsmPhone = phone; + + ((BaseCommands)mCm).setOnNewGsmBroadcastSms(this, EVENT_NEW_BROADCAST_SMS, null); } /** @@ -394,4 +402,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 aab359f..5abba6e 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; @@ -30,7 +32,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; @@ -45,9 +50,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 @@ -75,6 +86,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; } } }; @@ -193,6 +212,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; + } + @Override 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 0000000..5f27cfc --- /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 0000000..7136ea0 --- /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()); + } +} |