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