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