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