Merge "Add unit tests for CellBroadcastAlertAudio"
diff --git a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertAudio.java b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertAudio.java
index b8b9607..0159dfe 100644
--- a/src/com/android/cellbroadcastreceiver/CellBroadcastAlertAudio.java
+++ b/src/com/android/cellbroadcastreceiver/CellBroadcastAlertAudio.java
@@ -137,75 +137,13 @@
private static final int ALERT_PAUSE_FINISHED = 1001;
private static final int ALERT_LED_FLASH_TOGGLE = 1002;
- private final Handler mHandler = new Handler(Looper.myLooper()) {
- @Override
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case ALERT_SOUND_FINISHED:
- if (DBG) log("ALERT_SOUND_FINISHED");
- stop(); // stop alert sound
- // if we can speak the message text
- if (mMessageBody != null && mTtsEngineReady && mTtsLanguageSupported) {
- sendMessageDelayed(mHandler.obtainMessage(ALERT_PAUSE_FINISHED),
- PAUSE_DURATION_BEFORE_SPEAKING_MSEC);
- mState = STATE_PAUSING;
- } else {
- if (DBG) log("MessageEmpty = " + (mMessageBody == null) +
- ", mTtsEngineReady = " + mTtsEngineReady +
- ", mTtsLanguageSupported = " + mTtsLanguageSupported);
- stopSelf();
- mState = STATE_IDLE;
- }
- // Set alert reminder depending on user preference
- CellBroadcastAlertReminder.queueAlertReminder(getApplicationContext(), mSubId,
- true);
- break;
+ private Handler mHandler;
- case ALERT_PAUSE_FINISHED:
- if (DBG) log("ALERT_PAUSE_FINISHED");
- int res = TextToSpeech.ERROR;
- if (mMessageBody != null && mTtsEngineReady && mTtsLanguageSupported) {
- if (DBG) log("Speaking broadcast text: " + mMessageBody);
-
- mTts.setAudioAttributes(getAlertAudioAttributes(mAlertType));
- res = mTts.speak(mMessageBody, 2, null, TTS_UTTERANCE_ID);
- mState = STATE_SPEAKING;
- }
- if (res != TextToSpeech.SUCCESS) {
- loge("TTS engine not ready or language not supported or speak() failed");
- stopSelf();
- mState = STATE_IDLE;
- }
- break;
-
- case ALERT_LED_FLASH_TOGGLE:
- if (enableLedFlash(msg.arg1 != 0)) {
- sendMessageDelayed(mHandler.obtainMessage(
- ALERT_LED_FLASH_TOGGLE, msg.arg1 != 0 ? 0 : 1, 0),
- DEFAULT_LED_FLASH_INTERVAL_MSEC);
- }
- break;
-
- default:
- loge("Handler received unknown message, what=" + msg.what);
- }
- }
- };
-
- private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
- @Override
- public void onCallStateChanged(int state, String ignored) {
- // Stop the alert sound and speech if the call state changes.
- if (state != TelephonyManager.CALL_STATE_IDLE
- && state != mInitialCallState) {
- if (DBG) log("Call interrupted. Stop CellBroadcastAlertAudio service");
- stopSelf();
- }
- }
- };
+ private PhoneStateListener mPhoneStateListener;
/**
* Callback from TTS engine after initialization.
+ *
* @param status {@link TextToSpeech#SUCCESS} or {@link TextToSpeech#ERROR}.
*/
@Override
@@ -246,6 +184,7 @@
/**
* Callback from TTS engine.
+ *
* @param utteranceId the identifier of the utterance.
*/
@Override
@@ -266,6 +205,75 @@
mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
// Listen for incoming calls to kill the alarm.
mTelephonyManager = ((TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE));
+ mHandler = new Handler(Looper.getMainLooper()) {
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case ALERT_SOUND_FINISHED:
+ if (DBG) log("ALERT_SOUND_FINISHED");
+ stop(); // stop alert sound
+ // if we can speak the message text
+ if (mMessageBody != null && mTtsEngineReady && mTtsLanguageSupported) {
+ sendMessageDelayed(mHandler.obtainMessage(ALERT_PAUSE_FINISHED),
+ PAUSE_DURATION_BEFORE_SPEAKING_MSEC);
+ mState = STATE_PAUSING;
+ } else {
+ if (DBG) {
+ log("MessageEmpty = " + (mMessageBody == null)
+ + ", mTtsEngineReady = " + mTtsEngineReady
+ + ", mTtsLanguageSupported = " + mTtsLanguageSupported);
+ }
+ stopSelf();
+ mState = STATE_IDLE;
+ }
+ // Set alert reminder depending on user preference
+ CellBroadcastAlertReminder.queueAlertReminder(getApplicationContext(),
+ mSubId,
+ true);
+ break;
+
+ case ALERT_PAUSE_FINISHED:
+ if (DBG) log("ALERT_PAUSE_FINISHED");
+ int res = TextToSpeech.ERROR;
+ if (mMessageBody != null && mTtsEngineReady && mTtsLanguageSupported) {
+ if (DBG) log("Speaking broadcast text: " + mMessageBody);
+
+ mTts.setAudioAttributes(getAlertAudioAttributes(mAlertType));
+ res = mTts.speak(mMessageBody, 2, null, TTS_UTTERANCE_ID);
+ mState = STATE_SPEAKING;
+ }
+ if (res != TextToSpeech.SUCCESS) {
+ loge("TTS engine not ready or language not supported or speak() "
+ + "failed");
+ stopSelf();
+ mState = STATE_IDLE;
+ }
+ break;
+
+ case ALERT_LED_FLASH_TOGGLE:
+ if (enableLedFlash(msg.arg1 != 0)) {
+ sendMessageDelayed(mHandler.obtainMessage(
+ ALERT_LED_FLASH_TOGGLE, msg.arg1 != 0 ? 0 : 1, 0),
+ DEFAULT_LED_FLASH_INTERVAL_MSEC);
+ }
+ break;
+
+ default:
+ loge("Handler received unknown message, what=" + msg.what);
+ }
+ }
+ };
+ mPhoneStateListener = new PhoneStateListener() {
+ @Override
+ public void onCallStateChanged(int state, String ignored) {
+ // Stop the alert sound and speech if the call state changes.
+ if (state != TelephonyManager.CALL_STATE_IDLE
+ && state != mInitialCallState) {
+ if (DBG) log("Call interrupted. Stop CellBroadcastAlertAudio service");
+ stopSelf();
+ }
+ }
+ };
mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}
@@ -385,7 +393,8 @@
/**
* Start playing the alert sound.
- * @param alertType the alert type (e.g. default, earthquake, tsunami, etc..)
+ *
+ * @param alertType the alert type (e.g. default, earthquake, tsunami, etc..)
* @param patternArray the alert vibration pattern
*/
private void playAlertTone(AlertType alertType, int[] patternArray) {
@@ -533,7 +542,6 @@
* Turn on camera's LED
*
* @param on {@code true} if turned on, otherwise turned off.
- *
* @return {@code true} if successful, otherwise false.
*/
private boolean enableLedFlash(boolean on) {
@@ -679,7 +687,7 @@
if (mResetAlarmVolumeNeeded) {
log("resetting alarm volume to back to " + mUserSetAlarmVolume);
mAudioManager.setStreamVolume(alertType == AlertType.INFO
- ? AudioManager.STREAM_NOTIFICATION : AudioManager.STREAM_ALARM,
+ ? AudioManager.STREAM_NOTIFICATION : AudioManager.STREAM_ALARM,
mUserSetAlarmVolume, 0);
mResetAlarmVolumeNeeded = false;
}
diff --git a/tests/unit/src/com/android/cellbroadcastreceiver/unit/CellBroadcastAlertAudioTest.java b/tests/unit/src/com/android/cellbroadcastreceiver/unit/CellBroadcastAlertAudioTest.java
new file mode 100644
index 0000000..c743f99
--- /dev/null
+++ b/tests/unit/src/com/android/cellbroadcastreceiver/unit/CellBroadcastAlertAudioTest.java
@@ -0,0 +1,264 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cellbroadcastreceiver.unit;
+
+import static com.android.cellbroadcastreceiver.CellBroadcastAlertService.SHOW_NEW_ALERT_ACTION;
+
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.ArgumentMatchers.eq;
+import static org.mockito.Mockito.atLeastOnce;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.media.AudioDeviceInfo;
+import android.media.AudioManager;
+import android.os.HandlerThread;
+import android.telephony.TelephonyManager;
+
+import com.android.cellbroadcastreceiver.CellBroadcastAlertAudio;
+
+import org.junit.After;
+import org.junit.Before;
+import org.mockito.MockitoAnnotations;
+
+public class CellBroadcastAlertAudioTest extends
+ CellBroadcastServiceTestCase<CellBroadcastAlertAudio> {
+
+ private static final String TEST_MESSAGE_BODY = "test message body";
+ private static final int[] TEST_VIBRATION_PATTERN = new int[]{0, 1, 0, 1};
+ private static final int TEST_MAX_VOLUME = 1001;
+ private static final long MAX_INIT_WAIT_MS = 5000;
+
+ private Configuration mConfiguration = new Configuration();
+ private AudioDeviceInfo[] mDevices = new AudioDeviceInfo[0];
+ private Object mLock = new Object();
+ private boolean mReady;
+
+ public CellBroadcastAlertAudioTest() {
+ super(CellBroadcastAlertAudio.class);
+ }
+
+ private class PhoneStateListenerHandler extends HandlerThread {
+
+ private Runnable mFunction;
+
+ PhoneStateListenerHandler(String name, Runnable func) {
+ super(name);
+ mFunction = func;
+ }
+
+ @Override
+ public void onLooperPrepared() {
+ mFunction.run();
+ setReady(true);
+ }
+ }
+
+ protected void waitUntilReady() {
+ synchronized (mLock) {
+ if (!mReady) {
+ try {
+ mLock.wait(MAX_INIT_WAIT_MS);
+ } catch (InterruptedException ie) {
+ }
+
+ if (!mReady) {
+ fail("Telephony tests failed to initialize");
+ }
+ }
+ }
+ }
+
+ protected void setReady(boolean ready) {
+ synchronized (mLock) {
+ mReady = ready;
+ mLock.notifyAll();
+ }
+ }
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ MockitoAnnotations.initMocks(this);
+ doReturn(mConfiguration).when(mResources).getConfiguration();
+ doReturn(mDevices).when(mMockedAudioManager).getDevices(anyInt());
+
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ public void testStartService() throws Throwable {
+ PhoneStateListenerHandler phoneStateListenerHandler = new PhoneStateListenerHandler(
+ "testStartService",
+ () -> {
+ doReturn(AudioManager.RINGER_MODE_NORMAL).when(
+ mMockedAudioManager).getRingerMode();
+
+ Intent intent = new Intent(mContext, CellBroadcastAlertAudio.class);
+ intent.setAction(SHOW_NEW_ALERT_ACTION);
+ intent.putExtra(CellBroadcastAlertAudio.ALERT_AUDIO_MESSAGE_BODY,
+ TEST_MESSAGE_BODY);
+ intent.putExtra(CellBroadcastAlertAudio.ALERT_AUDIO_VIBRATION_PATTERN_EXTRA,
+ TEST_VIBRATION_PATTERN);
+ startService(intent);
+ });
+ phoneStateListenerHandler.start();
+ waitUntilReady();
+ verify(mMockedAudioManager).getRingerMode();
+ verify(mMockedVibrator).vibrate(any(), any());
+ phoneStateListenerHandler.quit();
+ }
+
+ /**
+ * If the user is currently not in a call and the override DND flag is set, the volume will be
+ * set to max.
+ */
+ public void testStartServiceNotInCallOverrideDnd() throws Throwable {
+ PhoneStateListenerHandler phoneStateListenerHandler = new PhoneStateListenerHandler(
+ "testStartServiceNotInCallOverrideDnd",
+ () -> {
+ doReturn(AudioManager.RINGER_MODE_SILENT).when(
+ mMockedAudioManager).getRingerMode();
+ doReturn(TelephonyManager.CALL_STATE_IDLE).when(
+ mMockedTelephonyManager).getCallState();
+ doReturn(TEST_MAX_VOLUME).when(mMockedAudioManager).getStreamMaxVolume(
+ anyInt());
+
+ Intent intent = new Intent(mContext, CellBroadcastAlertAudio.class);
+ intent.setAction(SHOW_NEW_ALERT_ACTION);
+ intent.putExtra(CellBroadcastAlertAudio.ALERT_AUDIO_MESSAGE_BODY,
+ TEST_MESSAGE_BODY);
+ intent.putExtra(CellBroadcastAlertAudio.ALERT_AUDIO_VIBRATION_PATTERN_EXTRA,
+ TEST_VIBRATION_PATTERN);
+ intent.putExtra(CellBroadcastAlertAudio.ALERT_AUDIO_OVERRIDE_DND_EXTRA, true);
+ startService(intent);
+ });
+ phoneStateListenerHandler.start();
+ waitUntilReady();
+ verify(mMockedAudioManager).getRingerMode();
+ verify(mMockedVibrator).vibrate(any(), any());
+ verify(mMockedTelephonyManager, atLeastOnce()).getCallState();
+ verify(mMockedAudioManager).requestAudioFocus(any(), anyInt(), anyInt());
+ verify(mMockedAudioManager).getDevices(anyInt());
+ verify(mMockedAudioManager).setStreamVolume(anyInt(), eq(TEST_MAX_VOLUME), anyInt());
+ phoneStateListenerHandler.quit();
+ }
+
+ public void testStartServiceEnableLedFlash() throws Throwable {
+ PhoneStateListenerHandler phoneStateListenerHandler = new PhoneStateListenerHandler(
+ "testStartServiceEnableLedFlash",
+ () -> {
+ doReturn(AudioManager.RINGER_MODE_NORMAL).when(
+ mMockedAudioManager).getRingerMode();
+ doReturn(true).when(mResources).getBoolean(
+ eq(com.android.cellbroadcastreceiver.R.bool.enable_led_flash));
+
+ Intent intent = new Intent(mContext, CellBroadcastAlertAudio.class);
+ intent.setAction(SHOW_NEW_ALERT_ACTION);
+ intent.putExtra(CellBroadcastAlertAudio.ALERT_AUDIO_MESSAGE_BODY,
+ TEST_MESSAGE_BODY);
+ intent.putExtra(CellBroadcastAlertAudio.ALERT_AUDIO_VIBRATION_PATTERN_EXTRA,
+ TEST_VIBRATION_PATTERN);
+ startService(intent);
+ });
+ phoneStateListenerHandler.start();
+ waitUntilReady();
+ // TODO(b/134400042): we can't mock CameraManager because it's final, but let's at least
+ // make sure the code doesn't crash. If we switch to Mockito 2 this
+ // will be mockable.
+ //verify(mMockedCameraManager).setTorchMode(anyString(), true);
+ phoneStateListenerHandler.quit();
+ }
+
+ public void testStartServiceSilentRinger() throws Throwable {
+ PhoneStateListenerHandler phoneStateListenerHandler = new PhoneStateListenerHandler(
+ "testStartServiceSilentRinger",
+ () -> {
+ doReturn(AudioManager.RINGER_MODE_SILENT).when(
+ mMockedAudioManager).getRingerMode();
+
+ Intent intent = new Intent(mContext, CellBroadcastAlertAudio.class);
+ intent.setAction(SHOW_NEW_ALERT_ACTION);
+ intent.putExtra(CellBroadcastAlertAudio.ALERT_AUDIO_MESSAGE_BODY,
+ TEST_MESSAGE_BODY);
+ intent.putExtra(CellBroadcastAlertAudio.ALERT_AUDIO_VIBRATION_PATTERN_EXTRA,
+ TEST_VIBRATION_PATTERN);
+ startService(intent);
+ });
+ phoneStateListenerHandler.start();
+ waitUntilReady();
+ verify(mMockedAudioManager).getRingerMode();
+ verify(mMockedVibrator, times(0)).vibrate(any(), any());
+ phoneStateListenerHandler.quit();
+ }
+
+ public void testStartServiceVibrateRinger() throws Throwable {
+ PhoneStateListenerHandler phoneStateListenerHandler = new PhoneStateListenerHandler(
+ "testStartServiceVibrateRinger",
+ () -> {
+ doReturn(AudioManager.RINGER_MODE_VIBRATE).when(
+ mMockedAudioManager).getRingerMode();
+
+ Intent intent = new Intent(mContext, CellBroadcastAlertAudio.class);
+ intent.setAction(SHOW_NEW_ALERT_ACTION);
+ intent.putExtra(CellBroadcastAlertAudio.ALERT_AUDIO_MESSAGE_BODY,
+ TEST_MESSAGE_BODY);
+ intent.putExtra(CellBroadcastAlertAudio.ALERT_AUDIO_VIBRATION_PATTERN_EXTRA,
+ TEST_VIBRATION_PATTERN);
+ startService(intent);
+ });
+ phoneStateListenerHandler.start();
+ waitUntilReady();
+ verify(mMockedAudioManager).getRingerMode();
+ verify(mMockedVibrator).vibrate(any(), any());
+ verify(mMockedVibrator).cancel();
+ phoneStateListenerHandler.quit();
+ }
+
+ /**
+ * When an alert is triggered while an alert is already happening, the system needs to stop
+ * the previous alert.
+ */
+ public void testStartServiceAndStop() throws Throwable {
+ Intent intent = new Intent(mContext, CellBroadcastAlertAudio.class);
+ intent.setAction(SHOW_NEW_ALERT_ACTION);
+ intent.putExtra(CellBroadcastAlertAudio.ALERT_AUDIO_MESSAGE_BODY,
+ TEST_MESSAGE_BODY);
+ intent.putExtra(CellBroadcastAlertAudio.ALERT_AUDIO_VIBRATION_PATTERN_EXTRA,
+ TEST_VIBRATION_PATTERN);
+ doReturn(AudioManager.RINGER_MODE_NORMAL).when(
+ mMockedAudioManager).getRingerMode();
+
+ PhoneStateListenerHandler phoneStateListenerHandler = new PhoneStateListenerHandler(
+ "testStartServiceStop",
+ () -> {
+ startService(intent);
+ startService(intent);
+ });
+ phoneStateListenerHandler.start();
+ waitUntilReady();
+ verify(mMockedVibrator, times(2)).cancel();
+ phoneStateListenerHandler.quit();
+ }
+}
diff --git a/tests/unit/src/com/android/cellbroadcastreceiver/unit/CellBroadcastServiceTestCase.java b/tests/unit/src/com/android/cellbroadcastreceiver/unit/CellBroadcastServiceTestCase.java
index 94a5265..ee72f0e 100644
--- a/tests/unit/src/com/android/cellbroadcastreceiver/unit/CellBroadcastServiceTestCase.java
+++ b/tests/unit/src/com/android/cellbroadcastreceiver/unit/CellBroadcastServiceTestCase.java
@@ -28,6 +28,7 @@
import android.content.Intent;
import android.content.res.Resources;
import android.media.AudioManager;
+import android.os.Vibrator;
import android.telephony.CarrierConfigManager;
import android.telephony.SubscriptionInfo;
import android.telephony.SubscriptionManager;
@@ -60,6 +61,8 @@
protected SubscriptionInfo mMockSubscriptionInfo;
@Mock
protected TelephonyManager mMockedTelephonyManager;
+ @Mock
+ protected Vibrator mMockedVibrator;
MockedServiceManager mMockedServiceManager;
@@ -118,6 +121,8 @@
return mMockedSubscriptionManager;
case Context.TELEPHONY_SERVICE:
return mMockedTelephonyManager;
+ case Context.VIBRATOR_SERVICE:
+ return mMockedVibrator;
}
return super.getSystemService(name);
}
@@ -130,7 +135,8 @@
// CellBroadcastSettings.getResources(context).
doReturn(mSubService).when(mSubService).queryLocalInterface(anyString());
doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mSubService).getDefaultSubId();
- doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(mSubService).getDefaultSmsSubId();
+ doReturn(SubscriptionManager.INVALID_SUBSCRIPTION_ID).when(
+ mSubService).getDefaultSmsSubId();
doReturn(new String[]{""}).when(mResources).getStringArray(anyInt());