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