Enable support for area info cell broadcasts in Brazil.

Brazilian operators require support for displaying the most recent
area info cell broadcast (message identifier == 50) received.
Handle this with a broadcast that the Settings app can receive
and display in the status screen. Settings can also request
CellBroadcastReceiver to send the most recent area info received.

Also, when the SIM country code is Brazil, show the checkbox to
enable/disable channel 50 in cell broadcast settings, and enable
channel 50 in the radio at startup unless the user disables it.

Also update the test application to allow message identifier to be
set for GSM and UMTS cell broadcasts (excluding ETWS) for testing
the handling of different message identifiers, and change the
message identifier of the test cell broadcast PDUs from 50 to 1.
To override the message identifier, set the category field in the
test app to a non-zero value.

Bug: 7445881
Change-Id: Ibca8d33c32c0391d0d4eee444c86dd93878d7d1e
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 9b60968..92fda98 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -28,6 +28,7 @@
     <uses-permission android:name="android.permission.WAKE_LOCK" />
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
     <uses-permission android:name="android.permission.VIBRATE" />
+    <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
 
     <application android:name="CellBroadcastReceiverApp"
             android:label="@string/app_label"
@@ -105,6 +106,14 @@
             </intent-filter>
         </receiver>
 
+        <!-- Require sender permission for querying latest area info broadcast -->
+        <receiver android:name="PrivilegedCellBroadcastReceiver"
+            android:permission="android.permission.READ_PHONE_STATE">
+            <intent-filter>
+                 <action android:name="android.cellbroadcastreceiver.GET_LATEST_CB_AREA_INFO" />
+            </intent-filter>
+        </receiver>
+
         <receiver android:name="CellBroadcastReceiver">
             <intent-filter>
                  <action android:name="android.intent.action.BOOT_COMPLETED" />
diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertService.java b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertService.java
index 3cc821c..6e1b601 100644
--- a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertService.java
+++ b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertService.java
@@ -26,6 +26,7 @@
 import android.content.SharedPreferences;
 import android.os.Bundle;
 import android.os.IBinder;
+import android.os.UserHandle;
 import android.preference.PreferenceManager;
 import android.provider.Telephony;
 import android.telephony.CellBroadcastMessage;
@@ -52,6 +53,10 @@
     /** Use the same notification ID for non-emergency alerts. */
     static final int NOTIFICATION_ID = 1;
 
+    /** Sticky broadcast for latest area info broadcast received. */
+    static final String CB_AREA_INFO_RECEIVED_ACTION =
+            "android.cellbroadcastreceiver.CB_AREA_INFO_RECEIVED";
+
     /** Container for message ID and geographical scope, for duplicate message detection. */
     private static final class MessageIdAndScope {
         private final int mMessageId;
@@ -245,6 +250,16 @@
             }
         }
 
+        if (message.getServiceCategory() == 50) {
+            // save latest area info broadcast for Settings display and send as broadcast
+            CellBroadcastReceiverApp.setLatestAreaInfo(message);
+            Intent intent = new Intent(CB_AREA_INFO_RECEIVED_ACTION);
+            intent.putExtra("message", message);
+            sendBroadcastAsUser(intent, UserHandle.ALL,
+                    android.Manifest.permission.READ_PHONE_STATE);
+            return false;   // area info broadcasts are displayed in Settings status screen
+        }
+
         return true;    // other broadcast messages are always enabled
     }
 
@@ -339,7 +354,7 @@
                 messageList);
         intent.putExtra(CellBroadcastAlertFullScreen.FROM_NOTIFICATION_EXTRA, true);
 
-        PendingIntent pi = PendingIntent.getActivity(this, 0, intent,
+        PendingIntent pi = PendingIntent.getActivity(this, NOTIFICATION_ID, intent,
                 PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
 
         // use default sound/vibration/lights for non-emergency broadcasts
diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastConfigService.java b/src/com/android/cellbroadcastreceiver/CellBroadcastConfigService.java
index dd99dc5..3c6eea4 100644
--- a/src/com/android/cellbroadcastreceiver/CellBroadcastConfigService.java
+++ b/src/com/android/cellbroadcastreceiver/CellBroadcastConfigService.java
@@ -17,6 +17,7 @@
 package com.android.cellbroadcastreceiver;
 
 import android.app.IntentService;
+import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.content.res.Resources;
@@ -24,6 +25,7 @@
 import android.preference.PreferenceManager;
 import android.telephony.CellBroadcastMessage;
 import android.telephony.SmsManager;
+import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
 
@@ -133,7 +135,13 @@
                 boolean enableEmergencyAlerts = prefs.getBoolean(
                         CellBroadcastSettings.KEY_ENABLE_EMERGENCY_ALERTS, true);
 
-                boolean enableChannel50Alerts = res.getBoolean(R.bool.show_brazil_settings) &&
+                TelephonyManager tm = (TelephonyManager) getSystemService(
+                        Context.TELEPHONY_SERVICE);
+
+                boolean enableChannel50Support = res.getBoolean(R.bool.show_brazil_settings) ||
+                        "br".equals(tm.getSimCountryIso());
+
+                boolean enableChannel50Alerts = enableChannel50Support &&
                         prefs.getBoolean(CellBroadcastSettings.KEY_ENABLE_CHANNEL_50_ALERTS, true);
 
                 SmsManager manager = SmsManager.getDefault();
diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastReceiver.java b/src/com/android/cellbroadcastreceiver/CellBroadcastReceiver.java
index 63d0815..3eef633 100644
--- a/src/com/android/cellbroadcastreceiver/CellBroadcastReceiver.java
+++ b/src/com/android/cellbroadcastreceiver/CellBroadcastReceiver.java
@@ -20,10 +20,13 @@
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
 import android.os.RemoteException;
 import android.os.ServiceManager;
+import android.os.UserHandle;
 import android.preference.PreferenceManager;
 import android.provider.Telephony;
+import android.telephony.CellBroadcastMessage;
 import android.telephony.PhoneStateListener;
 import android.telephony.ServiceState;
 import android.telephony.TelephonyManager;
@@ -37,6 +40,9 @@
     private static final String TAG = "CellBroadcastReceiver";
     static final boolean DBG = true;    // STOPSHIP: change to false before ship
 
+    private static final String GET_LATEST_CB_AREA_INFO_ACTION =
+            "android.cellbroadcastreceiver.GET_LATEST_CB_AREA_INFO";
+
     @Override
     public void onReceive(Context context, Intent intent) {
         onReceiveWithPrivilege(context, intent, false);
@@ -83,6 +89,19 @@
             } else {
                 Log.e(TAG, "ignoring unprivileged action received " + action);
             }
+        } else if (GET_LATEST_CB_AREA_INFO_ACTION.equals(action)) {
+            if (privileged) {
+                CellBroadcastMessage message = CellBroadcastReceiverApp.getLatestAreaInfo();
+                if (message != null) {
+                    Intent areaInfoIntent = new Intent(
+                            CellBroadcastAlertService.CB_AREA_INFO_RECEIVED_ACTION);
+                    areaInfoIntent.putExtra("message", message);
+                    context.sendBroadcastAsUser(areaInfoIntent, UserHandle.ALL,
+                            android.Manifest.permission.READ_PHONE_STATE);
+                }
+            } else {
+                Log.e(TAG, "caller missing READ_PHONE_STATE permission, returning");
+            }
         } else {
             Log.w(TAG, "onReceive() unexpected action " + action);
         }
diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastReceiverApp.java b/src/com/android/cellbroadcastreceiver/CellBroadcastReceiverApp.java
index 65e8c72..b9e6039 100644
--- a/src/com/android/cellbroadcastreceiver/CellBroadcastReceiverApp.java
+++ b/src/com/android/cellbroadcastreceiver/CellBroadcastReceiverApp.java
@@ -42,6 +42,9 @@
     private static final ArrayList<CellBroadcastMessage> sNewMessageList =
             new ArrayList<CellBroadcastMessage>(4);
 
+    /** Latest area info cell broadcast received. */
+    private static CellBroadcastMessage sLatestAreaInfo;
+
     /** Adds a new unread non-emergency message and returns the current list. */
     static ArrayList<CellBroadcastMessage> addNewMessageToList(CellBroadcastMessage message) {
         sNewMessageList.add(message);
@@ -52,4 +55,14 @@
     static void clearNewMessageList() {
         sNewMessageList.clear();
     }
+
+    /** Saves the latest area info broadcast received. */
+    static void setLatestAreaInfo(CellBroadcastMessage areaInfo) {
+        sLatestAreaInfo = areaInfo;
+    }
+
+    /** Returns the latest area info broadcast received. */
+    static CellBroadcastMessage getLatestAreaInfo() {
+        return sLatestAreaInfo;
+    }
 }
diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastSettings.java b/src/com/android/cellbroadcastreceiver/CellBroadcastSettings.java
index a7c7482..027ef96 100644
--- a/src/com/android/cellbroadcastreceiver/CellBroadcastSettings.java
+++ b/src/com/android/cellbroadcastreceiver/CellBroadcastSettings.java
@@ -16,6 +16,7 @@
 
 package com.android.cellbroadcastreceiver;
 
+import android.content.Context;
 import android.content.res.Resources;
 import android.os.Bundle;
 import android.preference.ListPreference;
@@ -25,6 +26,8 @@
 import android.preference.PreferenceFragment;
 import android.preference.PreferenceScreen;
 import android.provider.Settings;
+import android.telephony.TelephonyManager;
+import android.util.Log;
 
 /**
  * Settings activity for the cell broadcast receiver.
@@ -163,7 +166,14 @@
                         findPreference(KEY_ENABLE_CMAS_SEVERE_THREAT_ALERTS));
                 alertCategory.removePreference(findPreference(KEY_ENABLE_CMAS_AMBER_ALERTS));
             }
-            if (!res.getBoolean(R.bool.show_brazil_settings)) {
+
+            TelephonyManager tm = (TelephonyManager) getActivity().getSystemService(
+                    Context.TELEPHONY_SERVICE);
+
+            boolean enableChannel50Support = res.getBoolean(R.bool.show_brazil_settings) ||
+                    "br".equals(tm.getSimCountryIso());
+
+            if (!enableChannel50Support) {
                 preferenceScreen.removePreference(findPreference(KEY_CATEGORY_BRAZIL_SETTINGS));
             }
             if (!enableDevSettings) {
diff --git a/tests/res/layout/test_buttons.xml b/tests/res/layout/test_buttons.xml
index 96103cb..4486433 100644
--- a/tests/res/layout/test_buttons.xml
+++ b/tests/res/layout/test_buttons.xml
@@ -42,6 +42,23 @@
 
     </LinearLayout>
 
+    <LinearLayout
+      android:layout_width="match_parent"
+      android:layout_height="match_parent"
+      android:orientation="horizontal">
+
+      <TextView android:id="@+id/category_id_label"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:text="@string/category_id_label" />
+
+      <EditText android:id="@+id/category_id"
+          android:layout_width="80dp"
+          android:layout_height="wrap_content"
+          android:inputType="number" />
+
+    </LinearLayout>
+
     <CheckBox android:id="@+id/button_delay_broadcast"
         android:text="@string/button_delay_broadcast"
         android:layout_marginLeft="20dp"
diff --git a/tests/res/values/strings.xml b/tests/res/values/strings.xml
index b1b1031..08560ac 100644
--- a/tests/res/values/strings.xml
+++ b/tests/res/values/strings.xml
@@ -40,4 +40,5 @@
     <string name="button_gsm_ucs2_with_language_umts_type">Send UMTS UCS-2 With Language</string>
     <string name="button_delay_broadcast">Delay 5 seconds before sending</string>
     <string name="message_id_label">Message ID:</string>
+    <string name="category_id_label">Category:</string>
 </resources>
diff --git a/tests/src/com/android/cellbroadcastreceiver/tests/SendTestBroadcastActivity.java b/tests/src/com/android/cellbroadcastreceiver/tests/SendTestBroadcastActivity.java
index f47ffd2..ca5a2a0 100644
--- a/tests/src/com/android/cellbroadcastreceiver/tests/SendTestBroadcastActivity.java
+++ b/tests/src/com/android/cellbroadcastreceiver/tests/SendTestBroadcastActivity.java
@@ -72,6 +72,15 @@
     }
 
     /**
+     * Return the value of the category field.
+     * @return the current value of the category text field
+     */
+    private int getCategory() {
+        EditText categoryField = (EditText) findViewById(R.id.category_id);
+        return Integer.parseInt(categoryField.getText().toString());
+    }
+
+    /**
      * Initialization of the Activity after it is first created.  Must at least
      * call {@link android.app.Activity#setContentView(int)} to
      * describe what is to be displayed in the screen.
@@ -85,7 +94,11 @@
         /* Set message ID to a random value from 1-65535. */
         EditText messageIdField = (EditText) findViewById(R.id.message_id);
         messageIdField.setText(String.valueOf(new Random().nextInt(65535) + 1));
-                
+
+        /* When category ID is non-zero, use it for the GSM/UMTS message identifier. */
+        EditText categoryIdField = (EditText) findViewById(R.id.category_id);
+        categoryIdField.setText("0");
+
         /* Send an ETWS normal broadcast message to app. */
         Button etwsNormalTypeButton = (Button) findViewById(R.id.button_etws_normal_type);
         etwsNormalTypeButton.setOnClickListener(new OnClickListener() {
@@ -94,7 +107,8 @@
                     Message msg = mDelayHandler.obtainMessage(0, this);
                     mDelayHandler.sendMessageDelayed(msg, DELAY_BEFORE_SENDING_MSEC);
                 } else {
-                    SendTestMessages.testSendEtwsMessageNormal(SendTestBroadcastActivity.this);
+                    SendTestMessages.testSendEtwsMessageNormal(SendTestBroadcastActivity.this,
+                            getMessageId());
                 }
             }
         });
@@ -107,7 +121,8 @@
                     Message msg = mDelayHandler.obtainMessage(0, this);
                     mDelayHandler.sendMessageDelayed(msg, DELAY_BEFORE_SENDING_MSEC);
                 } else {
-                    SendTestMessages.testSendEtwsMessageCancel(SendTestBroadcastActivity.this);
+                    SendTestMessages.testSendEtwsMessageCancel(SendTestBroadcastActivity.this,
+                            getMessageId());
                 }
             }
         });
@@ -120,7 +135,8 @@
                     Message msg = mDelayHandler.obtainMessage(0, this);
                     mDelayHandler.sendMessageDelayed(msg, DELAY_BEFORE_SENDING_MSEC);
                 } else {
-                    SendTestMessages.testSendEtwsMessageTest(SendTestBroadcastActivity.this);
+                    SendTestMessages.testSendEtwsMessageTest(SendTestBroadcastActivity.this,
+                            getMessageId());
                 }
             }
         });
@@ -203,7 +219,8 @@
                     Message msg = mDelayHandler.obtainMessage(0, this);
                     mDelayHandler.sendMessageDelayed(msg, DELAY_BEFORE_SENDING_MSEC);
                 } else {
-                    SendTestMessages.testSendMessage7bit(SendTestBroadcastActivity.this);
+                    SendTestMessages.testSendMessage7bit(SendTestBroadcastActivity.this,
+                            getMessageId(), getCategory());
                 }
             }
         });
@@ -216,7 +233,8 @@
                     Message msg = mDelayHandler.obtainMessage(0, this);
                     mDelayHandler.sendMessageDelayed(msg, DELAY_BEFORE_SENDING_MSEC);
                 } else {
-                    SendTestMessages.testSendMessage7bitUmts(SendTestBroadcastActivity.this);
+                    SendTestMessages.testSendMessage7bitUmts(SendTestBroadcastActivity.this,
+                            getMessageId(), getCategory());
                 }
             }
         });
@@ -229,7 +247,8 @@
                     Message msg = mDelayHandler.obtainMessage(0, this);
                     mDelayHandler.sendMessageDelayed(msg, DELAY_BEFORE_SENDING_MSEC);
                 } else {
-                    SendTestMessages.testSendMessage7bitNoPadding(SendTestBroadcastActivity.this);
+                    SendTestMessages.testSendMessage7bitNoPadding(SendTestBroadcastActivity.this,
+                            getMessageId(), getCategory());
                 }
             }
         });
@@ -243,7 +262,8 @@
                     Message msg = mDelayHandler.obtainMessage(0, this);
                     mDelayHandler.sendMessageDelayed(msg, DELAY_BEFORE_SENDING_MSEC);
                 } else {
-                    SendTestMessages.testSendMessage7bitNoPaddingUmts(SendTestBroadcastActivity.this);
+                    SendTestMessages.testSendMessage7bitNoPaddingUmts(
+                            SendTestBroadcastActivity.this, getMessageId(), getCategory());
                 }
             }
         });
@@ -257,7 +277,8 @@
                     Message msg = mDelayHandler.obtainMessage(0, this);
                     mDelayHandler.sendMessageDelayed(msg, DELAY_BEFORE_SENDING_MSEC);
                 } else {
-                    SendTestMessages.testSendMessage7bitMultipageGsm(SendTestBroadcastActivity.this);
+                    SendTestMessages.testSendMessage7bitMultipageGsm(SendTestBroadcastActivity.this,
+                            getMessageId(), getCategory());
                 }
             }
         });
@@ -271,7 +292,8 @@
                     Message msg = mDelayHandler.obtainMessage(0, this);
                     mDelayHandler.sendMessageDelayed(msg, DELAY_BEFORE_SENDING_MSEC);
                 } else {
-                    SendTestMessages.testSendMessage7bitMultipageUmts(SendTestBroadcastActivity.this);
+                    SendTestMessages.testSendMessage7bitMultipageUmts(
+                            SendTestBroadcastActivity.this, getMessageId(), getCategory());
                 }
             }
         });
@@ -285,7 +307,8 @@
                     Message msg = mDelayHandler.obtainMessage(0, this);
                     mDelayHandler.sendMessageDelayed(msg, DELAY_BEFORE_SENDING_MSEC);
                 } else {
-                    SendTestMessages.testSendMessage7bitWithLanguage(SendTestBroadcastActivity.this);
+                    SendTestMessages.testSendMessage7bitWithLanguage(SendTestBroadcastActivity.this,
+                            getMessageId(), getCategory());
                 }
             }
         });
@@ -300,7 +323,7 @@
                     mDelayHandler.sendMessageDelayed(msg, DELAY_BEFORE_SENDING_MSEC);
                 } else {
                     SendTestMessages.testSendMessage7bitWithLanguageInBody(
-                            SendTestBroadcastActivity.this);
+                            SendTestBroadcastActivity.this, getMessageId(), getCategory());
                 }
             }
         });
@@ -314,7 +337,8 @@
                     Message msg = mDelayHandler.obtainMessage(0, this);
                     mDelayHandler.sendMessageDelayed(msg, DELAY_BEFORE_SENDING_MSEC);
                 } else {
-                    SendTestMessages.testSendMessage7bitWithLanguageInBodyUmts(SendTestBroadcastActivity.this);
+                    SendTestMessages.testSendMessage7bitWithLanguageInBodyUmts(
+                            SendTestBroadcastActivity.this, getMessageId(), getCategory());
                 }
             }
         });
@@ -327,7 +351,8 @@
                     Message msg = mDelayHandler.obtainMessage(0, this);
                     mDelayHandler.sendMessageDelayed(msg, DELAY_BEFORE_SENDING_MSEC);
                 } else {
-                    SendTestMessages.testSendMessageUcs2(SendTestBroadcastActivity.this);
+                    SendTestMessages.testSendMessageUcs2(SendTestBroadcastActivity.this,
+                            getMessageId(), getCategory());
                 }
             }
         });
@@ -340,7 +365,8 @@
                     Message msg = mDelayHandler.obtainMessage(0, this);
                     mDelayHandler.sendMessageDelayed(msg, DELAY_BEFORE_SENDING_MSEC);
                 } else {
-                    SendTestMessages.testSendMessageUcs2Umts(SendTestBroadcastActivity.this);
+                    SendTestMessages.testSendMessageUcs2Umts(SendTestBroadcastActivity.this,
+                            getMessageId(), getCategory());
                 }
             }
         });
@@ -354,7 +380,8 @@
                     Message msg = mDelayHandler.obtainMessage(0, this);
                     mDelayHandler.sendMessageDelayed(msg, DELAY_BEFORE_SENDING_MSEC);
                 } else {
-                    SendTestMessages.testSendMessageUcs2MultipageUmts(SendTestBroadcastActivity.this);
+                    SendTestMessages.testSendMessageUcs2MultipageUmts(
+                            SendTestBroadcastActivity.this, getMessageId(), getCategory());
                 }
             }
         });
@@ -368,7 +395,8 @@
                     Message msg = mDelayHandler.obtainMessage(0, this);
                     mDelayHandler.sendMessageDelayed(msg, DELAY_BEFORE_SENDING_MSEC);
                 } else {
-                    SendTestMessages.testSendMessageUcs2WithLanguageInBody(SendTestBroadcastActivity.this);
+                    SendTestMessages.testSendMessageUcs2WithLanguageInBody(
+                            SendTestBroadcastActivity.this, getMessageId(), getCategory());
                 }
             }
         });
@@ -382,7 +410,8 @@
                     Message msg = mDelayHandler.obtainMessage(0, this);
                     mDelayHandler.sendMessageDelayed(msg, DELAY_BEFORE_SENDING_MSEC);
                 } else {
-                    SendTestMessages.testSendMessageUcs2WithLanguageUmts(SendTestBroadcastActivity.this);
+                    SendTestMessages.testSendMessageUcs2WithLanguageUmts(
+                            SendTestBroadcastActivity.this, getMessageId(), getCategory());
                 }
             }
         });
diff --git a/tests/src/com/android/cellbroadcastreceiver/tests/SendTestMessages.java b/tests/src/com/android/cellbroadcastreceiver/tests/SendTestMessages.java
index a8496ff..1867118 100644
--- a/tests/src/com/android/cellbroadcastreceiver/tests/SendTestMessages.java
+++ b/tests/src/com/android/cellbroadcastreceiver/tests/SendTestMessages.java
@@ -57,7 +57,7 @@
             "00000000000000000000000000000000000000000000");
 
     private static final byte[] gsm7BitTest = {
-            (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x40, (byte)0x11, (byte)0x41,
+            (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x01, (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,
@@ -73,7 +73,7 @@
     };
 
     private static final byte[] gsm7BitTestUmts = {
-            (byte)0x01, (byte)0x00, (byte)0x32, (byte)0xC0, (byte)0x00, (byte)0x40,
+            (byte)0x01, (byte)0x00, (byte)0x01, (byte)0xC0, (byte)0x00, (byte)0x40,
 
             (byte)0x01,
 
@@ -172,7 +172,7 @@
     };
 
     private static final byte[] gsm7BitTestNoPadding = {
-            (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x40, (byte)0x11, (byte)0x41,
+            (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x01, (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,
@@ -188,7 +188,7 @@
     };
 
     private static final byte[] gsm7BitTestNoPaddingUmts = {
-            (byte)0x01, (byte)0x00, (byte)0x32, (byte)0xC0, (byte)0x00, (byte)0x40,
+            (byte)0x01, (byte)0x00, (byte)0x01, (byte)0xC0, (byte)0x00, (byte)0x40,
 
             (byte)0x01,
 
@@ -211,7 +211,7 @@
     };
 
     private static final byte[] gsm7BitTestWithLanguage = {
-            (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x04, (byte)0x11, (byte)0x41,
+            (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x01, (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,
@@ -227,7 +227,7 @@
     };
 
     private static final byte[] gsm7BitTestWithLanguageInBody = {
-            (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x10, (byte)0x11, (byte)0x73,
+            (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x01, (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,
@@ -243,7 +243,7 @@
     };
 
     private static final byte[] gsm7BitTestWithLanguageInBodyUmts = {
-            (byte)0x01, (byte)0x00, (byte)0x32, (byte)0xC0, (byte)0x00, (byte)0x10,
+            (byte)0x01, (byte)0x00, (byte)0x01, (byte)0xC0, (byte)0x00, (byte)0x10,
 
             (byte)0x01,
 
@@ -266,7 +266,7 @@
     };
 
     private static final byte[] gsmUcs2Test = {
-            (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x48, (byte)0x11, (byte)0x00,
+            (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x01, (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,
@@ -282,7 +282,7 @@
     };
 
     private static final byte[] gsmUcs2TestUmts = {
-            (byte)0x01, (byte)0x00, (byte)0x32, (byte)0xC0, (byte)0x00, (byte)0x48,
+            (byte)0x01, (byte)0x00, (byte)0x01, (byte)0xC0, (byte)0x00, (byte)0x48,
 
             (byte)0x01,
 
@@ -305,7 +305,7 @@
     };
 
     private static final byte[] gsmUcs2TestMultipageUmts = {
-            (byte)0x01, (byte)0x00, (byte)0x32, (byte)0xC0, (byte)0x00, (byte)0x48,
+            (byte)0x01, (byte)0x00, (byte)0x01, (byte)0xC0, (byte)0x00, (byte)0x48,
 
             (byte)0x02,
 
@@ -345,7 +345,7 @@
     };
 
     private static final byte[] gsmUcs2TestWithLanguageInBody = {
-            (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x32, (byte)0x11, (byte)0x11, (byte)0x78,
+            (byte)0xC0, (byte)0x00, (byte)0x00, (byte)0x01, (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,
@@ -361,7 +361,7 @@
     };
 
     private static final byte[] gsmUcs2TestWithLanguageInBodyUmts = {
-            (byte)0x01, (byte)0x00, (byte)0x32, (byte)0xC0, (byte)0x00, (byte)0x11,
+            (byte)0x01, (byte)0x00, (byte)0x01, (byte)0xC0, (byte)0x00, (byte)0x11,
 
             (byte)0x01,
 
@@ -385,128 +385,161 @@
 
     private static final SmsCbLocation sEmptyLocation = new SmsCbLocation();
 
-    private static SmsCbMessage createFromPdu(byte[] pdu) {
+    private static SmsCbMessage createFromPdu(byte[] pdu, int serialNumber, int category) {
+        byte[][] pdus = new byte[1][];
+        pdus[0] = pdu;
+        return createFromPdus(pdus, serialNumber, category);
+    }
+
+    private static SmsCbMessage createFromPdus(byte[][] pdus, int serialNumber, int category) {
         try {
-            byte[][] pdus = new byte[1][];
-            pdus[0] = pdu;
+            for (byte[] pdu : pdus) {
+                if (pdu.length <= 88) {
+                    // GSM format cell broadcast
+                    Log.d(TAG, "setting GSM serial number to " + serialNumber);
+                    pdu[0] = (byte) ((serialNumber >>> 8) & 0xff);
+                    pdu[1] = (byte) (serialNumber & 0xff);
+                    if (category != 0) {
+                        Log.d(TAG, "setting GSM message identifier to " + category);
+                        pdu[2] = (byte) ((category >>> 8) & 0xff);
+                        pdu[3] = (byte) (category & 0xff);
+                    }
+                } else {
+                    // UMTS format cell broadcast
+                    Log.d(TAG, "setting UMTS serial number to " + serialNumber);
+                    pdu[3] = (byte) ((serialNumber >>> 8) & 0xff);
+                    pdu[4] = (byte) (serialNumber & 0xff);
+                    if (category != 0) {
+                        Log.d(TAG, "setting UMTS message identifier to " + category);
+                        pdu[1] = (byte) ((category >>> 8) & 0xff);
+                        pdu[2] = (byte) (category & 0xff);
+                    }
+                }
+            }
             return GsmSmsCbMessage.createSmsCbMessage(sEmptyLocation, pdus);
         } catch (IllegalArgumentException e) {
             return null;
         }
     }
 
-    private static SmsCbMessage createFromPdus(byte[][] pdus) {
-        try {
-            return GsmSmsCbMessage.createSmsCbMessage(sEmptyLocation, pdus);
-        } catch (IllegalArgumentException e) {
-            return null;
-        }
-    }
-
-    public static void testSendMessage7bit(Activity activity) {
+    public static void testSendMessage7bit(Activity activity, int serialNumber, int category) {
         Intent intent = new Intent(Intents.SMS_CB_RECEIVED_ACTION);
-        intent.putExtra("message", createFromPdu(gsm7BitTest));
+        intent.putExtra("message", createFromPdu(gsm7BitTest, serialNumber, category));
         activity.sendOrderedBroadcast(intent, "android.permission.RECEIVE_SMS");
     }
 
-    public static void testSendMessage7bitUmts(Activity activity) {
+    public static void testSendMessage7bitUmts(Activity activity, int serialNumber, int category) {
         Intent intent = new Intent(Intents.SMS_CB_RECEIVED_ACTION);
-        intent.putExtra("message", createFromPdu(gsm7BitTestUmts));
+        intent.putExtra("message", createFromPdu(gsm7BitTestUmts, serialNumber, category));
         activity.sendOrderedBroadcast(intent, "android.permission.RECEIVE_SMS");
     }
 
-    public static void testSendMessage7bitNoPadding(Activity activity) {
+    public static void testSendMessage7bitNoPadding(Activity activity, int serialNumber,
+            int category) {
         Intent intent = new Intent(Intents.SMS_CB_RECEIVED_ACTION);
-        intent.putExtra("message", createFromPdu(gsm7BitTestNoPadding));
+        intent.putExtra("message", createFromPdu(gsm7BitTestNoPadding, serialNumber, category));
         activity.sendOrderedBroadcast(intent, "android.permission.RECEIVE_SMS");
     }
 
-    public static void testSendMessage7bitNoPaddingUmts(Activity activity) {
+    public static void testSendMessage7bitNoPaddingUmts(Activity activity, int serialNumber,
+            int category) {
         Intent intent = new Intent(Intents.SMS_CB_RECEIVED_ACTION);
-        intent.putExtra("message", createFromPdu(gsm7BitTestNoPaddingUmts));
+        intent.putExtra("message", createFromPdu(gsm7BitTestNoPaddingUmts, serialNumber, category));
         activity.sendOrderedBroadcast(intent, "android.permission.RECEIVE_SMS");
     }
 
-    public static void testSendMessage7bitMultipageGsm(Activity activity) {
+    public static void testSendMessage7bitMultipageGsm(Activity activity, int serialNumber,
+            int category) {
         Intent intent = new Intent(Intents.SMS_CB_RECEIVED_ACTION);
         byte[][] pdus = new byte[2][];
         pdus[0] = gsm7BitTestMultipage1;
         pdus[1] = gsm7BitTestMultipage2;
-        intent.putExtra("message", createFromPdus(pdus));
+        intent.putExtra("message", createFromPdus(pdus, serialNumber, category));
         activity.sendOrderedBroadcast(intent, "android.permission.RECEIVE_SMS");
     }
 
-    public static void testSendMessage7bitMultipageUmts(Activity activity) {
+    public static void testSendMessage7bitMultipageUmts(Activity activity, int serialNumber,
+            int category) {
         Intent intent = new Intent(Intents.SMS_CB_RECEIVED_ACTION);
-        intent.putExtra("message", createFromPdu(gsm7BitTestMultipageUmts));
+        intent.putExtra("message", createFromPdu(gsm7BitTestMultipageUmts, serialNumber, category));
         activity.sendOrderedBroadcast(intent, "android.permission.RECEIVE_SMS");
     }
 
-    public static void testSendMessage7bitWithLanguage(Activity activity) {
+    public static void testSendMessage7bitWithLanguage(Activity activity, int serialNumber,
+            int category) {
         Intent intent = new Intent(Intents.SMS_CB_RECEIVED_ACTION);
-        intent.putExtra("message", createFromPdu(gsm7BitTestWithLanguage));
+        intent.putExtra("message", createFromPdu(gsm7BitTestWithLanguage, serialNumber, category));
         activity.sendOrderedBroadcast(intent, "android.permission.RECEIVE_SMS");
     }
 
-    public static void testSendMessage7bitWithLanguageInBody(Activity activity) {
+    public static void testSendMessage7bitWithLanguageInBody(Activity activity, int serialNumber,
+            int category) {
         Intent intent = new Intent(Intents.SMS_CB_RECEIVED_ACTION);
-        intent.putExtra("message", createFromPdu(gsm7BitTestWithLanguageInBody));
+        intent.putExtra("message", createFromPdu(gsm7BitTestWithLanguageInBody, serialNumber,
+                category));
         activity.sendOrderedBroadcast(intent, "android.permission.RECEIVE_SMS");
     }
 
-    public static void testSendMessage7bitWithLanguageInBodyUmts(Activity activity) {
+    public static void testSendMessage7bitWithLanguageInBodyUmts(Activity activity,
+            int serialNumber, int category) {
         Intent intent = new Intent(Intents.SMS_CB_RECEIVED_ACTION);
-        intent.putExtra("message", createFromPdu(gsm7BitTestWithLanguageInBodyUmts));
+        intent.putExtra("message", createFromPdu(gsm7BitTestWithLanguageInBodyUmts, serialNumber,
+                category));
         activity.sendOrderedBroadcast(intent, "android.permission.RECEIVE_SMS");
     }
 
-    public static void testSendMessageUcs2(Activity activity) {
+    public static void testSendMessageUcs2(Activity activity, int serialNumber, int category) {
         Intent intent = new Intent(Intents.SMS_CB_RECEIVED_ACTION);
-        intent.putExtra("message", createFromPdu(gsmUcs2Test));
+        intent.putExtra("message", createFromPdu(gsmUcs2Test, serialNumber, category));
         activity.sendOrderedBroadcast(intent, "android.permission.RECEIVE_SMS");
     }
 
-    public static void testSendMessageUcs2Umts(Activity activity) {
+    public static void testSendMessageUcs2Umts(Activity activity, int serialNumber, int category) {
         Intent intent = new Intent(Intents.SMS_CB_RECEIVED_ACTION);
-        intent.putExtra("message", createFromPdu(gsmUcs2TestUmts));
+        intent.putExtra("message", createFromPdu(gsmUcs2TestUmts, serialNumber, category));
         activity.sendOrderedBroadcast(intent, "android.permission.RECEIVE_SMS");
     }
 
-    public static void testSendMessageUcs2MultipageUmts(Activity activity) {
+    public static void testSendMessageUcs2MultipageUmts(Activity activity, int serialNumber,
+            int category) {
         Intent intent = new Intent(Intents.SMS_CB_RECEIVED_ACTION);
-        intent.putExtra("message", createFromPdu(gsmUcs2TestMultipageUmts));
+        intent.putExtra("message", createFromPdu(gsmUcs2TestMultipageUmts, serialNumber, category));
         activity.sendOrderedBroadcast(intent, "android.permission.RECEIVE_SMS");
     }
 
-    public static void testSendMessageUcs2WithLanguageInBody(Activity activity) {
+    public static void testSendMessageUcs2WithLanguageInBody(Activity activity, int serialNumber,
+            int category) {
         Intent intent = new Intent(Intents.SMS_CB_RECEIVED_ACTION);
-        intent.putExtra("message", createFromPdu(gsmUcs2TestWithLanguageInBody));
+        intent.putExtra("message", createFromPdu(gsmUcs2TestWithLanguageInBody, serialNumber,
+                category));
         activity.sendOrderedBroadcast(intent, "android.permission.RECEIVE_SMS");
     }
 
-    public static void testSendMessageUcs2WithLanguageUmts(Activity activity) {
+    public static void testSendMessageUcs2WithLanguageUmts(Activity activity, int serialNumber,
+            int category) {
         Intent intent = new Intent(Intents.SMS_CB_RECEIVED_ACTION);
-        intent.putExtra("message", createFromPdu(gsmUcs2TestWithLanguageInBodyUmts));
+        intent.putExtra("message", createFromPdu(gsmUcs2TestWithLanguageInBodyUmts, serialNumber,
+                category));
         activity.sendOrderedBroadcast(intent, "android.permission.RECEIVE_SMS");
     }
 
-    public static void testSendEtwsMessageNormal(Activity activity) {
+    public static void testSendEtwsMessageNormal(Activity activity, int serialNumber) {
         Intent intent = new Intent(Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION);
-        intent.putExtra("message", createFromPdu(etwsMessageNormal));
+        intent.putExtra("message", createFromPdu(etwsMessageNormal, serialNumber, 0));
         activity.sendOrderedBroadcast(intent,
                 "android.permission.RECEIVE_EMERGENCY_BROADCAST");
     }
 
-    public static void testSendEtwsMessageCancel(Activity activity) {
+    public static void testSendEtwsMessageCancel(Activity activity, int serialNumber) {
         Intent intent = new Intent(Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION);
-        intent.putExtra("message", createFromPdu(etwsMessageCancel));
+        intent.putExtra("message", createFromPdu(etwsMessageCancel, serialNumber, 0));
         activity.sendOrderedBroadcast(intent,
                 "android.permission.RECEIVE_EMERGENCY_BROADCAST");
     }
 
-    public static void testSendEtwsMessageTest(Activity activity) {
+    public static void testSendEtwsMessageTest(Activity activity, int serialNumber) {
         Intent intent = new Intent(Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION);
-        intent.putExtra("message", createFromPdu(etwsMessageTest));
+        intent.putExtra("message", createFromPdu(etwsMessageTest, serialNumber, 0));
         activity.sendOrderedBroadcast(intent,
                 "android.permission.RECEIVE_EMERGENCY_BROADCAST");
     }