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