1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.cellbroadcastreceiver;
18 
19 import static android.telephony.PhoneStateListener.LISTEN_NONE;
20 
21 import static com.android.cellbroadcastreceiver.CellBroadcastReceiver.DBG;
22 import static com.android.cellbroadcastservice.CellBroadcastMetrics.ERRSRC_CBR;
23 import static com.android.cellbroadcastservice.CellBroadcastMetrics.ERRTYPE_PLAYFLASH;
24 import static com.android.cellbroadcastservice.CellBroadcastMetrics.ERRTYPE_PLAYSOUND;
25 import static com.android.cellbroadcastservice.CellBroadcastMetrics.ERRTYPE_PLAYTTS;
26 
27 import android.app.AlarmManager;
28 import android.app.PendingIntent;
29 import android.app.Service;
30 import android.content.BroadcastReceiver;
31 import android.content.Context;
32 import android.content.Intent;
33 import android.content.IntentFilter;
34 import android.content.SharedPreferences;
35 import android.content.pm.PackageManager;
36 import android.content.res.AssetFileDescriptor;
37 import android.content.res.Resources;
38 import android.hardware.camera2.CameraAccessException;
39 import android.hardware.camera2.CameraCharacteristics;
40 import android.hardware.camera2.CameraManager;
41 import android.media.AudioAttributes;
42 import android.media.AudioDeviceInfo;
43 import android.media.AudioManager;
44 import android.media.MediaPlayer;
45 import android.media.MediaPlayer.OnCompletionListener;
46 import android.media.MediaPlayer.OnErrorListener;
47 import android.os.Handler;
48 import android.os.IBinder;
49 import android.os.Looper;
50 import android.os.Message;
51 import android.os.SystemClock;
52 import android.os.VibrationEffect;
53 import android.os.Vibrator;
54 import android.preference.PreferenceManager;
55 import android.provider.Settings;
56 import android.speech.tts.TextToSpeech;
57 import android.telephony.PhoneStateListener;
58 import android.telephony.SubscriptionManager;
59 import android.telephony.TelephonyManager;
60 import android.text.TextUtils;
61 import android.util.Log;
62 
63 import com.android.cellbroadcastreceiver.CellBroadcastAlertService.AlertType;
64 import com.android.internal.annotations.VisibleForTesting;
65 
66 import java.util.Locale;
67 
68 /**
69  * Manages alert audio and vibration and text-to-speech. Runs as a service so that
70  * it can continue to play if another activity overrides the CellBroadcastListActivity.
71  */
72 public class CellBroadcastAlertAudio extends Service implements TextToSpeech.OnInitListener,
73         TextToSpeech.OnUtteranceCompletedListener, AudioManager.OnAudioFocusChangeListener {
74     private static final String TAG = "CellBroadcastAlertAudio";
75 
76     /** Action to start playing alert audio/vibration/speech. */
77     @VisibleForTesting
78     public static final String ACTION_START_ALERT_AUDIO = "ACTION_START_ALERT_AUDIO";
79 
80     /** Extra for message body to speak (if speech enabled in settings). */
81     public static final String ALERT_AUDIO_MESSAGE_BODY =
82             "com.android.cellbroadcastreceiver.ALERT_AUDIO_MESSAGE_BODY";
83 
84     /** Extra for text-to-speech preferred language (if speech enabled in settings). */
85     public static final String ALERT_AUDIO_MESSAGE_LANGUAGE =
86             "com.android.cellbroadcastreceiver.ALERT_AUDIO_MESSAGE_LANGUAGE";
87 
88     /** Extra for alert tone type */
89     public static final String ALERT_AUDIO_TONE_TYPE =
90             "com.android.cellbroadcastreceiver.ALERT_AUDIO_TONE_TYPE";
91 
92     /** Extra for alert vibration pattern (unless main volume is silent). */
93     public static final String ALERT_AUDIO_VIBRATION_PATTERN_EXTRA =
94             "com.android.cellbroadcastreceiver.ALERT_AUDIO_VIBRATION_PATTERN";
95 
96     /** Extra for playing alert sound in full volume regardless Do Not Disturb is on. */
97     public static final String ALERT_AUDIO_OVERRIDE_DND_EXTRA =
98             "com.android.cellbroadcastreceiver.ALERT_OVERRIDE_DND_EXTRA";
99 
100     /** Extra for cutomized alert duration in ms. */
101     public static final String ALERT_AUDIO_DURATION =
102             "com.android.cellbroadcastreceiver.ALERT_AUDIO_DURATION";
103 
104     /** Extra for alert subscription index */
105     public static final String ALERT_AUDIO_SUB_INDEX =
106             "com.android.cellbroadcastreceiver.ALERT_AUDIO_SUB_INDEX";
107 
108     private static final String TTS_UTTERANCE_ID = "com.android.cellbroadcastreceiver.UTTERANCE_ID";
109 
110     /** Pause duration between alert sound and alert speech. */
111     private static final long PAUSE_DURATION_BEFORE_SPEAKING_MSEC = 1000L;
112 
113     private static final int STATE_IDLE = 0;
114     private static final int STATE_ALERTING = 1;
115     private static final int STATE_PAUSING = 2;
116     private static final int STATE_SPEAKING = 3;
117     private static final int STATE_STOPPING = 4;
118 
119     /** Default LED flashing frequency is 250 milliseconds */
120     private static final long DEFAULT_LED_FLASH_INTERVAL_MSEC = 250L;
121 
122     /** Default delay for resent alert audio intent */
123     private static final long DEFAULT_RESENT_DELAY_MSEC = 200L;
124 
125     private int mState;
126 
127     @VisibleForTesting
128     public TextToSpeech mTts;
129     private boolean mTtsEngineReady;
130 
131     private AlertType mAlertType;
132     private String mMessageBody;
133     private String mMessageLanguage;
134     private int mSubId;
135     private boolean mTtsLanguageSupported;
136     private boolean mEnableVibrate;
137     private boolean mEnableAudio;
138     private boolean mEnableLedFlash;
139     private boolean mIsMediaPlayerStarted;
140     private boolean mIsTextToSpeechSpeaking;
141     private boolean mOverrideDnd;
142     private boolean mResetAlarmVolumeNeeded;
143     private int mUserSetAlarmVolume;
144     private int[] mVibrationPattern;
145     private int mAlertDuration = -1;
146 
147     private Vibrator mVibrator;
148     private MediaPlayer mMediaPlayer;
149     @VisibleForTesting
150     public MediaPlayer mMediaPlayerInjected;
151     private AudioManager mAudioManager;
152     private TelephonyManager mTelephonyManager;
153     private int mInitialCallState;
154     private int mStartId;
155     private ScreenOffReceiver mScreenOffReceiver;
156 
157     // Internal messages
158     private static final int ALERT_SOUND_FINISHED = 1000;
159     private static final int ALERT_PAUSE_FINISHED = 1001;
160     private static final int ALERT_LED_FLASH_TOGGLE = 1002;
161 
162     @VisibleForTesting
163     public Handler mHandler;
164 
165     private PhoneStateListener mPhoneStateListener;
166 
167     /**
168      * Callback from TTS engine after initialization.
169      *
170      * @param status {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
171      */
172     @Override
onInit(int status)173     public void onInit(int status) {
174         if (DBG) log("onInit() TTS engine status: " + status);
175         if (status == TextToSpeech.SUCCESS) {
176             mTtsEngineReady = true;
177             mTts.setOnUtteranceCompletedListener(this);
178             // try to set the TTS language to match the broadcast
179             setTtsLanguage();
180         } else {
181             mTtsEngineReady = false;
182             mTts = null;
183             loge("onInit() TTS engine error: " + status);
184         }
185     }
186 
187     /**
188      * Try to set the TTS engine language to the preferred language. If failed, set
189      * it to the default language. mTtsLanguageSupported will be updated based on the response.
190      */
setTtsLanguage()191     private void setTtsLanguage() {
192         Locale locale = null;
193         if (!TextUtils.isEmpty(mMessageLanguage)) {
194             locale = new Locale(mMessageLanguage);
195         }
196         if (locale == null || locale.getLanguage().equalsIgnoreCase(
197                 Locale.getDefault().getLanguage())) {
198             // If the cell broadcast message does not specify the language, use device's default
199             // language.
200             locale = Locale.getDefault();
201         }
202 
203         if (DBG) log("Setting TTS language to '" + locale + '\'');
204         int result = mTts.setLanguage(locale);
205         if (DBG) log("TTS setLanguage() returned: " + result);
206         mTtsLanguageSupported = (result >= TextToSpeech.LANG_AVAILABLE);
207     }
208 
209     /**
210      * Callback from TTS engine.
211      *
212      * @param utteranceId the identifier of the utterance.
213      */
214     @Override
onUtteranceCompleted(String utteranceId)215     public void onUtteranceCompleted(String utteranceId) {
216         if (utteranceId.equals(TTS_UTTERANCE_ID)) {
217             // When we reach here, it could be TTS completed or TTS was cut due to another
218             // new alert started playing. We don't want to stop the service in the later case.
219             if (getState() == STATE_SPEAKING) {
220                 if (DBG) log("TTS completed. Stop CellBroadcastAlertAudio service");
221                 stopAlertAudioService();
222             }
223         }
224     }
225 
226     @Override
onCreate()227     public void onCreate() {
228         mVibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
229         mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
230         // Listen for incoming calls to kill the alarm.
231         mTelephonyManager = ((TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE));
232         mHandler = new Handler(Looper.getMainLooper()) {
233             @Override
234             public void handleMessage(Message msg) {
235                 switch (msg.what) {
236                     case ALERT_SOUND_FINISHED:
237                         if (DBG) log("ALERT_SOUND_FINISHED");
238                         stop();     // stop alert sound
239                         // if we can speak the message text
240                         if (mMessageBody != null && mTtsEngineReady && mTtsLanguageSupported) {
241                             sendMessageDelayed(mHandler.obtainMessage(ALERT_PAUSE_FINISHED),
242                                     PAUSE_DURATION_BEFORE_SPEAKING_MSEC);
243                             setState(STATE_PAUSING);
244                         } else {
245                             if (DBG) {
246                                 log("MessageEmpty = " + (mMessageBody == null)
247                                         + ", mTtsEngineReady = " + mTtsEngineReady
248                                         + ", mTtsLanguageSupported = " + mTtsLanguageSupported);
249                             }
250                             stopAlertAudioService();
251                         }
252                         // Set alert reminder depending on user preference
253                         CellBroadcastAlertReminder.queueAlertReminder(getApplicationContext(),
254                                 mSubId,
255                                 true);
256                         break;
257 
258                     case ALERT_PAUSE_FINISHED:
259                         if (DBG) log("ALERT_PAUSE_FINISHED");
260                         int res = TextToSpeech.ERROR;
261                         if (mMessageBody != null && mTtsEngineReady && mTtsLanguageSupported) {
262                             if (DBG) log("Speaking broadcast text: " + mMessageBody);
263 
264                             mTts.setAudioAttributes(getAlertAudioAttributes());
265                             // Flush the text to speech queue
266                             mTts.speak("", TextToSpeech.QUEUE_FLUSH, null, null);
267                             res = mTts.speak(mMessageBody, 2, null, TTS_UTTERANCE_ID);
268                             mIsTextToSpeechSpeaking = true;
269                             setState(STATE_SPEAKING);
270                         }
271                         if (res != TextToSpeech.SUCCESS) {
272                             loge("TTS engine not ready or language not supported or speak() "
273                                     + "failed");
274                             stopAlertAudioService();
275                         }
276                         break;
277 
278                     case ALERT_LED_FLASH_TOGGLE:
279                         if (enableLedFlash(msg.arg1 != 0)) {
280                             sendMessageDelayed(mHandler.obtainMessage(
281                                     ALERT_LED_FLASH_TOGGLE, msg.arg1 != 0 ? 0 : 1, 0),
282                                     DEFAULT_LED_FLASH_INTERVAL_MSEC);
283                         }
284                         break;
285 
286                     default:
287                         loge("Handler received unknown message, what=" + msg.what);
288                 }
289             }
290         };
291         mPhoneStateListener = new PhoneStateListener() {
292             @Override
293             public void onCallStateChanged(int state, String ignored) {
294                 // Stop the alert sound and speech if the call state changes.
295                 if (state != TelephonyManager.CALL_STATE_IDLE
296                         && state != mInitialCallState) {
297                     if (DBG) log("Call interrupted. Stop CellBroadcastAlertAudio service");
298                     stopAlertAudioService();
299                 }
300             }
301         };
302         mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
303     }
304 
305     @Override
onDestroy()306     public void onDestroy() {
307         setState(STATE_STOPPING);
308         // stop audio, vibration and TTS
309         if (DBG) log("onDestroy");
310         stop();
311         // Stop listening for incoming calls.
312         mTelephonyManager.listen(mPhoneStateListener, LISTEN_NONE);
313         // shutdown TTS engine
314         if (mTts != null) {
315             try {
316                 mTts.shutdown();
317             } catch (IllegalStateException e) {
318                 // catch "Unable to retrieve AudioTrack pointer for stop()" exception
319                 loge("exception trying to shutdown text-to-speech");
320             }
321         }
322         if (mEnableAudio) {
323             // Release the audio focus so other audio (e.g. music) can resume.
324             // Do not do this in stop() because stop() is also called when we stop the tone (before
325             // TTS is playing). We only want to release the focus when tone and TTS are played.
326             mAudioManager.abandonAudioFocus(this);
327         }
328     }
329 
330     @Override
onBind(Intent intent)331     public IBinder onBind(Intent intent) {
332         return null;
333     }
334 
335     @Override
onStartCommand(Intent intent, int flags, int startId)336     public int onStartCommand(Intent intent, int flags, int startId) {
337         if (DBG) log("onStartCommand");
338         // No intent, tell the system not to restart us.
339         if (intent == null) {
340             if (DBG) log("Null intent. Stop CellBroadcastAlertAudio service");
341             stopAlertAudioService();
342             return START_NOT_STICKY;
343         }
344 
345         // Check if service stop is in progress
346         if (getState() == STATE_STOPPING) {
347             if (DBG) log("stop is in progress");
348             PendingIntent pi;
349             pi = PendingIntent.getService(this, 1 /*REQUEST_CODE_CONTENT_INTENT*/, intent,
350                     PendingIntent.FLAG_ONE_SHOT
351                             | PendingIntent.FLAG_UPDATE_CURRENT
352                             | PendingIntent.FLAG_IMMUTABLE);
353             AlarmManager alarmManager = getSystemService(AlarmManager.class);
354             if (alarmManager == null) {
355                 loge("can't get Alarm Service");
356                 return START_NOT_STICKY;
357             }
358             if (DBG) log("resent intent");
359             // resent again
360             long triggerTime = SystemClock.elapsedRealtime() + DEFAULT_RESENT_DELAY_MSEC;
361             alarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
362                     triggerTime, pi);
363             return START_STICKY;
364         }
365 
366         mStartId = startId;
367         return handleStartIntent(intent);
368     }
369 
370     /**
371      * Handle the start intent
372      *
373      * @param intent    the intent to start the service
374      */
375     @VisibleForTesting
handleStartIntent(Intent intent)376     public int handleStartIntent(Intent intent) {
377         // Get text to speak (if enabled by user)
378         mMessageBody = intent.getStringExtra(ALERT_AUDIO_MESSAGE_BODY);
379         mMessageLanguage = intent.getStringExtra(ALERT_AUDIO_MESSAGE_LANGUAGE);
380         mSubId = intent.getIntExtra(ALERT_AUDIO_SUB_INDEX,
381                 SubscriptionManager.INVALID_SUBSCRIPTION_ID);
382 
383         SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
384 
385         // retrieve whether to play alert sound in full volume regardless Do Not Disturb is on.
386         mOverrideDnd = intent.getBooleanExtra(ALERT_AUDIO_OVERRIDE_DND_EXTRA, false);
387         // retrieve the vibrate settings from cellbroadcast receiver settings.
388         mEnableVibrate = prefs.getBoolean(CellBroadcastSettings.KEY_ENABLE_ALERT_VIBRATE, true)
389                 || mOverrideDnd;
390         // retrieve the vibration patterns.
391         mVibrationPattern = intent.getIntArrayExtra(ALERT_AUDIO_VIBRATION_PATTERN_EXTRA);
392 
393         Resources res = CellBroadcastSettings.getResourcesByOperator(getApplicationContext(),
394                 mSubId, CellBroadcastReceiver.getRoamingOperatorSupported(getApplicationContext()));
395         mEnableLedFlash = res.getBoolean(R.bool.enable_led_flash);
396 
397         // retrieve the customized alert duration. -1 means play the alert with the tone's duration.
398         mAlertDuration = intent.getIntExtra(ALERT_AUDIO_DURATION, -1);
399         // retrieve the alert type
400         mAlertType = AlertType.DEFAULT;
401         if (intent.getSerializableExtra(ALERT_AUDIO_TONE_TYPE) != null) {
402             mAlertType = (AlertType) intent.getSerializableExtra(ALERT_AUDIO_TONE_TYPE);
403         }
404 
405         switch (mAudioManager.getRingerMode()) {
406             case AudioManager.RINGER_MODE_SILENT:
407                 if (DBG) log("Ringer mode: silent");
408                 if (!mOverrideDnd) {
409                     mEnableVibrate = false;
410                 }
411                 // If the phone is in silent mode, we only enable the audio when override dnd
412                 // setting is turned on.
413                 mEnableAudio = mOverrideDnd;
414                 break;
415             case AudioManager.RINGER_MODE_VIBRATE:
416                 if (DBG) log("Ringer mode: vibrate");
417                 // If the phone is in vibration mode, we only enable the audio when override dnd
418                 // setting is turned on.
419                 mEnableAudio = mOverrideDnd;
420                 break;
421             case AudioManager.RINGER_MODE_NORMAL:
422             default:
423                 if (DBG) log("Ringer mode: normal");
424                 mEnableAudio = true;
425                 break;
426         }
427 
428         if (mMessageBody != null && mEnableAudio) {
429             if (mTts == null) {
430                 mTts = new TextToSpeech(this, this);
431             } else if (mTtsEngineReady) {
432                 setTtsLanguage();
433             }
434         }
435 
436         if ((mEnableAudio || mEnableVibrate) && (mAlertType != AlertType.MUTE)) {
437             playAlertTone(mAlertType, mVibrationPattern);
438         } else {
439             if (DBG) log("No audio/vibrate playing. Stop CellBroadcastAlertAudio service");
440             stopAlertAudioService();
441             return START_NOT_STICKY;
442         }
443 
444         // Record the initial call state here so that the new alarm has the
445         // newest state.
446         mInitialCallState = mTelephonyManager.getCallState();
447 
448         return START_STICKY;
449     }
450 
451     // Volume suggested by media team for in-call alarms.
452     private static final float IN_CALL_VOLUME_LEFT = 0.125f;
453     private static final float IN_CALL_VOLUME_RIGHT = 0.125f;
454 
455     /**
456      * Start playing the alert sound.
457      *
458      * @param alertType    the alert type (e.g. default, earthquake, tsunami, etc..)
459      * @param patternArray the alert vibration pattern
460      */
playAlertTone(AlertType alertType, int[] patternArray)461     private void playAlertTone(AlertType alertType, int[] patternArray) {
462         // stop() checks to see if we are already playing.
463         stop();
464 
465         log("playAlertTone: alertType=" + alertType + ", mEnableVibrate=" + mEnableVibrate
466                 + ", mEnableAudio=" + mEnableAudio + ", mOverrideDnd=" + mOverrideDnd
467                 + ", mSubId=" + mSubId);
468         Resources res = CellBroadcastSettings.getResourcesByOperator(getApplicationContext(),
469                 mSubId, CellBroadcastReceiver.getRoamingOperatorSupported(getApplicationContext()));
470 
471         // Vibration duration in milliseconds
472         long vibrateDuration = 0;
473 
474         // Get the alert tone duration. Negative tone duration value means we only play the tone
475         // once, not repeat it.
476         int customAlertDuration = mAlertDuration;
477 
478         // Start the vibration first.
479         if (mEnableVibrate) {
480             long[] vibrationPattern = new long[patternArray.length];
481 
482             for (int i = 0; i < patternArray.length; i++) {
483                 vibrationPattern[i] = patternArray[i];
484                 vibrateDuration += patternArray[i];
485             }
486 
487             AudioAttributes.Builder attrBuilder = new AudioAttributes.Builder();
488             attrBuilder.setUsage(AudioAttributes.USAGE_ALARM);
489             if (mOverrideDnd) {
490                 // Set the flags to bypass DnD mode if override dnd is turned on.
491                 attrBuilder.setFlags(AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY
492                         | AudioAttributes.FLAG_BYPASS_MUTE);
493             }
494             AudioAttributes attr = attrBuilder.build();
495             // If we only play the tone once, then we also play the vibration pattern once.
496             int repeatIndex = (customAlertDuration < 0)
497                     ? -1 /* not repeat */ : 0 /* index to repeat */;
498             VibrationEffect effect = VibrationEffect.createWaveform(vibrationPattern, repeatIndex);
499             log("vibrate: effect=" + effect + ", attr=" + attr + ", duration="
500                     + customAlertDuration);
501             mVibrator.vibrate(effect, attr);
502             // Android default behavior will stop vibration when screen turns off.
503             // if mute by physical button is not allowed, press power key should not turn off
504             // vibration.
505             if (!res.getBoolean(R.bool.mute_by_physical_button)) {
506                 mScreenOffReceiver = new ScreenOffReceiver(effect, attr);
507                 registerReceiver(mScreenOffReceiver, new IntentFilter(Intent.ACTION_SCREEN_OFF));
508             }
509         }
510 
511         if (mEnableLedFlash) {
512             log("Start LED flashing");
513             mHandler.sendMessage(mHandler.obtainMessage(ALERT_LED_FLASH_TOGGLE, 1, 0));
514         }
515 
516         if (mEnableAudio) {
517             // future optimization: reuse media player object
518             mMediaPlayer = mMediaPlayerInjected != null ? mMediaPlayerInjected : new MediaPlayer();
519             mMediaPlayer.setOnErrorListener(new OnErrorListener() {
520                 public boolean onError(MediaPlayer mp, int what, int extra) {
521                     loge("Error occurred while playing audio.");
522                     mHandler.sendMessage(mHandler.obtainMessage(ALERT_SOUND_FINISHED));
523                     return true;
524                 }
525             });
526 
527             // If the duration is not specified by the config, just play the alert tone
528             // with the tone's duration.
529             if (customAlertDuration < 0) {
530                 mMediaPlayer.setOnCompletionListener(new OnCompletionListener() {
531                     public void onCompletion(MediaPlayer mp) {
532                         if (DBG) log("Audio playback complete.");
533                         mHandler.sendMessage(mHandler.obtainMessage(ALERT_SOUND_FINISHED));
534                         return;
535                     }
536                 });
537             }
538 
539             try {
540                 log("Locale=" + res.getConfiguration().getLocales() + ", alertType=" + alertType);
541 
542                 // Load the tones based on type
543                 switch (alertType) {
544                     case ETWS_EARTHQUAKE:
545                         setDataSourceFromResource(res, mMediaPlayer, R.raw.etws_earthquake);
546                         break;
547                     case ETWS_TSUNAMI:
548                         setDataSourceFromResource(res, mMediaPlayer, R.raw.etws_tsunami);
549                         break;
550                     case OTHER:
551                         setDataSourceFromResource(res, mMediaPlayer, R.raw.etws_other_disaster);
552                         break;
553                     case ETWS_DEFAULT:
554                         setDataSourceFromResource(res, mMediaPlayer, R.raw.etws_default);
555                         break;
556                     case INFO:
557                     case AREA:
558                         if (getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH)) {
559                             //TODO(b/279183006): remove watch workaround when URI supported.
560                             setDataSourceFromResource(res, mMediaPlayer, R.raw.watch_info);
561                         } else {
562                             mMediaPlayer.setDataSource(this,
563                                     Settings.System.DEFAULT_NOTIFICATION_URI);
564                         }
565                         break;
566                     case TEST:
567                     case DEFAULT:
568                     default:
569                         setDataSourceFromResource(res, mMediaPlayer, R.raw.default_tone);
570                 }
571 
572                 // Request audio focus (though we're going to play even if we don't get it). The
573                 // only scenario we are not getting focus immediately is a voice call is holding
574                 // focus, since we are passing AUDIOFOCUS_FLAG_DELAY_OK, the focus will be granted
575                 // once voice call ends.
576                 mAudioManager.requestAudioFocus(this,
577                         new AudioAttributes.Builder().setLegacyStreamType(
578                                 (alertType == AlertType.INFO || alertType == AlertType.AREA) ?
579                                         AudioManager.STREAM_NOTIFICATION
580                                         : AudioManager.STREAM_ALARM).build(),
581                         AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,
582                         AudioManager.AUDIOFOCUS_FLAG_DELAY_OK);
583                 mMediaPlayer.setAudioAttributes(getAlertAudioAttributes());
584                 setAlertVolume();
585 
586                 // If we are using the custom alert duration, set looping to true so we can repeat
587                 // the alert. The tone playing will stop when ALERT_SOUND_FINISHED arrives.
588                 // Otherwise we just play the alert tone once.
589                 mMediaPlayer.setLooping(customAlertDuration >= 0);
590                 mMediaPlayer.prepare();
591                 // If the duration is specified by the config, stop playing the alert after
592                 // the specified duration.
593                 if (customAlertDuration >= 0) {
594                     mHandler.sendMessageDelayed(mHandler.obtainMessage(ALERT_SOUND_FINISHED),
595                             customAlertDuration);
596                 }
597                 mMediaPlayer.start();
598                 mIsMediaPlayerStarted = true;
599 
600             } catch (Exception ex) {
601                 loge("Failed to play alert sound: " + ex);
602                 CellBroadcastReceiverMetrics.getInstance().logModuleError(
603                         ERRSRC_CBR, ERRTYPE_PLAYSOUND);
604                 // Immediately move into the next state ALERT_SOUND_FINISHED.
605                 mHandler.sendMessage(mHandler.obtainMessage(ALERT_SOUND_FINISHED));
606             }
607         } else {
608             // In normal mode (playing tone + vibration), this service will stop after audio
609             // playback is done. However, if the device is in vibrate only mode, we need to stop
610             // the service right after vibration because there won't be any audio complete callback
611             // to stop the service. Unfortunately it's not like MediaPlayer has onCompletion()
612             // callback that we can use, we'll have to use our own timer to stop the service.
613             mHandler.sendMessageDelayed(mHandler.obtainMessage(ALERT_SOUND_FINISHED),
614                     customAlertDuration >= 0 ? customAlertDuration : vibrateDuration);
615         }
616         setState(STATE_ALERTING);
617     }
618 
setDataSourceFromResource(Resources resources, MediaPlayer player, int res)619     private static void setDataSourceFromResource(Resources resources,
620             MediaPlayer player, int res) throws java.io.IOException {
621         AssetFileDescriptor afd = resources.openRawResourceFd(res);
622         if (afd != null) {
623             player.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(),
624                     afd.getLength());
625             afd.close();
626         }
627     }
628 
629     /**
630      * Turn on camera's LED
631      *
632      * @param on {@code true} if turned on, otherwise turned off.
633      * @return {@code true} if successful, otherwise false.
634      */
enableLedFlash(boolean on)635     private boolean enableLedFlash(boolean on) {
636         log("enbleLedFlash=" + on);
637         CameraManager cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
638         if (cameraManager == null) return false;
639         final String[] ids;
640         try {
641             ids = cameraManager.getCameraIdList();
642         } catch (CameraAccessException e) {
643             CellBroadcastReceiverMetrics.getInstance()
644                     .logModuleError(ERRSRC_CBR, ERRTYPE_PLAYFLASH);
645             log("Can't get camera id");
646             return false;
647         }
648 
649         boolean success = false;
650         for (String id : ids) {
651             try {
652                 CameraCharacteristics c = cameraManager.getCameraCharacteristics(id);
653                 Boolean flashAvailable = c.get(CameraCharacteristics.FLASH_INFO_AVAILABLE);
654                 if (flashAvailable != null && flashAvailable) {
655                     cameraManager.setTorchMode(id, on);
656                     success = true;
657                 }
658             } catch (CameraAccessException e) {
659                 CellBroadcastReceiverMetrics.getInstance().logModuleError(
660                         ERRSRC_CBR, ERRTYPE_PLAYFLASH);
661                 log("Can't flash. e=" + e);
662                 // continue with the next available camera
663             }
664         }
665         return success;
666     }
667 
668     /**
669      * Stops alert audio and speech.
670      */
stop()671     public void stop() {
672         if (DBG) log("stop()");
673 
674         mHandler.removeMessages(ALERT_SOUND_FINISHED);
675         mHandler.removeMessages(ALERT_PAUSE_FINISHED);
676         mHandler.removeMessages(ALERT_LED_FLASH_TOGGLE);
677 
678         resetAlarmStreamVolume();
679 
680         // Stop audio playing
681         if (mMediaPlayer != null && mIsMediaPlayerStarted) {
682             try {
683                 mMediaPlayer.stop();
684                 mMediaPlayer.release();
685             } catch (IllegalStateException e) {
686                 // catch "Unable to retrieve AudioTrack pointer for stop()" exception
687                 loge("exception trying to stop media player");
688             }
689             mIsMediaPlayerStarted = false;
690             mMediaPlayer = null;
691         }
692 
693         // Stop vibrator
694         mVibrator.cancel();
695         if (mScreenOffReceiver != null) {
696             try {
697                 unregisterReceiver(mScreenOffReceiver);
698             } catch (Exception e) {
699                 // already unregistered
700             }
701             mScreenOffReceiver = null;
702         }
703 
704         if (mEnableLedFlash) {
705             enableLedFlash(false);
706         }
707 
708         if (mTts != null && mIsTextToSpeechSpeaking) {
709             try {
710                 mTts.stop();
711             } catch (IllegalStateException e) {
712                 // catch "Unable to retrieve AudioTrack pointer for stop()" exception
713                 loge("exception trying to stop text-to-speech");
714                 CellBroadcastReceiverMetrics.getInstance()
715                         .logModuleError(ERRSRC_CBR, ERRTYPE_PLAYTTS);
716             }
717             mIsTextToSpeechSpeaking = false;
718         }
719 
720         // Service will be destroyed if the state is STATE_STOPPING,
721         // so it should not be changed to another state.
722         if (getState() != STATE_STOPPING) {
723             setState(STATE_IDLE);
724         }
725     }
726 
727     @Override
onAudioFocusChange(int focusChange)728     public void onAudioFocusChange(int focusChange) {
729         log("onAudioFocusChanged: " + focusChange);
730         // Do nothing, as we don't care if focus was steal from other apps, as emergency alerts will
731         // play anyway.
732     }
733 
734     /**
735      * Get audio attribute for the alarm.
736      */
getAlertAudioAttributes()737     private AudioAttributes getAlertAudioAttributes() {
738         AudioAttributes.Builder builder = new AudioAttributes.Builder();
739 
740         builder.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION);
741         builder.setUsage((mAlertType == AlertType.INFO || mAlertType == AlertType.AREA) ?
742                 AudioAttributes.USAGE_NOTIFICATION : AudioAttributes.USAGE_ALARM);
743         if (mOverrideDnd) {
744             // Set FLAG_BYPASS_INTERRUPTION_POLICY and FLAG_BYPASS_MUTE so that it enables
745             // audio in any DnD mode, even in total silence DnD mode (requires MODIFY_PHONE_STATE).
746 
747             // Note: this only works when the audio attributes usage is set to USAGE_ALARM. If
748             // regulatory concerns mean that we need to bypass DnD for AlertType.INFO or
749             // AlertType.AREA as well, we'll need to add a config flag to have INFO go over the
750             // alarm stream as well for those jurisdictions in which those regulatory concerns apply
751             builder.setFlags(AudioAttributes.FLAG_BYPASS_INTERRUPTION_POLICY
752                     | AudioAttributes.FLAG_BYPASS_MUTE);
753         }
754 
755         return builder.build();
756     }
757 
758     /**
759      * Set volume for alerts.
760      */
setAlertVolume()761     private void setAlertVolume() {
762         if (mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE
763                 || isOnEarphone()) {
764             // If we are in a call, play the alert
765             // sound at a low volume to not disrupt the call.
766             log("in call: reducing volume");
767             mMediaPlayer.setVolume(IN_CALL_VOLUME_LEFT, IN_CALL_VOLUME_RIGHT);
768         } else if (mOverrideDnd) {
769             // If override DnD is turned on,
770             // we overwrite volume setting of STREAM_ALARM to full, play at
771             // max possible volume, and reset it after it's finished.
772             setAlarmStreamVolumeToFull();
773         }
774     }
775 
isOnEarphone()776     private boolean isOnEarphone() {
777         AudioDeviceInfo[] deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
778 
779         for (AudioDeviceInfo devInfo : deviceList) {
780             int type = devInfo.getType();
781             if (type == AudioDeviceInfo.TYPE_WIRED_HEADSET
782                     || type == AudioDeviceInfo.TYPE_WIRED_HEADPHONES
783                     || type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO
784                     || type == AudioDeviceInfo.TYPE_BLUETOOTH_A2DP) {
785                 return true;
786             }
787         }
788 
789         return false;
790     }
791 
792     /**
793      * Set volume of STREAM_ALARM to full.
794      */
setAlarmStreamVolumeToFull()795     private void setAlarmStreamVolumeToFull() {
796         if (mAlertType != AlertType.INFO && mAlertType != AlertType.AREA) {
797             log("setting alarm volume to full for cell broadcast alerts.");
798             int streamType = AudioManager.STREAM_ALARM;
799             mUserSetAlarmVolume = mAudioManager.getStreamVolume(streamType);
800             mResetAlarmVolumeNeeded = true;
801             mAudioManager.setStreamVolume(streamType,
802                     mAudioManager.getStreamMaxVolume(streamType), 0);
803         } else {
804             log("Skipping setting alarm volume to full for alert type INFO and AREA");
805         }
806     }
807 
808     /**
809      * Reset volume of STREAM_ALARM, if needed.
810      */
resetAlarmStreamVolume()811     private void resetAlarmStreamVolume() {
812         if (mResetAlarmVolumeNeeded) {
813             log("resetting alarm volume to back to " + mUserSetAlarmVolume);
814             mAudioManager.setStreamVolume(AudioManager.STREAM_ALARM, mUserSetAlarmVolume, 0);
815             mResetAlarmVolumeNeeded = false;
816         }
817     }
818 
819     /**
820      * Stop CellBroadcastAlertAudio Service and set state to STATE_STOPPING
821      */
stopAlertAudioService()822     private boolean stopAlertAudioService() {
823         if (DBG) log("stopAlertAudioService, current state is " + getState());
824         boolean result = false;
825         if (getState() != STATE_STOPPING) {
826             setState(STATE_STOPPING);
827             result = stopSelfResult(mStartId);
828             if (DBG) log((result ? "Successful" : "Failed")
829                     + " to stop AlertAudioService[" + mStartId + "]");
830         }
831         return result;
832     }
833 
834     /**
835      * Set AlertAudioService state
836      *
837      * @param state service status
838      */
setState(int state)839     private synchronized void setState(int state) {
840         if (DBG) log("Set state from " + mState + " to " + state);
841         mState = state;
842     }
843 
844     /**
845      * Get AlertAudioService status
846      * @return service status
847      */
848     @VisibleForTesting
getState()849     public synchronized int getState() {
850         return mState;
851     }
852 
853     /*
854      * BroadcastReceiver for screen off events. Used for Latam.
855      * CMAS requirements to make sure vibration continues when screen goes off
856      */
857     private class ScreenOffReceiver extends BroadcastReceiver {
858         VibrationEffect mVibrationEffect;
859         AudioAttributes mAudioAttr;
860 
ScreenOffReceiver(VibrationEffect effect, AudioAttributes attributes)861         ScreenOffReceiver(VibrationEffect effect, AudioAttributes attributes) {
862             this.mVibrationEffect = effect;
863             this.mAudioAttr = attributes;
864         }
865 
866         @Override
onReceive(Context context, Intent intent)867         public void onReceive(Context context, Intent intent) {
868             // Restart the vibration after screen off
869             if (mState == STATE_ALERTING) {
870                 mVibrator.vibrate(mVibrationEffect, mAudioAttr);
871             }
872         }
873     }
874 
log(String msg)875     private static void log(String msg) {
876         Log.d(TAG, msg);
877     }
878 
loge(String msg)879     private static void loge(String msg) {
880         Log.e(TAG, msg);
881     }
882 }
883