Merge "Support partial of the United Arab Emirates requirement"
diff --git a/res/values-mcc310/config.xml b/res/values-mcc310/config.xml
new file mode 100644
index 0000000..d164ad7
--- /dev/null
+++ b/res/values-mcc310/config.xml
@@ -0,0 +1,76 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2018 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.
+-->
+
+<resources>
+    <!-- 4370, 4383 -->
+    <string-array name="cmas_presidential_alerts_channels_range_strings" translatable="false">
+        <item>0x1112-0x1112:rat=gsm, emergency=true</item>
+        <item>0x1000-0x1000:rat=cdma, emergency=true</item>
+        <!-- additional language -->
+        <item>0x111F-0x111F:rat=gsm, emergency=true, filter_language=true</item>
+    </string-array>
+    <!-- 4371~4372, 4384~4385 -->
+    <string-array name="cmas_alert_extreme_channels_range_strings" translatable="false">
+        <item>0x1113-0x1114:rat=gsm, emergency=true</item>
+        <item>0x1001-0x1001:rat=cdma, emergency=true</item>
+        <!-- additional language -->
+        <item>0x1120-0x1121:rat=gsm, emergency=true, filter_language=true</item>
+    </string-array>
+    <!-- 4373~4378, 4386~4391 -->
+    <string-array name="cmas_alerts_severe_range_strings" translatable="false">
+        <item>0x1115-0x111A:rat=gsm, emergency=true</item>
+        <item>0x1002-0x1002:rat=cdma, emergency=true</item>
+        <!-- additional language -->
+        <item>0x1122-0x1127:rat=gsm, emergency=true, filter_language=true</item>
+    </string-array>
+    <!-- 4379, 4392 -->
+    <string-array name="cmas_amber_alerts_channels_range_strings" translatable="false">
+        <item>0x111B-0x111B:rat=gsm, emergency=true</item>
+        <item>0x1003-0x1003:rat=cdma, emergency=true</item>
+        <!-- additional language -->
+        <item>0x1128-0x1128:rat=gsm, emergency=true, filter_language=true</item>
+    </string-array>
+    <!-- 4380~4382, 4393~4395 -->
+    <string-array name="required_monthly_test_range_strings" translatable="false">
+        <item>0x111C-0x111C:rat=gsm, emergency=true</item>
+        <item>0x1004-0x1004:rat=cdma, emergency=true</item>
+        <!-- additional language -->
+        <item>0x1129-0x1129:rat=gsm, emergency=true, filter_language=true</item>
+    </string-array>
+    <string-array name="exercise_alert_range_strings" translatable="false">
+        <item>0x111D-0x111D:rat=gsm, emergency=true</item>
+        <!-- additional language -->
+        <item>0x112A-0x112A:rat=gsm, emergency=true, filter_language=true</item>
+    </string-array>
+    <string-array name="operator_defined_alert_range_strings" translatable="false">
+        <item>0x111E-0x111E:rat=gsm, emergency=true</item>
+        <!-- additional language -->
+        <item>0x112B-0x112B:rat=gsm, emergency=true, filter_language=true</item>
+    </string-array>
+    <string-array name="public_safety_messages_channels_range_strings" translatable="false">
+        <!-- Public safety messages -->
+        <item>0x112C-0x112C:rat=gsm, emergency=true</item>
+        <!-- Public safety messages for additional language -->
+        <item>0x112D-0x112D:rat=gsm, emergency=true, filter_language=true</item>
+    </string-array>
+    <!-- Channels to receive state/local test messages -->
+    <string-array name="state_local_test_alert_range_strings" translatable="false">
+        <!-- state/local test -->
+        <item>0x112E-0x112E:rat=gsm, emergency=true</item>
+        <!-- state/local test additional language -->
+        <item>0x112F-0x112F:rat=gsm, emergency=true, filter_language=true</item>
+    </string-array>
+</resources>
diff --git a/res/values-mcc311/config.xml b/res/values-mcc311/config.xml
new file mode 120000
index 0000000..0dc8a9d
--- /dev/null
+++ b/res/values-mcc311/config.xml
@@ -0,0 +1 @@
+../values-mcc310/config.xml
\ No newline at end of file
diff --git a/res/values-mcc312/config.xml b/res/values-mcc312/config.xml
new file mode 120000
index 0000000..0dc8a9d
--- /dev/null
+++ b/res/values-mcc312/config.xml
@@ -0,0 +1 @@
+../values-mcc310/config.xml
\ No newline at end of file
diff --git a/res/values-mcc313/config.xml b/res/values-mcc313/config.xml
new file mode 120000
index 0000000..0dc8a9d
--- /dev/null
+++ b/res/values-mcc313/config.xml
@@ -0,0 +1 @@
+../values-mcc310/config.xml
\ No newline at end of file
diff --git a/res/values-mcc314/config.xml b/res/values-mcc314/config.xml
new file mode 120000
index 0000000..0dc8a9d
--- /dev/null
+++ b/res/values-mcc314/config.xml
@@ -0,0 +1 @@
+../values-mcc310/config.xml
\ No newline at end of file
diff --git a/res/values-mcc315/config.xml b/res/values-mcc315/config.xml
new file mode 120000
index 0000000..0dc8a9d
--- /dev/null
+++ b/res/values-mcc315/config.xml
@@ -0,0 +1 @@
+../values-mcc310/config.xml
\ No newline at end of file
diff --git a/res/values-mcc316/config.xml b/res/values-mcc316/config.xml
new file mode 120000
index 0000000..0dc8a9d
--- /dev/null
+++ b/res/values-mcc316/config.xml
@@ -0,0 +1 @@
+../values-mcc310/config.xml
\ No newline at end of file
diff --git a/res/values/config.xml b/res/values/config.xml
index ca7911d..2f1d264 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -90,6 +90,8 @@
     <string-array name="emergency_alerts_channels_range_strings" translatable="false"></string-array>
     <!-- Channels to receive public safety messages -->
     <string-array name="public_safety_messages_channels_range_strings" translatable="false"></string-array>
+    <!-- Channels to receive state/local test messages -->
+    <string-array name="state_local_test_alert_range_strings" translatable="false"></string-array>
 
     <!-- Values that are retrieved from the ListPreference.
          These must match the alert_reminder_interval_entries list above. -->
diff --git a/res/values/strings.xml b/res/values/strings.xml
index f88a93c..669fe16 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -90,6 +90,8 @@
     <string name="cb_other_message_identifiers">Broadcast messages</string>
     <!-- Dialog title for all public safety message broadcasts. [CHAR LIMIT=50] -->
     <string name="public_safety_message">Public safety message</string>
+    <!-- Dialog title for all state/local test alerts. [CHAR LIMIT=50] -->
+    <string name="state_local_test_alert">State/Local test</string>
 
     <!-- Dialog title or menu title of emergency alert. [CHAR LIMIT=50] -->
     <string name="emergency_alert">Emergency alert</string>
@@ -145,6 +147,10 @@
     <string name="enable_public_safety_messages_title">Public safety messages</string>
     <!-- Preference summary for enable public safety messages checkbox. [CHAR LIMIT=100] -->
     <string name="enable_public_safety_messages_summary">Recommended actions that can save lives or property</string>
+    <!-- Preference title for enable state/local test alerts checkbox. [CHAR LIMIT=100] -->
+    <string name="enable_state_local_test_alerts_title">State and local tests</string>
+    <!-- Preference summary for enable state/local test alerts checkbox. [CHAR LIMIT=100] -->
+    <string name="enable_state_local_test_alerts_summary">Receive test messages from state and local authorities</string>
     <!-- Preference title for enable emergency alerts messages checkbox. [CHAR LIMIT=100] -->
     <string name="enable_emergency_alerts_message_title">Emergency alerts</string>
     <!-- Preference summary for enable emergency alerts messages checkbox. [CHAR LIMIT=100] -->
diff --git a/res/xml/preferences.xml b/res/xml/preferences.xml
index 9fd3228..e72f446 100644
--- a/res/xml/preferences.xml
+++ b/res/xml/preferences.xml
@@ -58,12 +58,18 @@
                           android:summary="@string/enable_public_safety_messages_summary"
                           android:title="@string/enable_public_safety_messages_title" />
 
-        <!-- Enable receive monthly test -->
+        <!-- Enable required monthly test alerts -->
         <SwitchPreference android:defaultValue="false"
                           android:key="enable_test_alerts"
                           android:summary="@string/enable_cmas_test_alerts_summary"
                           android:title="@string/enable_cmas_test_alerts_title" />
 
+        <!-- Enable state/local test alerts -->
+        <SwitchPreference android:defaultValue="false"
+                          android:key="enable_state_local_test_alerts"
+                          android:summary="@string/enable_state_local_test_alerts_summary"
+                          android:title="@string/enable_state_local_test_alerts_title" />
+
         <!-- Default value is true for Brazil and India. This preference is ignored and hidden
         unless the boolean "config_showAreaUpdateInfoSettings" is set to true in the global resource. -->
         <SwitchPreference android:defaultValue="true"
diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertService.java b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertService.java
index 3875003..b969f62 100644
--- a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertService.java
+++ b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertService.java
@@ -44,6 +44,7 @@
 import android.telephony.SmsCbLocation;
 import android.telephony.SmsCbMessage;
 import android.telephony.SubscriptionManager;
+import android.text.TextUtils;
 import android.util.Log;
 
 import com.android.cellbroadcastreceiver.CellBroadcastChannelManager.CellBroadcastChannelRange;
@@ -222,6 +223,38 @@
         return time;
     }
 
+    /**
+     * Check if we should display the received cell broadcast message.
+     *
+     * @param cbm Cell broadcast message
+     * @return True if the message should be displayed to the user
+     */
+    private boolean shouldDisplayMessage(CellBroadcastMessage cbm) {
+        // Check if the channel is enabled by the user or configuration.
+        if (!isChannelEnabled(cbm)) {
+            Log.d(TAG, "ignoring alert of type " + cbm.getServiceCategory()
+                    + " by user preference");
+            return false;
+        }
+
+        // Check if we need to perform language filtering.
+        CellBroadcastChannelRange range = CellBroadcastChannelManager
+                .getCellBroadcastChannelRangeFromMessage(getApplicationContext(), cbm);
+        if (range != null && range.mFilterLanguage) {
+            // If the message's language does not match device's message, we don't display the
+            // message.
+            String messageLanguage = cbm.getLanguageCode();
+            String deviceLanguage = Locale.getDefault().getLanguage();
+            if (!TextUtils.isEmpty(messageLanguage)
+                    && !messageLanguage.equalsIgnoreCase(deviceLanguage)) {
+                Log.d(TAG, "ignoring the alert due to language mismatch. Message lang="
+                        + messageLanguage + ", device lang=" + deviceLanguage);
+                return false;
+            }
+        }
+        return true;
+    }
+
     private void handleCellBroadcastIntent(Intent intent) {
         Bundle extras = intent.getExtras();
         if (extras == null) {
@@ -244,9 +277,7 @@
             Log.e(TAG, "Invalid subscription id");
         }
 
-        if (!isMessageEnabled(cbm)) {
-            Log.d(TAG, "ignoring alert of type " + cbm.getServiceCategory() +
-                    " by user preference");
+        if (!shouldDisplayMessage(cbm)) {
             return;
         }
 
@@ -351,13 +382,12 @@
     }
 
     /**
-     * Check if the message is enabled. The message could be enabled or disabled by users or
-     * roaming conditions.
+     * Check if the message's channel is enabled on the device.
      *
      * @param message the message to check
-     * @return true if the user has enabled this message type; false otherwise
+     * @return true if the channel is enabled on the device, otherwise false.
      */
-    private boolean isMessageEnabled(CellBroadcastMessage message) {
+    private boolean isChannelEnabled(CellBroadcastMessage message) {
 
         SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
         // Check if all emergency alerts are disabled.
@@ -482,6 +512,13 @@
                     .getBoolean(CellBroadcastSettings.KEY_ENABLE_PUBLIC_SAFETY_MESSAGES,
                             true);
         }
+        if (CellBroadcastChannelManager.checkCellBroadcastChannelRange(subId,
+                channel, R.array.state_local_test_alert_range_strings, this)) {
+            return emergencyAlertEnabled
+                    && PreferenceManager.getDefaultSharedPreferences(this)
+                    .getBoolean(CellBroadcastSettings.KEY_ENABLE_STATE_LOCAL_TEST_ALERTS,
+                            false);
+        }
         return true;
     }
 
@@ -528,11 +565,7 @@
         } else {
             int channel = message.getServiceCategory();
             ArrayList<CellBroadcastChannelRange> ranges = CellBroadcastChannelManager
-                    .getInstance().getCellBroadcastChannelRanges(getApplicationContext(),
-                            R.array.additional_cbs_channels_strings);
-            ranges.addAll(CellBroadcastChannelManager
-                    .getInstance().getCellBroadcastChannelRanges(getApplicationContext(),
-                            R.array.public_safety_messages_channels_range_strings));
+                    .getAllCellBroadcastChannelRanges(getApplicationContext());
             if (ranges != null) {
                 for (CellBroadcastChannelRange range : ranges) {
                     if (channel >= range.mStartId && channel <= range.mEndId) {
diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastChannelManager.java b/src/com/android/cellbroadcastreceiver/CellBroadcastChannelManager.java
index fd716e2..9797155 100644
--- a/src/com/android/cellbroadcastreceiver/CellBroadcastChannelManager.java
+++ b/src/com/android/cellbroadcastreceiver/CellBroadcastChannelManager.java
@@ -62,19 +62,33 @@
                     R.array.operator_defined_alert_range_strings,
                     R.array.etws_alerts_range_strings,
                     R.array.etws_test_alerts_range_strings,
-                    R.array.public_safety_messages_channels_range_strings
+                    R.array.public_safety_messages_channels_range_strings,
+                    R.array.state_local_test_alert_range_strings
             ));
+
+    private static ArrayList<CellBroadcastChannelRange> sAllCellBroadcastChannelRanges = null;
+
     /**
      * Cell broadcast channel range
      * A range is consisted by starting channel id, ending channel id, and the alert type
      */
     public static class CellBroadcastChannelRange {
-
+        /** Defines the type of the alert. */
         private static final String KEY_TYPE = "type";
+        /** Defines if the alert is emergency. */
         private static final String KEY_EMERGENCY = "emergency";
+        /** Defines the network RAT for the alert. */
         private static final String KEY_RAT = "rat";
+        /** Defines the scope of the alert. */
         private static final String KEY_SCOPE = "scope";
+        /** Defines the vibration pattern of the alert. */
         private static final String KEY_VIBRATION = "vibration";
+        /**
+         * Defines whether the channel needs language filter or not. True indicates that the alert
+         * will only pop-up when the alert's language matches the device's language.
+         */
+        private static final String KEY_FILTER_LANGUAGE = "filter_language";
+
 
         public static final int SCOPE_UNKNOWN       = 0;
         public static final int SCOPE_CARRIER       = 1;
@@ -92,6 +106,7 @@
         public int mRat;
         public int mScope;
         public int[] mVibrationPattern;
+        public boolean mFilterLanguage;
 
         public CellBroadcastChannelRange(Context context, String channelRange) throws Exception {
 
@@ -101,6 +116,7 @@
             mScope = SCOPE_UNKNOWN;
             mVibrationPattern = context.getResources().getIntArray(
                     R.array.default_vibration_pattern);
+            mFilterLanguage = false;
 
             int colonIndex = channelRange.indexOf(':');
             if (colonIndex != -1) {
@@ -147,6 +163,11 @@
                                     }
                                 }
                                 break;
+                            case KEY_FILTER_LANGUAGE:
+                                if (value.equalsIgnoreCase("true")) {
+                                    mFilterLanguage = true;
+                                }
+                                break;
                         }
                     }
                 }
@@ -209,6 +230,26 @@
     }
 
     /**
+     * Get all cell broadcast channels
+     *
+     * @param context Application context
+     * @return all cell broadcast channels
+     */
+    public static ArrayList<CellBroadcastChannelRange> getAllCellBroadcastChannelRanges(
+            Context context) {
+        if (sAllCellBroadcastChannelRanges != null) return sAllCellBroadcastChannelRanges;
+
+        ArrayList<CellBroadcastChannelRange> result = new ArrayList<>();
+
+        for (int key : sCellBroadcastRangeResourceKeys) {
+            result.addAll(getCellBroadcastChannelRanges(context, key));
+        }
+
+        sAllCellBroadcastChannelRanges = result;
+        return result;
+    }
+
+    /**
      * @param subId Subscription index
      * @param channel Cell broadcast message channel
      * @param context Application context
diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastConfigService.java b/src/com/android/cellbroadcastreceiver/CellBroadcastConfigService.java
index 95d7154..196d529 100644
--- a/src/com/android/cellbroadcastreceiver/CellBroadcastConfigService.java
+++ b/src/com/android/cellbroadcastreceiver/CellBroadcastConfigService.java
@@ -154,10 +154,12 @@
                 && prefs.getBoolean(CellBroadcastSettings.KEY_ENABLE_AREA_UPDATE_INFO_ALERTS,
                 false);
 
-        // Non-CMAS channels
         boolean enablePublicSafetyMessagesChannelAlerts = enableAlertsMasterToggle
                 && prefs.getBoolean(CellBroadcastSettings.KEY_ENABLE_PUBLIC_SAFETY_MESSAGES,
                 true);
+        boolean enableStateLocalTestAlerts = enableAlertsMasterToggle
+                && prefs.getBoolean(CellBroadcastSettings.KEY_ENABLE_STATE_LOCAL_TEST_ALERTS,
+                false);
 
         boolean enableEmergencyAlerts = enableAlertsMasterToggle && prefs.getBoolean(
                 CellBroadcastSettings.KEY_ENABLE_EMERGENCY_ALERTS, true);
@@ -173,6 +175,7 @@
             log("enableAreaUpdateInfoAlerts = " + enableAreaUpdateInfoAlerts);
             log("enablePublicSafetyMessagesChannelAlerts = "
                     + enablePublicSafetyMessagesChannelAlerts);
+            log("enableStateLocalTestAlerts = " + enableStateLocalTestAlerts);
             log("enableEmergencyAlerts = " + enableEmergencyAlerts);
         }
 
@@ -224,6 +227,11 @@
                 CellBroadcastChannelManager.getInstance().getCellBroadcastChannelRanges(this,
                         R.array.public_safety_messages_channels_range_strings));
 
+        // Enable/Disable GSM state/local test alerts.
+        setCellBroadcastRange(manager, enableStateLocalTestAlerts,
+                CellBroadcastChannelManager.getInstance().getCellBroadcastChannelRanges(this,
+                        R.array.state_local_test_alert_range_strings));
+
         /** Enable non-CMAS series messages. */
 
         setCellBroadcastRange(manager, enableEmergencyAlerts,
diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastResources.java b/src/com/android/cellbroadcastreceiver/CellBroadcastResources.java
index e6cabe8..2816a4b 100644
--- a/src/com/android/cellbroadcastreceiver/CellBroadcastResources.java
+++ b/src/com/android/cellbroadcastreceiver/CellBroadcastResources.java
@@ -315,6 +315,10 @@
                 serviceCategory, R.array.public_safety_messages_channels_range_strings, context)) {
             return R.string.public_safety_message;
         }
+        if (CellBroadcastChannelManager.checkCellBroadcastChannelRange(subId,
+                serviceCategory, R.array.state_local_test_alert_range_strings, context)) {
+            return R.string.state_local_test_alert;
+        }
 
         if (CellBroadcastChannelManager.isEmergencyMessage(context, cbm)) {
             ArrayList<CellBroadcastChannelRange> ranges = CellBroadcastChannelManager
diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastSettings.java b/src/com/android/cellbroadcastreceiver/CellBroadcastSettings.java
index 05d7187..0f63573 100644
--- a/src/com/android/cellbroadcastreceiver/CellBroadcastSettings.java
+++ b/src/com/android/cellbroadcastreceiver/CellBroadcastSettings.java
@@ -90,6 +90,10 @@
     // Whether to display monthly test messages (default is disabled).
     public static final String KEY_ENABLE_TEST_ALERTS = "enable_test_alerts";
 
+    // Whether to display state/local test messages (default disabled).
+    public static final String KEY_ENABLE_STATE_LOCAL_TEST_ALERTS =
+            "enable_state_local_test_alerts";
+
     // Preference key for whether to enable area update information notifications
     // Enabled by default for phones sold in Brazil and India, otherwise this setting may be hidden.
     public static final String KEY_ENABLE_AREA_UPDATE_INFO_ALERTS =
@@ -158,6 +162,7 @@
         private TwoStatePreference mFullVolumeCheckBox;
         private TwoStatePreference mAreaUpdateInfoCheckBox;
         private TwoStatePreference mTestCheckBox;
+        private TwoStatePreference mStateLocalTestCheckBox;
         private Preference mAlertHistory;
         private PreferenceCategory mAlertCategory;
         private PreferenceCategory mAlertPreferencesCategory;
@@ -202,6 +207,8 @@
                     findPreference(KEY_ENABLE_AREA_UPDATE_INFO_ALERTS);
             mTestCheckBox = (TwoStatePreference)
                     findPreference(KEY_ENABLE_TEST_ALERTS);
+            mStateLocalTestCheckBox = (TwoStatePreference)
+                    findPreference(KEY_ENABLE_STATE_LOCAL_TEST_ALERTS);
             mAlertHistory = findPreference(KEY_EMERGENCY_ALERT_HISTORY);
             mDevSettingCategory = (PreferenceCategory)
                     findPreference(KEY_CATEGORY_DEV_SETTINGS);
@@ -316,6 +323,8 @@
                 }
             }
 
+
+
             if (!Resources.getSystem().getBoolean(
                     com.android.internal.R.bool.config_showAreaUpdateInfoSettings)) {
                 if (mAlertCategory != null) {
@@ -347,6 +356,17 @@
                 }
             }
 
+            if (CellBroadcastChannelManager.getCellBroadcastChannelRanges(
+                    this.getContext(),
+                    R.array.state_local_test_alert_range_strings).isEmpty()) {
+                // Remove state local test messages
+                if (mAlertCategory != null) {
+                    if (mStateLocalTestCheckBox != null) {
+                        mAlertCategory.removePreference(mStateLocalTestCheckBox);
+                    }
+                }
+            }
+
             if (mAreaUpdateInfoCheckBox != null) {
                 mAreaUpdateInfoCheckBox.setOnPreferenceChangeListener(startConfigServiceListener);
             }
@@ -374,6 +394,10 @@
             if (mTestCheckBox != null) {
                 mTestCheckBox.setOnPreferenceChangeListener(startConfigServiceListener);
             }
+            if (mStateLocalTestCheckBox != null) {
+                mStateLocalTestCheckBox.setOnPreferenceChangeListener(
+                        startConfigServiceListener);
+            }
 
             if (mAlertHistory != null) {
                 mAlertHistory.setOnPreferenceClickListener(
@@ -467,6 +491,10 @@
                 mPublicSafetyMessagesChannelCheckBox.setEnabled(alertsEnabled);
                 mPublicSafetyMessagesChannelCheckBox.setChecked(alertsEnabled);
             }
+            if (mStateLocalTestCheckBox != null) {
+                mStateLocalTestCheckBox.setEnabled(alertsEnabled);
+                mStateLocalTestCheckBox.setChecked(alertsEnabled);
+            }
         }
     }
 
diff --git a/tests/testapp/res/layout/test_buttons.xml b/tests/testapp/res/layout/test_buttons.xml
index 5e3d26f..f43b360 100644
--- a/tests/testapp/res/layout/test_buttons.xml
+++ b/tests/testapp/res/layout/test_buttons.xml
@@ -168,12 +168,19 @@
         android:layout_marginLeft="20dp"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content" />
+
     <Button android:id="@+id/button_gsm_public_safety_message"
         android:text="@string/button_gsm_public_safety_message"
         android:layout_marginLeft="20dp"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content" />
 
+    <Button android:id="@+id/button_gsm_state_local_test_alert"
+        android:text="@string/button_gsm_state_local_test_alert"
+        android:layout_marginLeft="20dp"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
     <!-- Other Alerts -->
     <Button android:id="@+id/button_gsm_7bit_type"
         android:text="@string/button_gsm_7bit_type"
diff --git a/tests/testapp/res/values/strings.xml b/tests/testapp/res/values/strings.xml
index a9bb60c..f96c1d9 100644
--- a/tests/testapp/res/values/strings.xml
+++ b/tests/testapp/res/values/strings.xml
@@ -33,6 +33,7 @@
     <string name="button_gsm_cmas_amber_alert" translatable="false">Send GSM CMAS AMBER Alert</string>
     <string name="button_gsm_cmas_monthly_test" translatable="false">Send GSM CMAS Monthly Test Alert</string>
     <string name="button_gsm_public_safety_message" translatable="false">Send GSM Public Safety Message</string>
+    <string name="button_gsm_state_local_test_alert" translatable="false">Send State/Local Test Alert</string>
     <string name="button_gsm_7bit_type" translatable="false">Send GSM 7 Bit Test Broadcast</string>
     <string name="button_gsm_7bit_umts_type" translatable="false">Send UMTS 7 Bit Test Broadcast</string>
     <string name="button_gsm_7bit_nopadding_type" translatable="false">Send GSM 7 Bit Full Length Broadcast</string>
diff --git a/tests/testapp/src/com/android/cellbroadcastreceiver/tests/SendGsmCmasMessages.java b/tests/testapp/src/com/android/cellbroadcastreceiver/tests/SendGsmCmasMessages.java
index 87cbeb4..fb4aa36 100644
--- a/tests/testapp/src/com/android/cellbroadcastreceiver/tests/SendGsmCmasMessages.java
+++ b/tests/testapp/src/com/android/cellbroadcastreceiver/tests/SendGsmCmasMessages.java
@@ -53,6 +53,8 @@
 
     private static final String PUBLIC_SAFETY_MESSAGE = "This is a public safety message.";
 
+    private static final String STATE_LOCAL_ALERT = "This is a state/local test message.";
+
     private static void sendBroadcast(Activity activity, SmsCbMessage cbMessage) {
         Intent intent = new Intent(Telephony.Sms.Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION);
         intent.putExtra("message", cbMessage);
@@ -114,7 +116,7 @@
 
     public static void testSendPublicSafetyMessagesAlert(Activity activity, int serialNumber) {
         SmsCbMessage cbMessage = createCmasSmsMessage(
-                911, serialNumber, "en",
+                SmsCbConstants.MESSAGE_ID_CMAS_ALERT_PUBLIC_SAFETY, serialNumber, "en",
                 PUBLIC_SAFETY_MESSAGE, SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN,
                 SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN, SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN,
                 SmsCbMessage.MESSAGE_PRIORITY_NORMAL);
@@ -122,6 +124,15 @@
         sendBroadcast(activity, cbMessage);
     }
 
+    public static void testSendStateLocalTestAlert(Activity activity, int serialNumber) {
+        SmsCbMessage cbMessage = createCmasSmsMessage(
+                SmsCbConstants.MESSAGE_ID_CMAS_ALERT_STATE_LOCAL_TEST, serialNumber, "en",
+                STATE_LOCAL_ALERT, SmsCbCmasInfo.CMAS_SEVERITY_UNKNOWN,
+                SmsCbCmasInfo.CMAS_URGENCY_UNKNOWN, SmsCbCmasInfo.CMAS_CERTAINTY_UNKNOWN,
+                SmsCbMessage.MESSAGE_PRIORITY_NORMAL);
+
+        sendBroadcast(activity, cbMessage);
+    }
 
     /**
      * Create a new SmsCbMessage for testing GSM CMAS support.
diff --git a/tests/testapp/src/com/android/cellbroadcastreceiver/tests/SendTestBroadcastActivity.java b/tests/testapp/src/com/android/cellbroadcastreceiver/tests/SendTestBroadcastActivity.java
index d41427c..38c68a6 100644
--- a/tests/testapp/src/com/android/cellbroadcastreceiver/tests/SendTestBroadcastActivity.java
+++ b/tests/testapp/src/com/android/cellbroadcastreceiver/tests/SendTestBroadcastActivity.java
@@ -339,6 +339,21 @@
             }
         });
 
+        /* Send a GSM state/local test alert to app. */
+        Button GsmStateLocalTestAlertButton = findViewById(
+                R.id.button_gsm_state_local_test_alert);
+        GsmStateLocalTestAlertButton.setOnClickListener(new OnClickListener() {
+            public void onClick(View v) {
+                if (mDelayBeforeSending && v != null) {
+                    Message msg = mDelayHandler.obtainMessage(0, this);
+                    mDelayHandler.sendMessageDelayed(msg, DELAY_BEFORE_SENDING_MSEC);
+                } else {
+                    SendGsmCmasMessages.testSendStateLocalTestAlert(
+                            SendTestBroadcastActivity.this, getSerialNumber());
+                }
+            }
+        });
+
         /* Send a GSM 7-bit broadcast message to app. */
         Button gsm7bitTypeButton = (Button) findViewById(R.id.button_gsm_7bit_type);
         gsm7bitTypeButton.setOnClickListener(new OnClickListener() {