Added custom alert tone support

1. Added vocalized tones (with 5 different langauges) for
ETWS earthquake, tsunami, and other disaster messages.
2. Added a custom tone for Canadian WPAS messages.
3. Added custom vibration pattern support.

bug: 27688980

Change-Id: Id3a980784b5a6a6fb4cc3a4dfaeabea420b03017
diff --git a/res/raw-es/etws_earthquake.ogg b/res/raw-es/etws_earthquake.ogg
new file mode 100644
index 0000000..e4946ef
--- /dev/null
+++ b/res/raw-es/etws_earthquake.ogg
Binary files differ
diff --git a/res/raw-es/etws_tsunami.ogg b/res/raw-es/etws_tsunami.ogg
new file mode 100644
index 0000000..5e216ed
--- /dev/null
+++ b/res/raw-es/etws_tsunami.ogg
Binary files differ
diff --git a/res/raw-ja/etws_earthquake.ogg b/res/raw-ja/etws_earthquake.ogg
new file mode 100644
index 0000000..ace2502
--- /dev/null
+++ b/res/raw-ja/etws_earthquake.ogg
Binary files differ
diff --git a/res/raw-ja/etws_tsunami.ogg b/res/raw-ja/etws_tsunami.ogg
new file mode 100644
index 0000000..79bf46d
--- /dev/null
+++ b/res/raw-ja/etws_tsunami.ogg
Binary files differ
diff --git a/res/raw-ko/etws_earthquake.ogg b/res/raw-ko/etws_earthquake.ogg
new file mode 100644
index 0000000..65bcbc3
--- /dev/null
+++ b/res/raw-ko/etws_earthquake.ogg
Binary files differ
diff --git a/res/raw-ko/etws_tsunami.ogg b/res/raw-ko/etws_tsunami.ogg
new file mode 100644
index 0000000..e6b1527
--- /dev/null
+++ b/res/raw-ko/etws_tsunami.ogg
Binary files differ
diff --git a/res/raw-mcc302/cmas_default.ogg b/res/raw-mcc302/cmas_default.ogg
new file mode 100644
index 0000000..f044ef2
--- /dev/null
+++ b/res/raw-mcc302/cmas_default.ogg
Binary files differ
diff --git a/res/raw-mcc302/etws_default.ogg b/res/raw-mcc302/etws_default.ogg
new file mode 100644
index 0000000..f044ef2
--- /dev/null
+++ b/res/raw-mcc302/etws_default.ogg
Binary files differ
diff --git a/res/raw-mcc302/etws_earthquake.ogg b/res/raw-mcc302/etws_earthquake.ogg
new file mode 100644
index 0000000..f044ef2
--- /dev/null
+++ b/res/raw-mcc302/etws_earthquake.ogg
Binary files differ
diff --git a/res/raw-mcc302/etws_other_disaster.ogg b/res/raw-mcc302/etws_other_disaster.ogg
new file mode 100644
index 0000000..f044ef2
--- /dev/null
+++ b/res/raw-mcc302/etws_other_disaster.ogg
Binary files differ
diff --git a/res/raw-mcc302/etws_tsunami.ogg b/res/raw-mcc302/etws_tsunami.ogg
new file mode 100644
index 0000000..f044ef2
--- /dev/null
+++ b/res/raw-mcc302/etws_tsunami.ogg
Binary files differ
diff --git a/res/raw-pt/etws_earthquake.ogg b/res/raw-pt/etws_earthquake.ogg
new file mode 100644
index 0000000..8696414
--- /dev/null
+++ b/res/raw-pt/etws_earthquake.ogg
Binary files differ
diff --git a/res/raw-pt/etws_tsunami.ogg b/res/raw-pt/etws_tsunami.ogg
new file mode 100644
index 0000000..4bcacd2
--- /dev/null
+++ b/res/raw-pt/etws_tsunami.ogg
Binary files differ
diff --git a/res/raw-zh/etws_earthquake.ogg b/res/raw-zh/etws_earthquake.ogg
new file mode 100644
index 0000000..86f97a9
--- /dev/null
+++ b/res/raw-zh/etws_earthquake.ogg
Binary files differ
diff --git a/res/raw-zh/etws_tsunami.ogg b/res/raw-zh/etws_tsunami.ogg
new file mode 100644
index 0000000..8866c67
--- /dev/null
+++ b/res/raw-zh/etws_tsunami.ogg
Binary files differ
diff --git a/res/raw/attention_signal.ogg b/res/raw/cmas_default.ogg
similarity index 100%
rename from res/raw/attention_signal.ogg
rename to res/raw/cmas_default.ogg
Binary files differ
diff --git a/res/raw/etws_default.ogg b/res/raw/etws_default.ogg
new file mode 100644
index 0000000..673091e
--- /dev/null
+++ b/res/raw/etws_default.ogg
Binary files differ
diff --git a/res/raw/etws_earthquake.ogg b/res/raw/etws_earthquake.ogg
new file mode 100644
index 0000000..8bb3f0c
--- /dev/null
+++ b/res/raw/etws_earthquake.ogg
Binary files differ
diff --git a/res/raw/etws_other_disaster.ogg b/res/raw/etws_other_disaster.ogg
new file mode 100644
index 0000000..fc79098
--- /dev/null
+++ b/res/raw/etws_other_disaster.ogg
Binary files differ
diff --git a/res/raw/etws_tsunami.ogg b/res/raw/etws_tsunami.ogg
new file mode 100644
index 0000000..4ce8f78
--- /dev/null
+++ b/res/raw/etws_tsunami.ogg
Binary files differ
diff --git a/res/values-mcc302/strings.xml b/res/values-mcc302/strings.xml
index 3a92819..a7b1eb5 100644
--- a/res/values-mcc302/strings.xml
+++ b/res/values-mcc302/strings.xml
@@ -46,4 +46,25 @@
     <!-- Do not translate. Default value that in the ListPreference.
          These must be a subset of the alert_reminder_interval_values list above. -->
     <string name="alert_reminder_interval_default_value">2</string>
+
+    <!-- Do not translate. Values that for the vibration pattern. -->
+    <integer-array name="default_vibration_pattern">
+        <item>0</item>      <!-- the number of milliseconds to wait before turning the vibrator on -->
+        <item>500</item>    <!-- the number of milliseconds for which to keep the vibrator on before turning it off -->
+        <item>500</item>    <!-- Subsequent values alternate between durations in milliseconds to turn the vibrator off or to turn the vibrator on -->
+        <item>500</item>
+        <item>500</item>
+        <item>500</item>
+        <item>500</item>
+        <item>500</item>
+        <item>500</item>
+        <item>500</item>
+        <item>500</item>
+        <item>500</item>
+        <item>500</item>
+        <item>500</item>
+        <item>500</item>
+        <item>500</item>
+        <item>500</item>
+     </integer-array>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index ce69b52..444eae6 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -259,4 +259,20 @@
     <!-- Do not translate. Default value that in the ListPreference.
          These must be a subset of the alert_reminder_interval_values list above. -->
     <string name="alert_reminder_interval_default_value">0</string>
+
+    <!-- Do not translate. Values that for the vibration pattern. -->
+    <integer-array name="default_vibration_pattern">
+        <item>0</item>      <!-- the number of milliseconds to wait before turning the vibrator on -->
+        <item>2000</item>   <!-- the number of milliseconds for which to keep the vibrator on before turning it off -->
+        <item>500</item>    <!-- Subsequent values alternate between durations in milliseconds to turn the vibrator off or to turn the vibrator on -->
+        <item>1000</item>
+        <item>500</item>
+        <item>1000</item>
+        <item>500</item>
+        <item>2000</item>
+        <item>500</item>
+        <item>1000</item>
+        <item>500</item>
+        <item>1000</item>
+    </integer-array>
 </resources>
diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertAudio.java b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertAudio.java
index 1d08cad..fa6ec07 100644
--- a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertAudio.java
+++ b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertAudio.java
@@ -24,11 +24,8 @@
 import android.content.res.Resources;
 import android.media.AudioManager;
 import android.media.MediaPlayer;
-import android.media.MediaPlayer.OnErrorListener;
 import android.media.MediaPlayer.OnCompletionListener;
-import android.media.Ringtone;
-import android.media.RingtoneManager;
-import android.net.Uri;
+import android.media.MediaPlayer.OnErrorListener;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
@@ -39,7 +36,6 @@
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
-import java.util.HashMap;
 import java.util.Locale;
 import java.util.MissingResourceException;
 
@@ -69,6 +65,10 @@
     public static final String ALERT_AUDIO_MESSAGE_DEFAULT_LANGUAGE =
             "com.android.cellbroadcastreceiver.ALERT_AUDIO_MESSAGE_DEFAULT_LANGUAGE";
 
+    /** Extra for alert tone type */
+    public static final String ALERT_AUDIO_TONE_TYPE =
+            "com.android.cellbroadcastreceiver.ALERT_AUDIO_TONE_TYPE";
+
     /** Extra for alert audio vibration enabled (from settings). */
     public static final String ALERT_AUDIO_VIBRATE_EXTRA =
             "com.android.cellbroadcastreceiver.ALERT_AUDIO_VIBRATE";
@@ -82,10 +82,6 @@
     /** Pause duration between alert sound and alert speech. */
     private static final int PAUSE_DURATION_BEFORE_SPEAKING_MSEC = 1000;
 
-    /** Vibration uses the same on/off pattern as the CMAS alert tone */
-    private static final long[] sVibratePattern = { 0, 2000, 500, 1000, 500, 1000, 500,
-            2000, 500, 1000, 500, 1000};
-
     private static final int STATE_IDLE = 0;
     private static final int STATE_ALERTING = 1;
     private static final int STATE_PAUSING = 2;
@@ -337,7 +333,11 @@
         }
 
         if (mEnableAudio || mEnableVibrate) {
-            play();
+            ToneType toneType = ToneType.CMAS_DEFAULT;
+            if (intent.getSerializableExtra(ALERT_AUDIO_TONE_TYPE) != null) {
+                toneType = (ToneType) intent.getSerializableExtra(ALERT_AUDIO_TONE_TYPE);
+            }
+            playAlertTone(toneType);
         } else {
             stopSelf();
             return START_NOT_STICKY;
@@ -354,19 +354,30 @@
     private static final float IN_CALL_VOLUME = 0.125f;
 
     /**
-     * Start playing the alert sound, and send delayed message when it's time to stop.
+     * Start playing the alert sound.
+     * @param toneType the alert tone type (e.g. default, earthquake, tsunami, etc..)
      */
-    private void play() {
+    private void playAlertTone(ToneType toneType) {
         // stop() checks to see if we are already playing.
         stop();
 
-        if (DBG) log("play()");
+        log("playAlertTone: toneType=" + toneType);
 
         // Start the vibration first.
         if (mEnableVibrate) {
-            mVibrator.vibrate(sVibratePattern, -1);
+
+            int[] patternArray = getApplicationContext().getResources().
+                    getIntArray(R.array.default_vibration_pattern);
+            long[] vibrationPattern = new long[patternArray.length];
+
+            for (int i = 0; i < patternArray.length; i++) {
+                vibrationPattern[i] = patternArray[i];
+            }
+
+            mVibrator.vibrate(vibrationPattern, -1);
         }
 
+
         if (mEnableAudio) {
             // future optimization: reuse media player object
             mMediaPlayer = new MediaPlayer();
@@ -397,12 +408,40 @@
                     mMediaPlayer.setVolume(IN_CALL_VOLUME, IN_CALL_VOLUME);
                 }
 
+                log("Locale=" + getResources().getConfiguration().getLocales());
+
+                // Load the tones based on type
+                switch (toneType) {
+                    case EARTHQUAKE:
+                        setDataSourceFromResource(getResources(), mMediaPlayer,
+                                R.raw.etws_earthquake);
+                        break;
+                    case TSUNAMI:
+                        setDataSourceFromResource(getResources(), mMediaPlayer,
+                                R.raw.etws_tsunami);
+                        break;
+                    case OTHER:
+                        setDataSourceFromResource(getResources(), mMediaPlayer,
+                                R.raw.etws_other_disaster);
+                        break;
+                    case ETWS_DEFAULT:
+                        setDataSourceFromResource(getResources(), mMediaPlayer,
+                                R.raw.etws_default);
+                    case CMAS_DEFAULT:
+                    default:
+                        setDataSourceFromResource(getResources(), mMediaPlayer,
+                                R.raw.cmas_default);
+                }
+
                 // start playing alert audio (unless master volume is vibrate only or silent).
-                setDataSourceFromResource(getResources(), mMediaPlayer,
-                        R.raw.attention_signal);
                 mAudioManager.requestAudioFocus(null, AudioManager.STREAM_NOTIFICATION,
                         AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
-                startAlarm(mMediaPlayer);
+
+                mMediaPlayer.setAudioStreamType(AudioManager.STREAM_NOTIFICATION);
+                mMediaPlayer.setLooping(false);
+                mMediaPlayer.prepare();
+                mMediaPlayer.start();
+
             } catch (Exception ex) {
                 loge("Failed to play alert sound: " + ex);
             }
@@ -411,15 +450,6 @@
         mState = STATE_ALERTING;
     }
 
-    // Do the common stuff when starting the alarm.
-    private static void startAlarm(MediaPlayer player)
-            throws java.io.IOException, IllegalArgumentException, IllegalStateException {
-        player.setAudioStreamType(AudioManager.STREAM_NOTIFICATION);
-        player.setLooping(false);
-        player.prepare();
-        player.start();
-    }
-
     private static void setDataSourceFromResource(Resources resources,
             MediaPlayer player, int res) throws java.io.IOException {
         AssetFileDescriptor afd = resources.openRawResourceFd(res);
@@ -430,22 +460,6 @@
         }
     }
 
-    private void playAlertReminderSound() {
-        Uri notificationUri = RingtoneManager.getDefaultUri(
-                RingtoneManager.TYPE_NOTIFICATION | RingtoneManager.TYPE_ALARM);
-        if (notificationUri == null) {
-            loge("Can't get URI for alert reminder sound");
-            return;
-        }
-        Ringtone r = RingtoneManager.getRingtone(this, notificationUri);
-        if (r != null) {
-            log("playing alert reminder sound");
-            r.play();
-        } else {
-            loge("can't get Ringtone for alert reminder sound");
-        }
-    }
-
     /**
      * Stops alert audio and speech.
      */
diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertReminder.java b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertReminder.java
index bbc6110..846c860 100644
--- a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertReminder.java
+++ b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertReminder.java
@@ -145,8 +145,9 @@
         // remind user after 2 minutes or 15 minutes
         long triggerTime = SystemClock.elapsedRealtime() + (interval * 60000);
         // We use setExact instead of set because this is for emergency reminder.
-        alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP, 
+        alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
                 triggerTime, sPlayReminderIntent);
+        log("Set reminder in " + interval + " minutes");
         return true;
     }
 
diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertService.java b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertService.java
index 176cb20..cdb4176 100644
--- a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertService.java
+++ b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertService.java
@@ -16,12 +16,12 @@
 
 package com.android.cellbroadcastreceiver;
 
+import android.app.ActivityManagerNative;
 import android.app.KeyguardManager;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.Service;
-import android.app.ActivityManagerNative;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
@@ -36,9 +36,11 @@
 import android.telephony.SmsCbEtwsInfo;
 import android.telephony.SmsCbLocation;
 import android.telephony.SmsCbMessage;
-import android.telephony.SubscriptionManager;
 import android.util.Log;
 
+import com.android.cellbroadcastreceiver.CellBroadcastAlertAudio.ToneType;
+import com.android.cellbroadcastreceiver.CellBroadcastOtherChannelsManager.CellBroadcastChannelRange;
+
 import java.util.ArrayList;
 import java.util.HashSet;
 import java.util.Locale;
@@ -50,7 +52,7 @@
  * (but not when the user views a previously received broadcast).
  */
 public class CellBroadcastAlertService extends Service {
-    private static final String TAG = "CellBroadcastAlertService";
+    private static final String TAG = "CBAlertService";
 
     /** Intent action to display alert dialog/notification, after verifying the alert is new. */
     static final String SHOW_NEW_ALERT_ACTION = "cellbroadcastreceiver.SHOW_NEW_ALERT";
@@ -365,15 +367,47 @@
         audioIntent.setAction(CellBroadcastAlertAudio.ACTION_START_ALERT_AUDIO);
         SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
 
+        ToneType toneType = ToneType.CMAS_DEFAULT;
         if (message.isEtwsMessage()) {
             // For ETWS, always vibrate, even in silent mode.
             audioIntent.putExtra(CellBroadcastAlertAudio.ALERT_AUDIO_VIBRATE_EXTRA, true);
             audioIntent.putExtra(CellBroadcastAlertAudio.ALERT_AUDIO_ETWS_VIBRATE_EXTRA, true);
+            toneType = ToneType.ETWS_DEFAULT;
+
+            if (message.getEtwsWarningInfo() != null) {
+                int warningType = message.getEtwsWarningInfo().getWarningType();
+
+                switch (warningType) {
+                    case SmsCbEtwsInfo.ETWS_WARNING_TYPE_EARTHQUAKE:
+                    case SmsCbEtwsInfo.ETWS_WARNING_TYPE_EARTHQUAKE_AND_TSUNAMI:
+                        toneType = ToneType.EARTHQUAKE;
+                        break;
+                    case SmsCbEtwsInfo.ETWS_WARNING_TYPE_TSUNAMI:
+                        toneType = ToneType.TSUNAMI;
+                        break;
+                    case SmsCbEtwsInfo.ETWS_WARNING_TYPE_OTHER_EMERGENCY:
+                        toneType = ToneType.OTHER;
+                        break;
+                }
+            }
         } else {
             // For other alerts, vibration can be disabled in app settings.
             audioIntent.putExtra(CellBroadcastAlertAudio.ALERT_AUDIO_VIBRATE_EXTRA,
                     prefs.getBoolean(CellBroadcastSettings.KEY_ENABLE_ALERT_VIBRATE, true));
+            int channel = message.getServiceCategory();
+            ArrayList<CellBroadcastChannelRange> ranges= CellBroadcastOtherChannelsManager.
+                    getInstance().getCellBroadcastChannelRanges(getApplicationContext(),
+                    message.getSubId());
+            if (ranges != null) {
+                for (CellBroadcastChannelRange range : ranges) {
+                    if (channel >= range.mStartId && channel <= range.mEndId) {
+                        toneType = range.mToneType;
+                        break;
+                    }
+                }
+            }
         }
+        audioIntent.putExtra(CellBroadcastAlertAudio.ALERT_AUDIO_TONE_TYPE, toneType);
 
         String messageBody = message.getMessageBody();
 
@@ -385,14 +419,15 @@
             if (message.isEtwsMessage()) {
                 // Only do TTS for ETWS secondary message.
                 // There is no text in ETWS primary message. When we construct the ETWS primary
-                // message, we hardcode "ETWS" as the body hence we don't want to speak that out here.
-                
+                // message, we hardcode "ETWS" as the body hence we don't want to speak that out
+                // here.
+
                 // Also in many cases we see the secondary message comes few milliseconds after
                 // the primary one. If we play TTS for the primary one, It will be overwritten by
                 // the secondary one immediately anyway.
                 if (!message.getEtwsWarningInfo().isPrimary()) {
-                    // Since only Japanese carriers are using ETWS, if there is no language specified
-                    // in the ETWS message, we'll use Japanese as the default language.
+                    // Since only Japanese carriers are using ETWS, if there is no language
+                    // specified in the ETWS message, we'll use Japanese as the default language.
                     defaultLanguage = "ja";
                 }
             } else {
diff --git a/tests/testapp/res/layout/test_buttons.xml b/tests/testapp/res/layout/test_buttons.xml
index 4486433..cc33221 100644
--- a/tests/testapp/res/layout/test_buttons.xml
+++ b/tests/testapp/res/layout/test_buttons.xml
@@ -66,13 +66,31 @@
         android:layout_height="wrap_content" />
 
     <!-- ETWS Alerts -->
-    <Button android:id="@+id/button_etws_normal_type"
-        android:text="@string/button_etws_normal_type"
+    <Button android:id="@+id/button_etws_earthquake_type"
+        android:text="@string/button_etws_earthquake_type"
         android:layout_marginLeft="20dp"
         android:layout_marginTop="10dp"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content" />
 
+    <Button android:id="@+id/button_etws_tsunami_type"
+        android:text="@string/button_etws_tsunami_type"
+        android:layout_marginLeft="20dp"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+    <Button android:id="@+id/button_etws_earthquake_tsunami_type"
+        android:text="@string/button_etws_earthquake_tsunami_type"
+        android:layout_marginLeft="20dp"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
+    <Button android:id="@+id/button_etws_other_type"
+        android:text="@string/button_etws_other_type"
+        android:layout_marginLeft="20dp"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content" />
+
     <Button android:id="@+id/button_etws_cancel_type"
         android:text="@string/button_etws_cancel_type"
         android:layout_marginLeft="20dp"
diff --git a/tests/testapp/res/values/strings.xml b/tests/testapp/res/values/strings.xml
index 08560ac..8b6ae05 100644
--- a/tests/testapp/res/values/strings.xml
+++ b/tests/testapp/res/values/strings.xml
@@ -16,7 +16,10 @@
 
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <string name="app_label">Cell Broadcast Tests</string>
-    <string name="button_etws_normal_type">Send ETWS Normal Broadcast</string>
+    <string name="button_etws_earthquake_type">Send ETWS Earthquake Broadcast</string>
+    <string name="button_etws_tsunami_type">Send ETWS Tsunami Broadcast</string>
+    <string name="button_etws_earthquake_tsunami_type">Send ETWS Earthquake and Tsunami Broadcast</string>
+    <string name="button_etws_other_type">Send ETWS Other Emergency Broadcast</string>
     <string name="button_etws_cancel_type">Send ETWS Cancel Broadcast</string>
     <string name="button_etws_test_type">Send ETWS Test Broadcast</string>
     <string name="button_cmas_pres_alert">Send CMAS Presidential Alert</string>
diff --git a/tests/testapp/src/com/android/cellbroadcastreceiver/tests/SendTestBroadcastActivity.java b/tests/testapp/src/com/android/cellbroadcastreceiver/tests/SendTestBroadcastActivity.java
index d84a695..d5f2f2e 100644
--- a/tests/testapp/src/com/android/cellbroadcastreceiver/tests/SendTestBroadcastActivity.java
+++ b/tests/testapp/src/com/android/cellbroadcastreceiver/tests/SendTestBroadcastActivity.java
@@ -20,7 +20,6 @@
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Message;
-import android.telephony.SubscriptionManager;
 import android.util.Log;
 import android.view.View;
 import android.view.View.OnClickListener;
@@ -100,20 +99,63 @@
         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() {
+        /* Send an ETWS earthquake broadcast message to app. */
+        Button etwsEarthquakeTypeButton = (Button) findViewById(R.id.button_etws_earthquake_type);
+        etwsEarthquakeTypeButton.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 {
-                    SendTestMessages.testSendEtwsMessageNormal(SendTestBroadcastActivity.this,
+                    SendTestMessages.testSendEtwsMessageEarthquake(SendTestBroadcastActivity.this,
                             getMessageId());
                 }
             }
         });
 
+        /* Send an ETWS tsunami broadcast message to app. */
+        Button etwsTsunamiTypeButton = (Button) findViewById(R.id.button_etws_tsunami_type);
+        etwsTsunamiTypeButton.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 {
+                    SendTestMessages.testSendEtwsMessageTsunami(SendTestBroadcastActivity.this,
+                            getMessageId());
+                }
+            }
+        });
+
+        /* Send an ETWS earthquake and tsunami broadcast message to app. */
+        Button etwsEarthquakeTsunamiTypeButton = (Button)
+                findViewById(R.id.button_etws_earthquake_tsunami_type);
+        etwsEarthquakeTsunamiTypeButton.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 {
+                    SendTestMessages.testSendEtwsMessageEarthquakeTsunami(
+                            SendTestBroadcastActivity.this, getMessageId());
+                }
+            }
+        });
+
+        /* Send an ETWS other emergency broadcast message to app. */
+        Button etwsOtherTypeButton = (Button) findViewById(R.id.button_etws_other_type);
+        etwsOtherTypeButton.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 {
+                    SendTestMessages.testSendEtwsMessageOther(
+                            SendTestBroadcastActivity.this, getMessageId());
+                }
+            }
+        });
+
         /* Send an ETWS cancel broadcast message to app. */
         Button etwsCancelTypeButton = (Button) findViewById(R.id.button_etws_cancel_type);
         etwsCancelTypeButton.setOnClickListener(new OnClickListener() {
diff --git a/tests/testapp/src/com/android/cellbroadcastreceiver/tests/SendTestMessages.java b/tests/testapp/src/com/android/cellbroadcastreceiver/tests/SendTestMessages.java
index 06dbf1d..9c32169 100644
--- a/tests/testapp/src/com/android/cellbroadcastreceiver/tests/SendTestMessages.java
+++ b/tests/testapp/src/com/android/cellbroadcastreceiver/tests/SendTestMessages.java
@@ -30,6 +30,7 @@
 import com.android.internal.telephony.EncodeException;
 import com.android.internal.telephony.GsmAlphabet;
 import com.android.internal.telephony.gsm.GsmSmsCbMessage;
+import com.android.internal.telephony.gsm.SmsCbConstants;
 import com.android.internal.telephony.uicc.IccUtils;
 
 import java.io.UnsupportedEncodingException;
@@ -545,9 +546,37 @@
                 AppOpsManager.OP_RECEIVE_SMS, null, null, Activity.RESULT_OK, null, null);
     }
 
-    public static void testSendEtwsMessageNormal(Activity activity, int serialNumber) {
+    public static void testSendEtwsMessageEarthquake(Activity activity, int serialNumber) {
         Intent intent = new Intent(Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION);
-        intent.putExtra("message", createFromPdu(etwsMessageNormal, serialNumber, 0));
+        intent.putExtra("message", createFromPdu(etwsMessageNormal, serialNumber,
+                SmsCbConstants.MESSAGE_ID_ETWS_EARTHQUAKE_WARNING));
+        activity.sendOrderedBroadcastAsUser(intent, UserHandle.ALL,
+                Manifest.permission.RECEIVE_EMERGENCY_BROADCAST,
+                AppOpsManager.OP_RECEIVE_EMERGECY_SMS, null, null, Activity.RESULT_OK, null, null);
+    }
+
+    public static void testSendEtwsMessageTsunami(Activity activity, int serialNumber) {
+        Intent intent = new Intent(Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION);
+        intent.putExtra("message", createFromPdu(etwsMessageNormal, serialNumber,
+                SmsCbConstants.MESSAGE_ID_ETWS_TSUNAMI_WARNING));
+        activity.sendOrderedBroadcastAsUser(intent, UserHandle.ALL,
+                Manifest.permission.RECEIVE_EMERGENCY_BROADCAST,
+                AppOpsManager.OP_RECEIVE_EMERGECY_SMS, null, null, Activity.RESULT_OK, null, null);
+    }
+
+    public static void testSendEtwsMessageEarthquakeTsunami(Activity activity, int serialNumber) {
+        Intent intent = new Intent(Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION);
+        intent.putExtra("message", createFromPdu(etwsMessageNormal, serialNumber,
+                SmsCbConstants.MESSAGE_ID_ETWS_EARTHQUAKE_AND_TSUNAMI_WARNING));
+        activity.sendOrderedBroadcastAsUser(intent, UserHandle.ALL,
+                Manifest.permission.RECEIVE_EMERGENCY_BROADCAST,
+                AppOpsManager.OP_RECEIVE_EMERGECY_SMS, null, null, Activity.RESULT_OK, null, null);
+    }
+
+    public static void testSendEtwsMessageOther(Activity activity, int serialNumber) {
+        Intent intent = new Intent(Intents.SMS_EMERGENCY_CB_RECEIVED_ACTION);
+        intent.putExtra("message", createFromPdu(etwsMessageNormal, serialNumber,
+                SmsCbConstants.MESSAGE_ID_ETWS_OTHER_EMERGENCY_TYPE));
         activity.sendOrderedBroadcastAsUser(intent, UserHandle.ALL,
                 Manifest.permission.RECEIVE_EMERGENCY_BROADCAST,
                 AppOpsManager.OP_RECEIVE_EMERGECY_SMS, null, null, Activity.RESULT_OK, null, null);