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