Merge "Taiwan NCC requirements part 3"
diff --git a/res/values-mcc466/config.xml b/res/values-mcc466/config.xml
index 9cd747d..dceecd2 100644
--- a/res/values-mcc466/config.xml
+++ b/res/values-mcc466/config.xml
@@ -21,8 +21,8 @@
 
     <string-array name="safety_info_alerts_channels_range_strings" translatable="false">
         <!-- Channel 911 and 919 are required by Taiwan NCC -->
-        <item>0x038F:type=other, emergency=false</item>
-        <item>0x0397:type=other, emergency=false</item>
+        <item>0x038F:type=info, emergency=true, vibration=0|350|250|350</item>
+        <item>0x0397:type=info, emergency=true, vibration=0|350|250|350</item>
     </string-array>
     <!-- 4370, 4383 -->
     <string-array name="cmas_presidential_alerts_channels_range_strings" translatable="false">
diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertAudio.java b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertAudio.java
index 97e7563..c6f7f1e 100644
--- a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertAudio.java
+++ b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertAudio.java
@@ -37,6 +37,7 @@
 import android.os.Message;
 import android.os.Vibrator;
 import android.preference.PreferenceManager;
+import android.provider.Settings;
 import android.speech.tts.TextToSpeech;
 import android.telephony.PhoneStateListener;
 import android.telephony.TelephonyManager;
@@ -79,6 +80,10 @@
     public static final String ALERT_AUDIO_VIBRATE_EXTRA =
             "com.android.cellbroadcastreceiver.ALERT_AUDIO_VIBRATE";
 
+    /** Extra for alert vibration pattern (unless master volume is silent). */
+    public static final String ALERT_AUDIO_VIBRATION_PATTERN_EXTRA =
+            "com.android.cellbroadcastreceiver.ALERT_VIBRATION_PATTERN";
+
     private static final String TTS_UTTERANCE_ID = "com.android.cellbroadcastreceiver.UTTERANCE_ID";
 
     /** Pause duration between alert sound and alert speech. */
@@ -103,6 +108,7 @@
     private boolean mUseFullVolume;
     private boolean mResetAlarmVolumeNeeded;
     private int mUserSetAlarmVolume;
+    private int[] mVibrationPattern;
 
     private Vibrator mVibrator;
     private MediaPlayer mMediaPlayer;
@@ -305,6 +311,9 @@
 
         // retrieve the vibrate settings from cellbroadcast receiver settings.
         mEnableVibrate = intent.getBooleanExtra(ALERT_AUDIO_VIBRATE_EXTRA, true);
+        // retrieve the vibration patterns
+        mVibrationPattern = intent.getIntArrayExtra(ALERT_AUDIO_VIBRATION_PATTERN_EXTRA);
+
         switch (mAudioManager.getRingerMode()) {
             case AudioManager.RINGER_MODE_SILENT:
                 if (DBG) log("Ringer mode: silent");
@@ -339,7 +348,7 @@
             if (intent.getSerializableExtra(ALERT_AUDIO_TONE_TYPE) != null) {
                 alertType = (AlertType) intent.getSerializableExtra(ALERT_AUDIO_TONE_TYPE);
             }
-            playAlertTone(alertType);
+            playAlertTone(alertType, mVibrationPattern);
         } else {
             stopSelf();
             return START_NOT_STICKY;
@@ -358,8 +367,9 @@
     /**
      * Start playing the alert sound.
      * @param alertType the alert type (e.g. default, earthquake, tsunami, etc..)
+     * @param patternArray the alert vibration pattern
      */
-    private void playAlertTone(AlertType alertType) {
+    private void playAlertTone(AlertType alertType, int[] patternArray) {
         // stop() checks to see if we are already playing.
         stop();
 
@@ -372,9 +382,6 @@
 
         // Start the vibration first.
         if (mEnableVibrate) {
-
-            int[] patternArray = getApplicationContext().getResources().
-                    getIntArray(R.array.default_vibration_pattern);
             long[] vibrationPattern = new long[patternArray.length];
 
             for (int i = 0; i < patternArray.length; i++) {
@@ -433,6 +440,13 @@
                         setDataSourceFromResource(getResources(), mMediaPlayer,
                                 R.raw.etws_default);
                         break;
+                    case INFO:
+                        // for non-emergency alerts, we are using system default notification sound.
+                        String sound = Settings.System.getString(
+                                getApplicationContext().getContentResolver(),
+                                Settings.System.NOTIFICATION_SOUND);
+                        mMediaPlayer.setDataSource(sound);
+                        break;
                     case CMAS_DEFAULT:
                     default:
                         setDataSourceFromResource(getResources(), mMediaPlayer,
diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertService.java b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertService.java
index c6c35b1..2a0e369 100644
--- a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertService.java
+++ b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertService.java
@@ -106,6 +106,7 @@
         ETWS_TSUNAMI,
         TEST,
         AREA,
+        INFO,
         OTHER
     }
 
@@ -536,7 +537,10 @@
             int channel = message.getServiceCategory();
             ArrayList<CellBroadcastChannelRange> ranges = CellBroadcastChannelManager
                     .getInstance().getCellBroadcastChannelRanges(getApplicationContext(),
-                    R.array.additional_cbs_channels_strings);
+                            R.array.additional_cbs_channels_strings);
+            ranges.addAll(CellBroadcastChannelManager
+                    .getInstance().getCellBroadcastChannelRanges(getApplicationContext(),
+                            R.array.safety_info_alerts_channels_range_strings));
             if (ranges != null) {
                 for (CellBroadcastChannelRange range : ranges) {
                     if (channel >= range.mStartId && channel <= range.mEndId) {
@@ -546,9 +550,13 @@
                 }
             }
         }
+        CellBroadcastChannelRange range = CellBroadcastChannelManager
+                .getCellBroadcastChannelRangeFromMessage(getApplicationContext(), message);
         audioIntent.putExtra(CellBroadcastAlertAudio.ALERT_AUDIO_TONE_TYPE, alertType);
         audioIntent.putExtra(CellBroadcastAlertAudio.ALERT_AUDIO_VIBRATE_EXTRA,
                 prefs.getBoolean(CellBroadcastSettings.KEY_ENABLE_ALERT_VIBRATE, true));
+        audioIntent.putExtra(CellBroadcastAlertAudio.ALERT_AUDIO_VIBRATION_PATTERN_EXTRA,
+                (range != null) ? range.mVibrationPattern : null);
 
         String messageBody = message.getMessageBody();
 
diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastChannelManager.java b/src/com/android/cellbroadcastreceiver/CellBroadcastChannelManager.java
index 73aaf67..a2cd72c 100644
--- a/src/com/android/cellbroadcastreceiver/CellBroadcastChannelManager.java
+++ b/src/com/android/cellbroadcastreceiver/CellBroadcastChannelManager.java
@@ -17,14 +17,18 @@
 package com.android.cellbroadcastreceiver;
 
 import android.content.Context;
+import android.telephony.CellBroadcastMessage;
 import android.telephony.ServiceState;
 import android.telephony.SmsManager;
 import android.telephony.TelephonyManager;
 import android.util.Log;
 
 import com.android.cellbroadcastreceiver.CellBroadcastAlertService.AlertType;
+import com.android.internal.util.ArrayUtils;
 
 import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
 
 /**
  * CellBroadcastChannelManager handles the additional cell broadcast channels that
@@ -46,6 +50,20 @@
 
     private static CellBroadcastChannelManager sInstance = null;
 
+    private static List<Integer> sCellBroadcastRangeResourceKeys = new ArrayList<>(
+            Arrays.asList(R.array.additional_cbs_channels_strings,
+                    R.array.emergency_alerts_channels_range_strings,
+                    R.array.cmas_presidential_alerts_channels_range_strings,
+                    R.array.cmas_alert_extreme_channels_range_strings,
+                    R.array.cmas_alerts_severe_range_strings,
+                    R.array.cmas_amber_alerts_channels_range_strings,
+                    R.array.required_monthly_test_range_strings,
+                    R.array.exercise_alert_range_strings,
+                    R.array.operator_defined_alert_range_strings,
+                    R.array.etws_alerts_range_strings,
+                    R.array.etws_test_alerts_range_strings,
+                    R.array.safety_info_alerts_channels_range_strings
+            ));
     /**
      * Cell broadcast channel range
      * A range is consisted by starting channel id, ending channel id, and the alert type
@@ -56,6 +74,7 @@
         private static final String KEY_EMERGENCY = "emergency";
         private static final String KEY_RAT = "rat";
         private static final String KEY_SCOPE = "scope";
+        private static final String KEY_VIBRATION = "vibration";
 
         public static final int SCOPE_UNKNOWN       = 0;
         public static final int SCOPE_CARRIER       = 1;
@@ -68,13 +87,16 @@
         public boolean mIsEmergency;
         public int mRat;
         public int mScope;
+        public int[] mVibrationPattern;
 
-        public CellBroadcastChannelRange(String channelRange) throws Exception {
+        public CellBroadcastChannelRange(Context context, String channelRange) throws Exception {
 
             mAlertType = AlertType.CMAS_DEFAULT;
             mIsEmergency = false;
             mRat = SmsManager.CELL_BROADCAST_RAN_TYPE_GSM;
             mScope = SCOPE_UNKNOWN;
+            mVibrationPattern = context.getResources().getIntArray(
+                    R.array.default_vibration_pattern);
 
             int colonIndex = channelRange.indexOf(':');
             if (colonIndex != -1) {
@@ -108,6 +130,15 @@
                                     mScope = SCOPE_INTERNATIONAL;
                                 }
                                 break;
+                            case KEY_VIBRATION:
+                                String[] vibration = value.split("\\|");
+                                if (!ArrayUtils.isEmpty(vibration)) {
+                                    mVibrationPattern = new int[vibration.length];
+                                    for (int i = 0; i < vibration.length; i++) {
+                                        mVibrationPattern[i] = Integer.parseInt(vibration[i]);
+                                    }
+                                }
+                                break;
                         }
                     }
                 }
@@ -144,7 +175,7 @@
      * @param key Resource key
      * @return The list of channel ranges enabled by the carriers.
      */
-    public ArrayList<CellBroadcastChannelRange> getCellBroadcastChannelRanges(
+    public static ArrayList<CellBroadcastChannelRange> getCellBroadcastChannelRanges(
             Context context, int key) {
         ArrayList<CellBroadcastChannelRange> result = new ArrayList<>();
         String[] ranges = context.getResources().getStringArray(key);
@@ -152,7 +183,7 @@
         if (ranges != null) {
             for (String range : ranges) {
                 try {
-                    result.add(new CellBroadcastChannelRange(range));
+                    result.add(new CellBroadcastChannelRange(context, range));
                 } catch (Exception e) {
                     loge("Failed to parse \"" + range + "\". e=" + e);
                 }
@@ -217,6 +248,34 @@
         return true;
     }
 
+    /**
+     * Return corresponding cellbroadcast range where message belong to
+     * @param context
+     * @param message
+     */
+    public static CellBroadcastChannelRange getCellBroadcastChannelRangeFromMessage(
+            Context context, CellBroadcastMessage message) {
+        int subId = message.getSubId();
+        int channel = message.getServiceCategory();
+        ArrayList<CellBroadcastChannelRange> ranges = null;
+
+        for (int key : sCellBroadcastRangeResourceKeys) {
+            if (checkCellBroadcastChannelRange(subId, channel, key, context)) {
+                ranges = getCellBroadcastChannelRanges(context, key);
+                break;
+            }
+        }
+        if (ranges != null) {
+            for (CellBroadcastChannelRange range : ranges) {
+                if (range.mStartId <= message.getServiceCategory()
+                        && range.mEndId >= message.getServiceCategory()) {
+                    return range;
+                }
+            }
+        }
+        return null;
+    }
+
     private static void log(String msg) {
         Log.d(TAG, msg);
     }
diff --git a/tests/unit/src/com/android/cellbroadcastreceiver/CellBroadcastConfigServiceTest.java b/tests/unit/src/com/android/cellbroadcastreceiver/CellBroadcastConfigServiceTest.java
index 076451c..186bfeb 100644
--- a/tests/unit/src/com/android/cellbroadcastreceiver/CellBroadcastConfigServiceTest.java
+++ b/tests/unit/src/com/android/cellbroadcastreceiver/CellBroadcastConfigServiceTest.java
@@ -148,7 +148,7 @@
     @SmallTest
     public void testEnableCellBroadcastRange() throws Exception {
         ArrayList<CellBroadcastChannelRange> result = new ArrayList<>();
-        result.add(new CellBroadcastChannelRange("10-20"));
+        result.add(new CellBroadcastChannelRange(mContext, "10-20"));
         setCellBroadcastRange(true, result);
         ArgumentCaptor<Integer> captorStart = ArgumentCaptor.forClass(Integer.class);
         ArgumentCaptor<Integer> captorEnd = ArgumentCaptor.forClass(Integer.class);
@@ -169,7 +169,7 @@
     @SmallTest
     public void testDisableCellBroadcastRange() throws Exception {
         ArrayList<CellBroadcastChannelRange> result = new ArrayList<>();
-        result.add(new CellBroadcastChannelRange("10-20"));
+        result.add(new CellBroadcastChannelRange(mContext, "10-20"));
         setCellBroadcastRange(false, result);
         ArgumentCaptor<Integer> captorStart = ArgumentCaptor.forClass(Integer.class);
         ArgumentCaptor<Integer> captorEnd = ArgumentCaptor.forClass(Integer.class);