diff options
16 files changed, 1135 insertions, 57 deletions
diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index 2fb746c3f2c1..9a3d6212fc8c 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -1107,6 +1107,9 @@ public class SearchDialog extends Dialog implements OnItemClickListener, OnItemS       * @return true if a successful launch, false if could not (e.g. bad position).       */      protected boolean launchSuggestion(int position, int actionKey, String actionMsg) { +        if (mSuggestionsAdapter == null) { +            return false; +        }          Cursor c = mSuggestionsAdapter.getCursor();          if ((c != null) && c.moveToPosition(position)) { diff --git a/core/java/android/content/res/StringBlock.java b/core/java/android/content/res/StringBlock.java index 5e90b9110e71..23a6f97c37db 100644 --- a/core/java/android/content/res/StringBlock.java +++ b/core/java/android/content/res/StringBlock.java @@ -87,21 +87,48 @@ final class StringBlock {              if (style != null) {                  if (mStyleIDs == null) {                      mStyleIDs = new StyleIDs(); -                    mStyleIDs.boldId = nativeIndexOfString(mNative, "b"); -                    mStyleIDs.italicId = nativeIndexOfString(mNative, "i"); -                    mStyleIDs.underlineId = nativeIndexOfString(mNative, "u"); -                    mStyleIDs.ttId = nativeIndexOfString(mNative, "tt"); -                    mStyleIDs.bigId = nativeIndexOfString(mNative, "big"); -                    mStyleIDs.smallId = nativeIndexOfString(mNative, "small"); -                    mStyleIDs.supId = nativeIndexOfString(mNative, "sup"); -                    mStyleIDs.subId = nativeIndexOfString(mNative, "sub"); -                    mStyleIDs.strikeId = nativeIndexOfString(mNative, "strike"); -                    mStyleIDs.listItemId = nativeIndexOfString(mNative, "li"); -                    mStyleIDs.marqueeId = nativeIndexOfString(mNative, "marquee"); - -                    if (localLOGV) Log.v(TAG, "BoldId=" + mStyleIDs.boldId -                            + ", ItalicId=" + mStyleIDs.italicId -                            + ", UnderlineId=" + mStyleIDs.underlineId); +                } + +                // the style array is a flat array of <type, start, end> hence +                // the magic constant 3. +                for (int styleIndex = 0; styleIndex < style.length; styleIndex += 3) { +                    int styleId = style[styleIndex]; + +                    if (styleId == mStyleIDs.boldId || styleId == mStyleIDs.italicId +                            || styleId == mStyleIDs.underlineId || styleId == mStyleIDs.ttId +                            || styleId == mStyleIDs.bigId || styleId == mStyleIDs.smallId +                            || styleId == mStyleIDs.subId || styleId == mStyleIDs.supId +                            || styleId == mStyleIDs.strikeId || styleId == mStyleIDs.listItemId +                            || styleId == mStyleIDs.marqueeId) { +                        // id already found skip to next style +                        continue; +                    } + +                    String styleTag = nativeGetString(mNative, styleId); + +                    if (styleTag.equals("b")) { +                        mStyleIDs.boldId = styleId; +                    } else if (styleTag.equals("i")) { +                        mStyleIDs.italicId = styleId; +                    } else if (styleTag.equals("u")) { +                        mStyleIDs.underlineId = styleId; +                    } else if (styleTag.equals("tt")) { +                        mStyleIDs.ttId = styleId; +                    } else if (styleTag.equals("big")) { +                        mStyleIDs.bigId = styleId; +                    } else if (styleTag.equals("small")) { +                        mStyleIDs.smallId = styleId; +                    } else if (styleTag.equals("sup")) { +                        mStyleIDs.supId = styleId; +                    } else if (styleTag.equals("sub")) { +                        mStyleIDs.subId = styleId; +                    } else if (styleTag.equals("strike")) { +                        mStyleIDs.strikeId = styleId; +                    } else if (styleTag.equals("li")) { +                        mStyleIDs.listItemId = styleId; +                    } else if (styleTag.equals("marquee")) { +                        mStyleIDs.marqueeId = styleId; +                    }                  }                  res = applyStyles(str, style, mStyleIDs); @@ -119,17 +146,17 @@ final class StringBlock {      }      static final class StyleIDs { -        private int boldId; -        private int italicId; -        private int underlineId; -        private int ttId; -        private int bigId; -        private int smallId; -        private int subId; -        private int supId; -        private int strikeId; -        private int listItemId; -        private int marqueeId; +        private int boldId = -1; +        private int italicId = -1; +        private int underlineId = -1; +        private int ttId = -1; +        private int bigId = -1; +        private int smallId = -1; +        private int subId = -1; +        private int supId = -1; +        private int strikeId = -1; +        private int listItemId = -1; +        private int marqueeId = -1;      }      private CharSequence applyStyles(String str, int[] style, StyleIDs ids) { @@ -403,6 +430,5 @@ final class StringBlock {      private static final native int nativeGetSize(int obj);      private static final native String nativeGetString(int obj, int idx);      private static final native int[] nativeGetStyle(int obj, int idx); -    private static final native int nativeIndexOfString(int obj, String str);      private static final native void nativeDestroy(int obj);  } diff --git a/core/jni/android_util_StringBlock.cpp b/core/jni/android_util_StringBlock.cpp index 641fbce5c2a6..a021efda763a 100644 --- a/core/jni/android_util_StringBlock.cpp +++ b/core/jni/android_util_StringBlock.cpp @@ -147,25 +147,6 @@ static jintArray android_content_StringBlock_nativeGetStyle(JNIEnv* env, jobject      return array;  } -static jint android_content_StringBlock_nativeIndexOfString(JNIEnv* env, jobject clazz, -                                                         jint token, jstring str) -{ -    ResStringPool* osb = (ResStringPool*)token; -    if (osb == NULL || str == NULL) { -        doThrow(env, "java/lang/NullPointerException"); -        return 0; -    } - -    const char16_t* str16 = env->GetStringChars(str, NULL); -    jsize strLen = env->GetStringLength(str); - -    ssize_t idx = osb->indexOfString(str16, strLen); - -    env->ReleaseStringChars(str, str16); - -    return idx; -} -  static void android_content_StringBlock_nativeDestroy(JNIEnv* env, jobject clazz,                                                     jint token)  { @@ -193,8 +174,6 @@ static JNINativeMethod gStringBlockMethods[] = {              (void*) android_content_StringBlock_nativeGetString },      { "nativeGetStyle",    "(II)[I",              (void*) android_content_StringBlock_nativeGetStyle }, -    { "nativeIndexOfString","(ILjava/lang/String;)I", -            (void*) android_content_StringBlock_nativeIndexOfString },      { "nativeDestroy",      "(I)V",              (void*) android_content_StringBlock_nativeDestroy },  }; diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp index 03287dd1fe4c..2318844d3abf 100644 --- a/media/libstagefright/StagefrightMediaScanner.cpp +++ b/media/libstagefright/StagefrightMediaScanner.cpp @@ -172,14 +172,16 @@ status_t StagefrightMediaScanner::processFile(              || !strcasecmp(extension, ".rtttl")              || !strcasecmp(extension, ".rtx")              || !strcasecmp(extension, ".ota")) { -        return HandleMIDI(path, &client); -    } - -    if (!strcasecmp(extension, ".ogg")) { -        return HandleOGG(path, &client); -    } - -    if (mRetriever->setDataSource(path) == OK +        status_t status = HandleMIDI(path, &client); +        if (status != OK) { +            return status; +        } +    } else if (!strcasecmp(extension, ".ogg")) { +        status_t status = HandleOGG(path, &client); +        if (status != OK) { +            return status; +        } +    } else if (mRetriever->setDataSource(path) == OK              && mRetriever->setMode(                  METADATA_MODE_METADATA_RETRIEVAL_ONLY) == OK) {          const char *value; 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()); +    } +} diff --git a/test-runner/src/android/test/IsolatedContext.java b/test-runner/src/android/test/IsolatedContext.java index b483b825fdef..bc00f68e8322 100644 --- a/test-runner/src/android/test/IsolatedContext.java +++ b/test-runner/src/android/test/IsolatedContext.java @@ -87,6 +87,11 @@ public class IsolatedContext extends ContextWrapper {      }      @Override +    public void unregisterReceiver(BroadcastReceiver receiver) { +        // Ignore +    } + +    @Override      public void sendBroadcast(Intent intent) {          mBroadcastIntents.add(intent);      }  |