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