blob: a8d516405ea2aa1169660bc2c65402817585e151 [file] [log] [blame]
/*
* Copyright (C) 2017 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 android.hardware.radio.tests.functional;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
import android.hardware.radio.RadioTuner;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import android.test.suitebuilder.annotation.MediumTest;
import android.util.Log;
import java.lang.reflect.Constructor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import static org.junit.Assert.*;
import static org.junit.Assume.*;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyInt;
import static org.mockito.Mockito.after;
import static org.mockito.Mockito.atLeast;
import static org.mockito.Mockito.atMost;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.testng.Assert.assertThrows;
/**
* A test for broadcast radio API.
*/
@RunWith(AndroidJUnit4.class)
@MediumTest
public class RadioTunerTest {
private static final String TAG = "BroadcastRadioTests.RadioTuner";
public final Context mContext = InstrumentationRegistry.getContext();
private final int kConfigCallbackTimeoutMs = 10000;
private final int kCancelTimeoutMs = 1000;
private final int kTuneCallbackTimeoutMs = 30000;
private final int kFullScanTimeoutMs = 60000;
private RadioManager mRadioManager;
private RadioTuner mRadioTuner;
private RadioManager.ModuleProperties mModule;
private final List<RadioManager.ModuleProperties> mModules = new ArrayList<>();
@Mock private RadioTuner.Callback mCallback;
RadioManager.AmBandDescriptor mAmBandDescriptor;
RadioManager.FmBandDescriptor mFmBandDescriptor;
RadioManager.BandConfig mAmBandConfig;
RadioManager.BandConfig mFmBandConfig;
@Before
public void setup() {
MockitoAnnotations.initMocks(this);
// check if radio is supported and skip the test if it's not
PackageManager packageManager = mContext.getPackageManager();
boolean isRadioSupported = packageManager.hasSystemFeature(
PackageManager.FEATURE_BROADCAST_RADIO);
assumeTrue(isRadioSupported);
// Check radio access permission
int res = mContext.checkCallingOrSelfPermission(Manifest.permission.ACCESS_BROADCAST_RADIO);
assertEquals("ACCESS_BROADCAST_RADIO permission not granted",
PackageManager.PERMISSION_GRANTED, res);
mRadioManager = (RadioManager)mContext.getSystemService(Context.RADIO_SERVICE);
assertNotNull(mRadioManager);
int status = mRadioManager.listModules(mModules);
assertEquals(RadioManager.STATUS_OK, status);
assertFalse(mModules.isEmpty());
}
@After
public void tearDown() {
mRadioManager = null;
mModules.clear();
if (mRadioTuner != null) {
mRadioTuner.close();
mRadioTuner = null;
}
resetCallback();
}
private void openTuner() {
openTuner(true);
}
private void resetCallback() {
verify(mCallback, atLeast(0)).onMetadataChanged(any());
verifyNoMoreInteractions(mCallback);
Mockito.reset(mCallback);
}
private void openTuner(boolean withAudio) {
assertNull(mRadioTuner);
// find FM band and build its config
mModule = mModules.get(0);
for (RadioManager.BandDescriptor band : mModule.getBands()) {
Log.d(TAG, "Band: " + band);
int bandType = band.getType();
if (bandType == RadioManager.BAND_AM || bandType == RadioManager.BAND_AM_HD) {
mAmBandDescriptor = (RadioManager.AmBandDescriptor)band;
}
if (bandType == RadioManager.BAND_FM || bandType == RadioManager.BAND_FM_HD) {
mFmBandDescriptor = (RadioManager.FmBandDescriptor)band;
}
}
assertNotNull(mAmBandDescriptor);
assertNotNull(mFmBandDescriptor);
mAmBandConfig = new RadioManager.AmBandConfig.Builder(mAmBandDescriptor).build();
mFmBandConfig = new RadioManager.FmBandConfig.Builder(mFmBandDescriptor).build();
mRadioTuner = mRadioManager.openTuner(mModule.getId(),
mFmBandConfig, withAudio, mCallback, null);
assertNotNull(mRadioTuner);
verify(mCallback, timeout(kConfigCallbackTimeoutMs)).onConfigurationChanged(any());
resetCallback();
boolean isAntennaConnected = mRadioTuner.isAntennaConnected();
assertTrue(isAntennaConnected);
}
@Test
public void testOpenTuner() {
openTuner();
}
@Test
public void testReopenTuner() throws Throwable {
openTuner();
mRadioTuner.close();
mRadioTuner = null;
Thread.sleep(100); // TODO(b/36122635): force reopen
openTuner();
}
@Test
public void testDoubleClose() {
openTuner();
mRadioTuner.close();
mRadioTuner.close();
}
@Test
public void testUseAfterClose() {
openTuner();
mRadioTuner.close();
int ret = mRadioTuner.cancel();
assertEquals(RadioManager.STATUS_INVALID_OPERATION, ret);
}
@Test
public void testSetAndGetConfiguration() {
openTuner();
// set
int ret = mRadioTuner.setConfiguration(mAmBandConfig);
assertEquals(RadioManager.STATUS_OK, ret);
verify(mCallback, timeout(kConfigCallbackTimeoutMs)).onConfigurationChanged(any());
// get
RadioManager.BandConfig[] config = new RadioManager.BandConfig[1];
ret = mRadioTuner.getConfiguration(config);
assertEquals(RadioManager.STATUS_OK, ret);
assertEquals(mAmBandConfig, config[0]);
}
@Test
public void testSetBadConfiguration() throws Throwable {
openTuner();
// set bad config
Constructor<RadioManager.AmBandConfig> configConstr =
RadioManager.AmBandConfig.class.getDeclaredConstructor(
int.class, int.class, int.class, int.class, int.class, boolean.class);
configConstr.setAccessible(true);
RadioManager.AmBandConfig badConfig = configConstr.newInstance(
0 /*region*/, RadioManager.BAND_AM /*type*/,
10000 /*lowerLimit*/, 1 /*upperLimit*/, 100 /*spacing*/, false /*stereo*/);
int ret = mRadioTuner.setConfiguration(badConfig);
assertEquals(RadioManager.STATUS_BAD_VALUE, ret);
verify(mCallback, never()).onConfigurationChanged(any());
// set null config
ret = mRadioTuner.setConfiguration(null);
assertEquals(RadioManager.STATUS_BAD_VALUE, ret);
verify(mCallback, never()).onConfigurationChanged(any());
// setting good config should recover
ret = mRadioTuner.setConfiguration(mAmBandConfig);
assertEquals(RadioManager.STATUS_OK, ret);
verify(mCallback, timeout(kConfigCallbackTimeoutMs)).onConfigurationChanged(any());
}
@Test
public void testMute() {
openTuner();
boolean isMuted = mRadioTuner.getMute();
assertFalse(isMuted);
int ret = mRadioTuner.setMute(true);
assertEquals(RadioManager.STATUS_OK, ret);
isMuted = mRadioTuner.getMute();
assertTrue(isMuted);
ret = mRadioTuner.setMute(false);
assertEquals(RadioManager.STATUS_OK, ret);
isMuted = mRadioTuner.getMute();
assertFalse(isMuted);
}
@Test
public void testMuteNoAudio() {
openTuner(false);
int ret = mRadioTuner.setMute(false);
assertEquals(RadioManager.STATUS_ERROR, ret);
boolean isMuted = mRadioTuner.getMute();
assertTrue(isMuted);
}
@Test
public void testStep() {
openTuner();
int ret = mRadioTuner.step(RadioTuner.DIRECTION_DOWN, true);
assertEquals(RadioManager.STATUS_OK, ret);
verify(mCallback, timeout(kTuneCallbackTimeoutMs)).onProgramInfoChanged(any());
resetCallback();
ret = mRadioTuner.step(RadioTuner.DIRECTION_UP, false);
assertEquals(RadioManager.STATUS_OK, ret);
verify(mCallback, timeout(kTuneCallbackTimeoutMs)).onProgramInfoChanged(any());
}
@Test
public void testStepLoop() {
openTuner();
for (int i = 0; i < 10; i++) {
Log.d(TAG, "step loop iteration " + (i + 1));
int ret = mRadioTuner.step(RadioTuner.DIRECTION_DOWN, true);
assertEquals(RadioManager.STATUS_OK, ret);
verify(mCallback, timeout(kTuneCallbackTimeoutMs)).onProgramInfoChanged(any());
resetCallback();
}
}
@Test
public void testTuneAndGetPI() {
openTuner();
int channel = mFmBandConfig.getLowerLimit() + mFmBandConfig.getSpacing();
// test tune
int ret = mRadioTuner.tune(channel, 0);
assertEquals(RadioManager.STATUS_OK, ret);
ArgumentCaptor<RadioManager.ProgramInfo> infoc =
ArgumentCaptor.forClass(RadioManager.ProgramInfo.class);
verify(mCallback, timeout(kTuneCallbackTimeoutMs))
.onProgramInfoChanged(infoc.capture());
assertEquals(channel, infoc.getValue().getChannel());
// test getProgramInformation
RadioManager.ProgramInfo[] info = new RadioManager.ProgramInfo[1];
ret = mRadioTuner.getProgramInformation(info);
assertEquals(RadioManager.STATUS_OK, ret);
assertNotNull(info[0]);
assertEquals(channel, info[0].getChannel());
Log.d(TAG, "PI: " + info[0].toString());
}
@Test
public void testDummyCancel() {
openTuner();
int ret = mRadioTuner.cancel();
assertEquals(RadioManager.STATUS_OK, ret);
}
@Test
public void testLateCancel() {
openTuner();
int ret = mRadioTuner.step(RadioTuner.DIRECTION_DOWN, false);
assertEquals(RadioManager.STATUS_OK, ret);
verify(mCallback, timeout(kTuneCallbackTimeoutMs)).onProgramInfoChanged(any());
int cancelRet = mRadioTuner.cancel();
assertEquals(RadioManager.STATUS_OK, cancelRet);
}
@Test
public void testScanAndCancel() {
openTuner();
/* There is a possible race condition between scan and cancel commands - the scan may finish
* before cancel command is issued. Thus we accept both outcomes in this test.
*/
int scanRet = mRadioTuner.scan(RadioTuner.DIRECTION_DOWN, true);
int cancelRet = mRadioTuner.cancel();
assertEquals(RadioManager.STATUS_OK, scanRet);
assertEquals(RadioManager.STATUS_OK, cancelRet);
verify(mCallback, after(kCancelTimeoutMs).atMost(1)).onError(RadioTuner.ERROR_CANCELLED);
verify(mCallback, atMost(1)).onProgramInfoChanged(any());
}
@Test
public void testStartBackgroundScan() {
openTuner();
boolean ret = mRadioTuner.startBackgroundScan();
boolean isSupported = mModule.isBackgroundScanningSupported();
assertEquals(isSupported, ret);
}
@Test
public void testGetProgramList() {
openTuner();
try {
Map<String, String> filter = new HashMap<>();
filter.put("com.google.dummy", "dummy");
List<RadioManager.ProgramInfo> list = mRadioTuner.getProgramList(filter);
assertNotNull(list);
} catch (IllegalStateException e) {
// the list may or may not be ready at this point
Log.i(TAG, "Background list is not ready");
}
}
@Test
public void testTuneFromProgramList() {
openTuner();
List<RadioManager.ProgramInfo> list;
try {
list = mRadioTuner.getProgramList(null);
assertNotNull(list);
} catch (IllegalStateException e) {
Log.i(TAG, "Background list is not ready, trying to fix it");
boolean success = mRadioTuner.startBackgroundScan();
assertTrue(success);
verify(mCallback, timeout(kFullScanTimeoutMs)).onBackgroundScanComplete();
list = mRadioTuner.getProgramList(null);
assertNotNull(list);
}
if (list.isEmpty()) {
Log.i(TAG, "Program list is empty, can't test tune");
return;
}
ProgramSelector sel = list.get(0).getSelector();
mRadioTuner.tune(sel);
ArgumentCaptor<RadioManager.ProgramInfo> infoc =
ArgumentCaptor.forClass(RadioManager.ProgramInfo.class);
verify(mCallback, timeout(kTuneCallbackTimeoutMs)).onProgramInfoChanged(infoc.capture());
assertEquals(sel, infoc.getValue().getSelector());
}
@Test
public void testForcedAnalog() {
openTuner();
boolean isSupported = true;
boolean isForced;
try {
isForced = mRadioTuner.isAnalogForced();
assertFalse(isForced);
} catch (IllegalStateException ex) {
Log.i(TAG, "Forced analog switch is not supported by this tuner");
isSupported = false;
}
if (isSupported) {
mRadioTuner.setAnalogForced(true);
isForced = mRadioTuner.isAnalogForced();
assertTrue(isForced);
mRadioTuner.setAnalogForced(false);
isForced = mRadioTuner.isAnalogForced();
assertFalse(isForced);
} else {
assertThrows(IllegalStateException.class, () -> mRadioTuner.setAnalogForced(true));
}
}
}