1/* 2 * Copyright (C) 2017 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 */ 16package android.hardware.radio.tests.functional; 17 18import android.Manifest; 19import android.content.Context; 20import android.content.pm.PackageManager; 21import android.hardware.radio.ProgramSelector; 22import android.hardware.radio.RadioManager; 23import android.hardware.radio.RadioTuner; 24import android.support.test.InstrumentationRegistry; 25import android.support.test.runner.AndroidJUnit4; 26import android.test.suitebuilder.annotation.MediumTest; 27import android.util.Log; 28 29import java.lang.reflect.Constructor; 30import java.util.ArrayList; 31import java.util.HashMap; 32import java.util.List; 33import java.util.Map; 34 35import org.junit.After; 36import org.junit.Before; 37import org.junit.Test; 38import org.junit.runner.RunWith; 39import org.mockito.ArgumentCaptor; 40import org.mockito.Mock; 41import org.mockito.Mockito; 42import org.mockito.MockitoAnnotations; 43 44import static org.junit.Assert.*; 45import static org.junit.Assume.*; 46import static org.mockito.Matchers.any; 47import static org.mockito.Matchers.anyInt; 48import static org.mockito.Mockito.after; 49import static org.mockito.Mockito.atLeast; 50import static org.mockito.Mockito.atMost; 51import static org.mockito.Mockito.never; 52import static org.mockito.Mockito.timeout; 53import static org.mockito.Mockito.times; 54import static org.mockito.Mockito.verify; 55import static org.mockito.Mockito.verifyNoMoreInteractions; 56import static org.testng.Assert.assertThrows; 57 58/** 59 * A test for broadcast radio API. 60 */ 61@RunWith(AndroidJUnit4.class) 62@MediumTest 63public class RadioTunerTest { 64 private static final String TAG = "BroadcastRadioTests.RadioTuner"; 65 66 public final Context mContext = InstrumentationRegistry.getContext(); 67 68 private final int kConfigCallbackTimeoutMs = 10000; 69 private final int kCancelTimeoutMs = 1000; 70 private final int kTuneCallbackTimeoutMs = 30000; 71 private final int kFullScanTimeoutMs = 60000; 72 73 private RadioManager mRadioManager; 74 private RadioTuner mRadioTuner; 75 private RadioManager.ModuleProperties mModule; 76 private final List<RadioManager.ModuleProperties> mModules = new ArrayList<>(); 77 @Mock private RadioTuner.Callback mCallback; 78 79 RadioManager.AmBandDescriptor mAmBandDescriptor; 80 RadioManager.FmBandDescriptor mFmBandDescriptor; 81 82 RadioManager.BandConfig mAmBandConfig; 83 RadioManager.BandConfig mFmBandConfig; 84 85 @Before 86 public void setup() { 87 MockitoAnnotations.initMocks(this); 88 89 // check if radio is supported and skip the test if it's not 90 PackageManager packageManager = mContext.getPackageManager(); 91 boolean isRadioSupported = packageManager.hasSystemFeature( 92 PackageManager.FEATURE_BROADCAST_RADIO); 93 assumeTrue(isRadioSupported); 94 95 // Check radio access permission 96 int res = mContext.checkCallingOrSelfPermission(Manifest.permission.ACCESS_BROADCAST_RADIO); 97 assertEquals("ACCESS_BROADCAST_RADIO permission not granted", 98 PackageManager.PERMISSION_GRANTED, res); 99 100 mRadioManager = (RadioManager)mContext.getSystemService(Context.RADIO_SERVICE); 101 assertNotNull(mRadioManager); 102 103 int status = mRadioManager.listModules(mModules); 104 assertEquals(RadioManager.STATUS_OK, status); 105 assertFalse(mModules.isEmpty()); 106 } 107 108 @After 109 public void tearDown() { 110 mRadioManager = null; 111 mModules.clear(); 112 if (mRadioTuner != null) { 113 mRadioTuner.close(); 114 mRadioTuner = null; 115 } 116 resetCallback(); 117 } 118 119 private void openTuner() { 120 openTuner(true); 121 } 122 123 private void resetCallback() { 124 verify(mCallback, atLeast(0)).onMetadataChanged(any()); 125 verify(mCallback, atLeast(0)).onProgramInfoChanged(any()); 126 verify(mCallback, atLeast(0)).onProgramListChanged(); 127 verifyNoMoreInteractions(mCallback); 128 Mockito.reset(mCallback); 129 } 130 131 private void openTuner(boolean withAudio) { 132 assertNull(mRadioTuner); 133 134 // find FM band and build its config 135 mModule = mModules.get(0); 136 137 for (RadioManager.BandDescriptor band : mModule.getBands()) { 138 Log.d(TAG, "Band: " + band); 139 int bandType = band.getType(); 140 if (bandType == RadioManager.BAND_AM || bandType == RadioManager.BAND_AM_HD) { 141 mAmBandDescriptor = (RadioManager.AmBandDescriptor)band; 142 } 143 if (bandType == RadioManager.BAND_FM || bandType == RadioManager.BAND_FM_HD) { 144 mFmBandDescriptor = (RadioManager.FmBandDescriptor)band; 145 } 146 } 147 assertNotNull(mAmBandDescriptor); 148 assertNotNull(mFmBandDescriptor); 149 mAmBandConfig = new RadioManager.AmBandConfig.Builder(mAmBandDescriptor).build(); 150 mFmBandConfig = new RadioManager.FmBandConfig.Builder(mFmBandDescriptor).build(); 151 152 mRadioTuner = mRadioManager.openTuner(mModule.getId(), 153 mFmBandConfig, withAudio, mCallback, null); 154 if (!withAudio) { 155 // non-audio sessions might not be supported - if so, then skip the test 156 assumeNotNull(mRadioTuner); 157 } 158 assertNotNull(mRadioTuner); 159 verify(mCallback, timeout(kConfigCallbackTimeoutMs)).onConfigurationChanged(any()); 160 resetCallback(); 161 162 boolean isAntennaConnected = mRadioTuner.isAntennaConnected(); 163 assertTrue(isAntennaConnected); 164 } 165 166 @Test 167 public void testOpenTuner() { 168 openTuner(); 169 } 170 171 @Test 172 public void testReopenTuner() throws Throwable { 173 openTuner(); 174 mRadioTuner.close(); 175 mRadioTuner = null; 176 Thread.sleep(100); // TODO(b/36122635): force reopen 177 openTuner(); 178 } 179 180 @Test 181 public void testDoubleClose() { 182 openTuner(); 183 mRadioTuner.close(); 184 mRadioTuner.close(); 185 } 186 187 @Test 188 public void testUseAfterClose() { 189 openTuner(); 190 mRadioTuner.close(); 191 int ret = mRadioTuner.cancel(); 192 assertEquals(RadioManager.STATUS_INVALID_OPERATION, ret); 193 } 194 195 @Test 196 public void testSetAndGetConfiguration() { 197 openTuner(); 198 199 // set 200 int ret = mRadioTuner.setConfiguration(mAmBandConfig); 201 assertEquals(RadioManager.STATUS_OK, ret); 202 verify(mCallback, timeout(kConfigCallbackTimeoutMs)).onConfigurationChanged(any()); 203 204 // get 205 RadioManager.BandConfig[] config = new RadioManager.BandConfig[1]; 206 ret = mRadioTuner.getConfiguration(config); 207 assertEquals(RadioManager.STATUS_OK, ret); 208 209 assertEquals(mAmBandConfig, config[0]); 210 } 211 212 @Test 213 public void testSetBadConfiguration() throws Throwable { 214 openTuner(); 215 216 // set null config 217 int ret = mRadioTuner.setConfiguration(null); 218 assertEquals(RadioManager.STATUS_BAD_VALUE, ret); 219 verify(mCallback, never()).onConfigurationChanged(any()); 220 221 // setting good config should recover 222 ret = mRadioTuner.setConfiguration(mAmBandConfig); 223 assertEquals(RadioManager.STATUS_OK, ret); 224 verify(mCallback, timeout(kConfigCallbackTimeoutMs)).onConfigurationChanged(any()); 225 } 226 227 @Test 228 public void testMute() { 229 openTuner(); 230 231 boolean isMuted = mRadioTuner.getMute(); 232 assertFalse(isMuted); 233 234 int ret = mRadioTuner.setMute(true); 235 assertEquals(RadioManager.STATUS_OK, ret); 236 isMuted = mRadioTuner.getMute(); 237 assertTrue(isMuted); 238 239 ret = mRadioTuner.setMute(false); 240 assertEquals(RadioManager.STATUS_OK, ret); 241 isMuted = mRadioTuner.getMute(); 242 assertFalse(isMuted); 243 } 244 245 @Test 246 public void testMuteNoAudio() { 247 openTuner(false); 248 249 int ret = mRadioTuner.setMute(false); 250 assertEquals(RadioManager.STATUS_ERROR, ret); 251 252 boolean isMuted = mRadioTuner.getMute(); 253 assertTrue(isMuted); 254 } 255 256 @Test 257 public void testStep() { 258 openTuner(); 259 260 int ret = mRadioTuner.step(RadioTuner.DIRECTION_DOWN, true); 261 assertEquals(RadioManager.STATUS_OK, ret); 262 verify(mCallback, timeout(kTuneCallbackTimeoutMs)).onProgramInfoChanged(any()); 263 264 resetCallback(); 265 266 ret = mRadioTuner.step(RadioTuner.DIRECTION_UP, false); 267 assertEquals(RadioManager.STATUS_OK, ret); 268 verify(mCallback, timeout(kTuneCallbackTimeoutMs)).onProgramInfoChanged(any()); 269 } 270 271 @Test 272 public void testStepLoop() { 273 openTuner(); 274 275 for (int i = 0; i < 10; i++) { 276 Log.d(TAG, "step loop iteration " + (i + 1)); 277 278 int ret = mRadioTuner.step(RadioTuner.DIRECTION_DOWN, true); 279 assertEquals(RadioManager.STATUS_OK, ret); 280 verify(mCallback, timeout(kTuneCallbackTimeoutMs)).onProgramInfoChanged(any()); 281 282 resetCallback(); 283 } 284 } 285 286 @Test 287 public void testTuneAndGetPI() { 288 openTuner(); 289 290 int channel = mFmBandConfig.getLowerLimit() + mFmBandConfig.getSpacing(); 291 292 // test tune 293 int ret = mRadioTuner.tune(channel, 0); 294 assertEquals(RadioManager.STATUS_OK, ret); 295 ArgumentCaptor<RadioManager.ProgramInfo> infoc = 296 ArgumentCaptor.forClass(RadioManager.ProgramInfo.class); 297 verify(mCallback, timeout(kTuneCallbackTimeoutMs)) 298 .onProgramInfoChanged(infoc.capture()); 299 assertEquals(channel, infoc.getValue().getChannel()); 300 301 // test getProgramInformation 302 RadioManager.ProgramInfo[] info = new RadioManager.ProgramInfo[1]; 303 ret = mRadioTuner.getProgramInformation(info); 304 assertEquals(RadioManager.STATUS_OK, ret); 305 assertNotNull(info[0]); 306 assertEquals(channel, info[0].getChannel()); 307 Log.d(TAG, "PI: " + info[0].toString()); 308 } 309 310 @Test 311 public void testDummyCancel() { 312 openTuner(); 313 314 int ret = mRadioTuner.cancel(); 315 assertEquals(RadioManager.STATUS_OK, ret); 316 } 317 318 @Test 319 public void testLateCancel() { 320 openTuner(); 321 322 int ret = mRadioTuner.step(RadioTuner.DIRECTION_DOWN, false); 323 assertEquals(RadioManager.STATUS_OK, ret); 324 verify(mCallback, timeout(kTuneCallbackTimeoutMs)).onProgramInfoChanged(any()); 325 326 int cancelRet = mRadioTuner.cancel(); 327 assertEquals(RadioManager.STATUS_OK, cancelRet); 328 } 329 330 @Test 331 public void testScanAndCancel() { 332 openTuner(); 333 334 /* There is a possible race condition between scan and cancel commands - the scan may finish 335 * before cancel command is issued. Thus we accept both outcomes in this test. 336 */ 337 int scanRet = mRadioTuner.scan(RadioTuner.DIRECTION_DOWN, true); 338 int cancelRet = mRadioTuner.cancel(); 339 340 assertEquals(RadioManager.STATUS_OK, scanRet); 341 assertEquals(RadioManager.STATUS_OK, cancelRet); 342 343 verify(mCallback, after(kCancelTimeoutMs).atMost(1)).onError(RadioTuner.ERROR_CANCELLED); 344 verify(mCallback, atMost(1)).onProgramInfoChanged(any()); 345 } 346 347 @Test 348 public void testStartBackgroundScan() { 349 openTuner(); 350 351 boolean ret = mRadioTuner.startBackgroundScan(); 352 boolean isSupported = mModule.isBackgroundScanningSupported(); 353 assertEquals(isSupported, ret); 354 } 355 356 @Test 357 public void testGetProgramList() { 358 openTuner(); 359 360 try { 361 Map<String, String> filter = new HashMap<>(); 362 filter.put("com.google.dummy", "dummy"); 363 List<RadioManager.ProgramInfo> list = mRadioTuner.getProgramList(filter); 364 assertNotNull(list); 365 } catch (IllegalStateException e) { 366 // the list may or may not be ready at this point 367 Log.i(TAG, "Background list is not ready"); 368 } 369 } 370 371 @Test 372 public void testTuneFromProgramList() { 373 openTuner(); 374 375 List<RadioManager.ProgramInfo> list; 376 377 try { 378 list = mRadioTuner.getProgramList(null); 379 assertNotNull(list); 380 } catch (IllegalStateException e) { 381 Log.i(TAG, "Background list is not ready, trying to fix it"); 382 383 boolean success = mRadioTuner.startBackgroundScan(); 384 assertTrue(success); 385 verify(mCallback, timeout(kFullScanTimeoutMs)).onBackgroundScanComplete(); 386 387 list = mRadioTuner.getProgramList(null); 388 assertNotNull(list); 389 } 390 391 if (list.isEmpty()) { 392 Log.i(TAG, "Program list is empty, can't test tune"); 393 return; 394 } 395 396 ProgramSelector sel = list.get(0).getSelector(); 397 mRadioTuner.tune(sel); 398 ArgumentCaptor<RadioManager.ProgramInfo> infoc = 399 ArgumentCaptor.forClass(RadioManager.ProgramInfo.class); 400 verify(mCallback, timeout(kTuneCallbackTimeoutMs)).onProgramInfoChanged(infoc.capture()); 401 assertEquals(sel, infoc.getValue().getSelector()); 402 } 403 404 @Test 405 public void testForcedAnalog() { 406 openTuner(); 407 408 boolean isSupported = true; 409 boolean isForced; 410 try { 411 isForced = mRadioTuner.isAnalogForced(); 412 assertFalse(isForced); 413 } catch (IllegalStateException ex) { 414 Log.i(TAG, "Forced analog switch is not supported by this tuner"); 415 isSupported = false; 416 } 417 418 if (isSupported) { 419 mRadioTuner.setAnalogForced(true); 420 isForced = mRadioTuner.isAnalogForced(); 421 assertTrue(isForced); 422 423 mRadioTuner.setAnalogForced(false); 424 isForced = mRadioTuner.isAnalogForced(); 425 assertFalse(isForced); 426 } else { 427 assertThrows(IllegalStateException.class, () -> mRadioTuner.setAnalogForced(true)); 428 } 429 } 430} 431