1 /* 2 * Copyright (C) 2022 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.server.broadcastradio.aidl; 18 19 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 20 21 import static com.google.common.truth.Truth.assertWithMessage; 22 23 import static org.junit.Assert.assertThrows; 24 import static org.mockito.ArgumentMatchers.any; 25 import static org.mockito.ArgumentMatchers.anyBoolean; 26 import static org.mockito.ArgumentMatchers.anyInt; 27 import static org.mockito.ArgumentMatchers.eq; 28 import static org.mockito.Mockito.doAnswer; 29 import static org.mockito.Mockito.doThrow; 30 import static org.mockito.Mockito.mock; 31 import static org.mockito.Mockito.never; 32 import static org.mockito.Mockito.timeout; 33 import static org.mockito.Mockito.verify; 34 import static org.mockito.Mockito.when; 35 36 import android.app.compat.CompatChanges; 37 import android.graphics.Bitmap; 38 import android.hardware.broadcastradio.IBroadcastRadio; 39 import android.hardware.broadcastradio.ITunerCallback; 40 import android.hardware.broadcastradio.IdentifierType; 41 import android.hardware.broadcastradio.ProgramFilter; 42 import android.hardware.broadcastradio.ProgramInfo; 43 import android.hardware.broadcastradio.ProgramListChunk; 44 import android.hardware.broadcastradio.Result; 45 import android.hardware.broadcastradio.VendorKeyValue; 46 import android.hardware.radio.ProgramList; 47 import android.hardware.radio.ProgramSelector; 48 import android.hardware.radio.RadioManager; 49 import android.hardware.radio.RadioTuner; 50 import android.os.Binder; 51 import android.os.ParcelableException; 52 import android.os.RemoteException; 53 import android.os.ServiceSpecificException; 54 import android.os.UserHandle; 55 import android.util.ArrayMap; 56 import android.util.ArraySet; 57 58 import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder; 59 import com.android.server.broadcastradio.ExtendedRadioMockitoTestCase; 60 import com.android.server.broadcastradio.RadioServiceUserController; 61 62 import org.junit.After; 63 import org.junit.Before; 64 import org.junit.Test; 65 import org.mockito.Mock; 66 import org.mockito.verification.VerificationWithTimeout; 67 68 import java.util.ArrayList; 69 import java.util.List; 70 import java.util.Map; 71 import java.util.Set; 72 73 /** 74 * Tests for AIDL HAL TunerSession. 75 */ 76 public final class TunerSessionTest extends ExtendedRadioMockitoTestCase { 77 78 private static final int USER_ID_1 = 11; 79 private static final int USER_ID_2 = 12; 80 private static final VerificationWithTimeout CALLBACK_TIMEOUT = 81 timeout(/* millis= */ 200); 82 private static final int SIGNAL_QUALITY = 90; 83 private static final long AM_FM_FREQUENCY_SPACING = 500; 84 private static final long[] AM_FM_FREQUENCY_LIST = {97_500, 98_100, 99_100}; 85 private static final RadioManager.FmBandDescriptor FM_BAND_DESCRIPTOR = 86 new RadioManager.FmBandDescriptor(RadioManager.REGION_ITU_1, RadioManager.BAND_FM, 87 /* lowerLimit= */ 87_500, /* upperLimit= */ 108_000, /* spacing= */ 100, 88 /* stereo= */ false, /* rds= */ false, /* ta= */ false, /* af= */ false, 89 /* ea= */ false); 90 private static final RadioManager.BandConfig FM_BAND_CONFIG = 91 new RadioManager.FmBandConfig(FM_BAND_DESCRIPTOR); 92 private static final int UNSUPPORTED_CONFIG_FLAG = 0; 93 94 private static final ProgramSelector.Identifier TEST_FM_FREQUENCY_ID = 95 new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, 96 /* value= */ 88_500); 97 private static final ProgramSelector.Identifier TEST_RDS_PI_ID = 98 new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_RDS_PI, 99 /* value= */ 15_019); 100 101 private static final RadioManager.ProgramInfo TEST_FM_INFO = AidlTestUtils.makeProgramInfo( 102 AidlTestUtils.makeProgramSelector(ProgramSelector.PROGRAM_TYPE_FM, 103 TEST_FM_FREQUENCY_ID), TEST_FM_FREQUENCY_ID, TEST_FM_FREQUENCY_ID, 104 SIGNAL_QUALITY); 105 private static final RadioManager.ProgramInfo TEST_FM_INFO_MODIFIED = 106 AidlTestUtils.makeProgramInfo(AidlTestUtils.makeProgramSelector( 107 ProgramSelector.PROGRAM_TYPE_FM, TEST_FM_FREQUENCY_ID), TEST_FM_FREQUENCY_ID, 108 TEST_FM_FREQUENCY_ID, /* signalQuality= */ 100); 109 private static final RadioManager.ProgramInfo TEST_RDS_INFO = AidlTestUtils.makeProgramInfo( 110 AidlTestUtils.makeProgramSelector(ProgramSelector.PROGRAM_TYPE_FM, TEST_RDS_PI_ID), 111 TEST_RDS_PI_ID, new ProgramSelector.Identifier( 112 ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, /* value= */ 89_500), 113 SIGNAL_QUALITY); 114 115 // Mocks 116 @Mock 117 private UserHandle mUserHandleMock; 118 @Mock 119 private IBroadcastRadio mBroadcastRadioMock; 120 private android.hardware.radio.ITunerCallback[] mAidlTunerCallbackMocks; 121 122 // RadioModule under test 123 private RadioModule mRadioModule; 124 125 // Objects created by mRadioModule 126 private ITunerCallback mHalTunerCallback; 127 private ProgramInfo mHalCurrentInfo; 128 private final ArrayMap<Integer, Boolean> mHalConfigMap = new ArrayMap<>(); 129 130 private TunerSession[] mTunerSessions; 131 132 @Override initializeSession(StaticMockitoSessionBuilder builder)133 protected void initializeSession(StaticMockitoSessionBuilder builder) { 134 builder.spyStatic(RadioServiceUserController.class).spyStatic(CompatChanges.class) 135 .spyStatic(Binder.class); 136 } 137 138 @Before setup()139 public void setup() throws Exception { 140 when(mUserHandleMock.getIdentifier()).thenReturn(USER_ID_1); 141 doReturn(true).when(() -> CompatChanges.isChangeEnabled( 142 eq(ConversionUtils.RADIO_U_VERSION_REQUIRED), anyInt())); 143 doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser()); 144 doReturn(USER_ID_1).when(() -> RadioServiceUserController.getCurrentUser()); 145 doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle()); 146 147 mRadioModule = new RadioModule(mBroadcastRadioMock, 148 AidlTestUtils.makeDefaultModuleProperties()); 149 150 doAnswer(invocation -> { 151 mHalTunerCallback = (ITunerCallback) invocation.getArguments()[0]; 152 return null; 153 }).when(mBroadcastRadioMock).setTunerCallback(any()); 154 mRadioModule.setInternalHalCallback(); 155 156 doAnswer(invocation -> { 157 android.hardware.broadcastradio.ProgramSelector halSel = 158 (android.hardware.broadcastradio.ProgramSelector) invocation.getArguments()[0]; 159 mHalCurrentInfo = AidlTestUtils.makeHalProgramInfo(halSel, SIGNAL_QUALITY); 160 if (halSel.primaryId.type != IdentifierType.AMFM_FREQUENCY_KHZ) { 161 throw new ServiceSpecificException(Result.NOT_SUPPORTED); 162 } 163 mHalTunerCallback.onCurrentProgramInfoChanged(mHalCurrentInfo); 164 return Result.OK; 165 }).when(mBroadcastRadioMock).tune(any()); 166 167 doAnswer(invocation -> { 168 if ((boolean) invocation.getArguments()[0]) { 169 mHalCurrentInfo.selector.primaryId.value += AM_FM_FREQUENCY_SPACING; 170 } else { 171 mHalCurrentInfo.selector.primaryId.value -= AM_FM_FREQUENCY_SPACING; 172 } 173 mHalCurrentInfo.logicallyTunedTo = mHalCurrentInfo.selector.primaryId; 174 mHalCurrentInfo.physicallyTunedTo = mHalCurrentInfo.selector.primaryId; 175 mHalTunerCallback.onCurrentProgramInfoChanged(mHalCurrentInfo); 176 return Result.OK; 177 }).when(mBroadcastRadioMock).step(anyBoolean()); 178 179 doAnswer(invocation -> { 180 if (mHalCurrentInfo == null) { 181 android.hardware.broadcastradio.ProgramSelector placeHolderSelector = 182 AidlTestUtils.makeHalFmSelector(/* freq= */ 97300); 183 184 mHalTunerCallback.onTuneFailed(Result.TIMEOUT, placeHolderSelector); 185 return Result.OK; 186 } 187 mHalCurrentInfo.selector.primaryId.value = getSeekFrequency( 188 mHalCurrentInfo.selector.primaryId.value, 189 !(boolean) invocation.getArguments()[0]); 190 mHalCurrentInfo.logicallyTunedTo = mHalCurrentInfo.selector.primaryId; 191 mHalCurrentInfo.physicallyTunedTo = mHalCurrentInfo.selector.primaryId; 192 mHalTunerCallback.onCurrentProgramInfoChanged(mHalCurrentInfo); 193 return Result.OK; 194 }).when(mBroadcastRadioMock).seek(anyBoolean(), anyBoolean()); 195 196 when(mBroadcastRadioMock.getImage(anyInt())).thenReturn(null); 197 198 doAnswer(invocation -> { 199 int configFlag = (int) invocation.getArguments()[0]; 200 if (configFlag == UNSUPPORTED_CONFIG_FLAG) { 201 throw new ServiceSpecificException(Result.NOT_SUPPORTED); 202 } 203 return mHalConfigMap.getOrDefault(configFlag, false); 204 }).when(mBroadcastRadioMock).isConfigFlagSet(anyInt()); 205 206 doAnswer(invocation -> { 207 int configFlag = (int) invocation.getArguments()[0]; 208 if (configFlag == UNSUPPORTED_CONFIG_FLAG) { 209 throw new ServiceSpecificException(Result.NOT_SUPPORTED); 210 } 211 mHalConfigMap.put(configFlag, (boolean) invocation.getArguments()[1]); 212 return null; 213 }).when(mBroadcastRadioMock).setConfigFlag(anyInt(), anyBoolean()); 214 } 215 216 @After cleanUp()217 public void cleanUp() { 218 mHalConfigMap.clear(); 219 } 220 221 @Test openSession_withMultipleSessions()222 public void openSession_withMultipleSessions() throws Exception { 223 int numSessions = 3; 224 225 openAidlClients(numSessions); 226 227 for (int index = 0; index < numSessions; index++) { 228 assertWithMessage("Session of index %s close state", index) 229 .that(mTunerSessions[index].isClosed()).isFalse(); 230 } 231 } 232 233 @Test setConfiguration()234 public void setConfiguration() throws Exception { 235 openAidlClients(/* numClients= */ 1); 236 237 mTunerSessions[0].setConfiguration(FM_BAND_CONFIG); 238 239 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onConfigurationChanged(FM_BAND_CONFIG); 240 } 241 242 @Test setConfiguration_forNonCurrentUser_doesNotInvokesCallback()243 public void setConfiguration_forNonCurrentUser_doesNotInvokesCallback() throws Exception { 244 openAidlClients(/* numClients= */ 1); 245 doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser()); 246 247 mTunerSessions[0].setConfiguration(FM_BAND_CONFIG); 248 249 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)) 250 .onConfigurationChanged(FM_BAND_CONFIG); 251 } 252 253 @Test getConfiguration()254 public void getConfiguration() throws Exception { 255 openAidlClients(/* numClients= */ 1); 256 mTunerSessions[0].setConfiguration(FM_BAND_CONFIG); 257 258 RadioManager.BandConfig config = mTunerSessions[0].getConfiguration(); 259 260 assertWithMessage("Session configuration").that(config) 261 .isEqualTo(FM_BAND_CONFIG); 262 } 263 264 @Test setMuted_withUnmuted()265 public void setMuted_withUnmuted() throws Exception { 266 openAidlClients(/* numClients= */ 1); 267 268 mTunerSessions[0].setMuted(/* mute= */ false); 269 270 assertWithMessage("Session mute state after setting unmuted") 271 .that(mTunerSessions[0].isMuted()).isFalse(); 272 } 273 274 @Test setMuted_withMuted()275 public void setMuted_withMuted() throws Exception { 276 openAidlClients(/* numClients= */ 1); 277 278 mTunerSessions[0].setMuted(/* mute= */ true); 279 280 assertWithMessage("Session mute state after setting muted") 281 .that(mTunerSessions[0].isMuted()).isTrue(); 282 } 283 284 @Test close_withOneSession()285 public void close_withOneSession() throws Exception { 286 openAidlClients(/* numClients= */ 1); 287 288 mTunerSessions[0].close(); 289 290 assertWithMessage("Close state of broadcast radio service session") 291 .that(mTunerSessions[0].isClosed()).isTrue(); 292 } 293 294 @Test close_withOnlyOneSession_withMultipleSessions()295 public void close_withOnlyOneSession_withMultipleSessions() throws Exception { 296 int numSessions = 3; 297 openAidlClients(numSessions); 298 int closeIdx = 0; 299 300 mTunerSessions[closeIdx].close(); 301 302 for (int index = 0; index < numSessions; index++) { 303 if (index == closeIdx) { 304 assertWithMessage( 305 "Close state of broadcast radio service session of index %s", index) 306 .that(mTunerSessions[index].isClosed()).isTrue(); 307 } else { 308 assertWithMessage( 309 "Close state of broadcast radio service session of index %s", index) 310 .that(mTunerSessions[index].isClosed()).isFalse(); 311 } 312 } 313 } 314 315 @Test close_withOneSession_withError()316 public void close_withOneSession_withError() throws Exception { 317 openAidlClients(/* numClients= */ 1); 318 int errorCode = RadioTuner.ERROR_SERVER_DIED; 319 320 mTunerSessions[0].close(errorCode); 321 322 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onError(errorCode); 323 assertWithMessage("Close state of broadcast radio service session") 324 .that(mTunerSessions[0].isClosed()).isTrue(); 325 } 326 327 @Test closeSessions_withMultipleSessions_withError()328 public void closeSessions_withMultipleSessions_withError() throws Exception { 329 int numSessions = 3; 330 openAidlClients(numSessions); 331 332 int errorCode = RadioTuner.ERROR_SERVER_DIED; 333 mRadioModule.closeSessions(errorCode); 334 335 for (int index = 0; index < numSessions; index++) { 336 verify(mAidlTunerCallbackMocks[index], CALLBACK_TIMEOUT).onError(errorCode); 337 assertWithMessage("Close state of broadcast radio service session of index %s", index) 338 .that(mTunerSessions[index].isClosed()).isTrue(); 339 } 340 } 341 342 @Test tune_withOneSession()343 public void tune_withOneSession() throws Exception { 344 openAidlClients(/* numClients= */ 1); 345 ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); 346 RadioManager.ProgramInfo tuneInfo = 347 AidlTestUtils.makeProgramInfo(initialSel, SIGNAL_QUALITY); 348 349 mTunerSessions[0].tune(initialSel); 350 351 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo); 352 } 353 354 @Test tune_withLowerSdkVersion()355 public void tune_withLowerSdkVersion() throws Exception { 356 doReturn(false).when(() -> CompatChanges.isChangeEnabled( 357 eq(ConversionUtils.RADIO_U_VERSION_REQUIRED), anyInt())); 358 openAidlClients(/* numClients= */ 1); 359 ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); 360 RadioManager.ProgramInfo tuneInfo = 361 AidlTestUtils.makeProgramInfo(initialSel, SIGNAL_QUALITY); 362 363 mTunerSessions[0].tune(initialSel); 364 365 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo); 366 } 367 368 @Test tune_withMultipleSessions()369 public void tune_withMultipleSessions() throws Exception { 370 int numSessions = 3; 371 openAidlClients(numSessions); 372 ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); 373 RadioManager.ProgramInfo tuneInfo = 374 AidlTestUtils.makeProgramInfo(initialSel, SIGNAL_QUALITY); 375 376 mTunerSessions[0].tune(initialSel); 377 378 for (int index = 0; index < numSessions; index++) { 379 verify(mAidlTunerCallbackMocks[index], CALLBACK_TIMEOUT) 380 .onCurrentProgramInfoChanged(tuneInfo); 381 } 382 } 383 384 @Test tune_withUnsupportedSelector_throwsException()385 public void tune_withUnsupportedSelector_throwsException() throws Exception { 386 ProgramSelector.Identifier dabPrimaryId = 387 new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT, 388 /* value= */ 0xA000000111L); 389 ProgramSelector.Identifier[] dabSecondaryIds = new ProgramSelector.Identifier[]{ 390 new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE, 391 /* value= */ 1337), 392 new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY, 393 /* value= */ 225648)}; 394 ProgramSelector unsupportedSelector = new ProgramSelector(ProgramSelector.PROGRAM_TYPE_DAB, 395 dabPrimaryId, dabSecondaryIds, /* vendorIds= */ null); 396 openAidlClients(/* numClients= */ 1); 397 398 UnsupportedOperationException thrown = assertThrows(UnsupportedOperationException.class, 399 () -> mTunerSessions[0].tune(unsupportedSelector)); 400 401 assertWithMessage("Exception for tuning on unsupported program selector") 402 .that(thrown).hasMessageThat().contains("tune: NOT_SUPPORTED"); 403 } 404 405 @Test tune_withInvalidSelector_throwsIllegalArgumentException()406 public void tune_withInvalidSelector_throwsIllegalArgumentException() throws Exception { 407 openAidlClients(/* numClients= */ 1); 408 ProgramSelector.Identifier invalidDabId = new ProgramSelector.Identifier( 409 ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE, /* value= */ 0x1001); 410 ProgramSelector invalidSel = new ProgramSelector(ProgramSelector.PROGRAM_TYPE_DAB, 411 invalidDabId, new ProgramSelector.Identifier[0], new long[0]); 412 413 IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, 414 () -> mTunerSessions[0].tune(invalidSel)); 415 416 assertWithMessage("Exception for tuning on DAB selector without DAB_SID_EXT primary id") 417 .that(thrown).hasMessageThat().contains("tune: INVALID_ARGUMENTS"); 418 } 419 420 @Test tune_forNonCurrentUser_doesNotTune()421 public void tune_forNonCurrentUser_doesNotTune() throws Exception { 422 openAidlClients(/* numClients= */ 1); 423 doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser()); 424 ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); 425 RadioManager.ProgramInfo tuneInfo = 426 AidlTestUtils.makeProgramInfo(initialSel, SIGNAL_QUALITY); 427 428 mTunerSessions[0].tune(initialSel); 429 430 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)) 431 .onCurrentProgramInfoChanged(tuneInfo); 432 } 433 434 @Test tune_forSystemUser()435 public void tune_forSystemUser() throws Exception { 436 when(mUserHandleMock.getIdentifier()).thenReturn(UserHandle.USER_SYSTEM); 437 doReturn(mUserHandleMock).when(() -> Binder.getCallingUserHandle()); 438 doReturn(true).when(() -> RadioServiceUserController.isCurrentOrSystemUser()); 439 ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); 440 RadioManager.ProgramInfo tuneInfo = 441 AidlTestUtils.makeProgramInfo(initialSel, SIGNAL_QUALITY); 442 openAidlClients(/* numClients= */ 1); 443 444 mTunerSessions[0].tune(initialSel); 445 446 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onCurrentProgramInfoChanged(tuneInfo); 447 } 448 449 @Test tune_withUnknownErrorFromHal_fails()450 public void tune_withUnknownErrorFromHal_fails() throws Exception { 451 openAidlClients(/* numClients= */ 1); 452 ProgramSelector sel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); 453 doThrow(new ServiceSpecificException(Result.UNKNOWN_ERROR)) 454 .when(mBroadcastRadioMock).tune(any()); 455 456 ParcelableException thrown = assertThrows(ParcelableException.class, () -> { 457 mTunerSessions[0].tune(sel); 458 }); 459 460 assertWithMessage("Unknown error HAL exception when tuning") 461 .that(thrown).hasMessageThat().contains("UNKNOWN_ERROR"); 462 } 463 464 @Test step_withDirectionUp()465 public void step_withDirectionUp() throws Exception { 466 long initFreq = AM_FM_FREQUENCY_LIST[1]; 467 ProgramSelector initialSel = AidlTestUtils.makeFmSelector(initFreq); 468 RadioManager.ProgramInfo stepUpInfo = AidlTestUtils.makeProgramInfo( 469 AidlTestUtils.makeFmSelector(initFreq + AM_FM_FREQUENCY_SPACING), 470 SIGNAL_QUALITY); 471 openAidlClients(/* numClients= */ 1); 472 mHalCurrentInfo = AidlTestUtils.makeHalProgramInfo( 473 ConversionUtils.programSelectorToHalProgramSelector(initialSel), SIGNAL_QUALITY); 474 475 mTunerSessions[0].step(/* directionDown= */ false, /* skipSubChannel= */ false); 476 477 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT) 478 .onCurrentProgramInfoChanged(stepUpInfo); 479 } 480 481 @Test step_withDirectionDown()482 public void step_withDirectionDown() throws Exception { 483 long initFreq = AM_FM_FREQUENCY_LIST[1]; 484 ProgramSelector initialSel = AidlTestUtils.makeFmSelector(initFreq); 485 RadioManager.ProgramInfo stepDownInfo = AidlTestUtils.makeProgramInfo( 486 AidlTestUtils.makeFmSelector(initFreq - AM_FM_FREQUENCY_SPACING), 487 SIGNAL_QUALITY); 488 openAidlClients(/* numClients= */ 1); 489 mHalCurrentInfo = AidlTestUtils.makeHalProgramInfo( 490 ConversionUtils.programSelectorToHalProgramSelector(initialSel), SIGNAL_QUALITY); 491 492 mTunerSessions[0].step(/* directionDown= */ true, /* skipSubChannel= */ false); 493 494 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT) 495 .onCurrentProgramInfoChanged(stepDownInfo); 496 } 497 498 @Test step_forNonCurrentUser_doesNotStep()499 public void step_forNonCurrentUser_doesNotStep() throws Exception { 500 long initFreq = AM_FM_FREQUENCY_LIST[1]; 501 ProgramSelector initialSel = AidlTestUtils.makeFmSelector(initFreq); 502 openAidlClients(/* numClients= */ 1); 503 mHalCurrentInfo = AidlTestUtils.makeHalProgramInfo( 504 ConversionUtils.programSelectorToHalProgramSelector(initialSel), SIGNAL_QUALITY); 505 doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser()); 506 507 mTunerSessions[0].step(/* directionDown= */ true, /* skipSubChannel= */ false); 508 509 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)) 510 .onCurrentProgramInfoChanged(any()); 511 } 512 513 @Test step_withHalInInvalidState_fails()514 public void step_withHalInInvalidState_fails() throws Exception { 515 openAidlClients(/* numClients= */ 1); 516 doThrow(new ServiceSpecificException(Result.INVALID_STATE)) 517 .when(mBroadcastRadioMock).step(anyBoolean()); 518 519 IllegalStateException thrown = assertThrows(IllegalStateException.class, () -> { 520 mTunerSessions[0].step(/* directionDown= */ true, /* skipSubChannel= */ false); 521 }); 522 523 assertWithMessage("Exception for stepping when HAL is in invalid state") 524 .that(thrown).hasMessageThat().contains("INVALID_STATE"); 525 } 526 527 @Test seek_withDirectionUp()528 public void seek_withDirectionUp() throws Exception { 529 long initFreq = AM_FM_FREQUENCY_LIST[2]; 530 ProgramSelector initialSel = AidlTestUtils.makeFmSelector(initFreq); 531 RadioManager.ProgramInfo seekUpInfo = AidlTestUtils.makeProgramInfo( 532 AidlTestUtils.makeFmSelector(getSeekFrequency(initFreq, /* seekDown= */ false)), 533 SIGNAL_QUALITY); 534 openAidlClients(/* numClients= */ 1); 535 mHalCurrentInfo = AidlTestUtils.makeHalProgramInfo( 536 ConversionUtils.programSelectorToHalProgramSelector(initialSel), SIGNAL_QUALITY); 537 538 mTunerSessions[0].seek(/* directionDown= */ false, /* skipSubChannel= */ false); 539 540 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT) 541 .onCurrentProgramInfoChanged(seekUpInfo); 542 } 543 544 @Test seek_callsOnTuneFailedWhenTimeout()545 public void seek_callsOnTuneFailedWhenTimeout() throws Exception { 546 int numSessions = 2; 547 openAidlClients(numSessions); 548 549 mTunerSessions[0].seek(/* directionDown= */ false, /* skipSubChannel= */ false); 550 551 for (int index = 0; index < numSessions; index++) { 552 verify(mAidlTunerCallbackMocks[index], CALLBACK_TIMEOUT) 553 .onTuneFailed(eq(RadioTuner.TUNER_RESULT_TIMEOUT), any()); 554 } 555 } 556 557 @Test seek_withDirectionDown()558 public void seek_withDirectionDown() throws Exception { 559 long initFreq = AM_FM_FREQUENCY_LIST[2]; 560 ProgramSelector initialSel = AidlTestUtils.makeFmSelector(initFreq); 561 RadioManager.ProgramInfo seekUpInfo = AidlTestUtils.makeProgramInfo( 562 AidlTestUtils.makeFmSelector(getSeekFrequency(initFreq, /* seekDown= */ true)), 563 SIGNAL_QUALITY); 564 openAidlClients(/* numClients= */ 1); 565 mHalCurrentInfo = AidlTestUtils.makeHalProgramInfo( 566 ConversionUtils.programSelectorToHalProgramSelector(initialSel), SIGNAL_QUALITY); 567 568 mTunerSessions[0].seek(/* directionDown= */ true, /* skipSubChannel= */ false); 569 570 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT) 571 .onCurrentProgramInfoChanged(seekUpInfo); 572 } 573 574 @Test seek_forNonCurrentUser_doesNotSeek()575 public void seek_forNonCurrentUser_doesNotSeek() throws Exception { 576 long initFreq = AM_FM_FREQUENCY_LIST[2]; 577 ProgramSelector initialSel = AidlTestUtils.makeFmSelector(initFreq); 578 RadioManager.ProgramInfo seekUpInfo = AidlTestUtils.makeProgramInfo( 579 AidlTestUtils.makeFmSelector(getSeekFrequency(initFreq, /* seekDown= */ true)), 580 SIGNAL_QUALITY); 581 openAidlClients(/* numClients= */ 1); 582 mHalCurrentInfo = AidlTestUtils.makeHalProgramInfo( 583 ConversionUtils.programSelectorToHalProgramSelector(initialSel), SIGNAL_QUALITY); 584 doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser()); 585 586 mTunerSessions[0].seek(/* directionDown= */ true, /* skipSubChannel= */ false); 587 588 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)) 589 .onCurrentProgramInfoChanged(seekUpInfo); 590 } 591 592 @Test seek_withInternalErrorFromHal_fails()593 public void seek_withInternalErrorFromHal_fails() throws Exception { 594 openAidlClients(/* numClients= */ 1); 595 doThrow(new ServiceSpecificException(Result.INTERNAL_ERROR)) 596 .when(mBroadcastRadioMock).seek(anyBoolean(), anyBoolean()); 597 598 ParcelableException thrown = assertThrows(ParcelableException.class, () -> { 599 mTunerSessions[0].seek(/* directionDown= */ true, /* skipSubChannel= */ false); 600 }); 601 602 assertWithMessage("Internal error HAL exception when seeking") 603 .that(thrown).hasMessageThat().contains("INTERNAL_ERROR"); 604 } 605 606 @Test cancel()607 public void cancel() throws Exception { 608 openAidlClients(/* numClients= */ 1); 609 ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); 610 mTunerSessions[0].tune(initialSel); 611 612 mTunerSessions[0].cancel(); 613 614 verify(mBroadcastRadioMock).cancel(); 615 } 616 617 @Test cancel_forNonCurrentUser_doesNotCancel()618 public void cancel_forNonCurrentUser_doesNotCancel() throws Exception { 619 openAidlClients(/* numClients= */ 1); 620 ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); 621 mTunerSessions[0].tune(initialSel); 622 doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser()); 623 624 mTunerSessions[0].cancel(); 625 626 verify(mBroadcastRadioMock, never()).cancel(); 627 } 628 629 @Test cancel_whenHalThrowsRemoteException_fails()630 public void cancel_whenHalThrowsRemoteException_fails() throws Exception { 631 openAidlClients(/* numClients= */ 1); 632 String exceptionMessage = "HAL service died."; 633 doThrow(new RemoteException(exceptionMessage)).when(mBroadcastRadioMock).cancel(); 634 635 RuntimeException thrown = assertThrows(RuntimeException.class, () -> { 636 mTunerSessions[0].cancel(); 637 }); 638 639 assertWithMessage("Exception for canceling when HAL throws remote exception") 640 .that(thrown).hasMessageThat().contains(exceptionMessage); 641 } 642 643 @Test getImage_withInvalidId_throwsIllegalArgumentException()644 public void getImage_withInvalidId_throwsIllegalArgumentException() throws Exception { 645 openAidlClients(/* numClients= */ 1); 646 int imageId = IBroadcastRadio.INVALID_IMAGE; 647 648 IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> { 649 mTunerSessions[0].getImage(imageId); 650 }); 651 652 assertWithMessage("Get image exception") 653 .that(thrown).hasMessageThat().contains("Image ID is missing"); 654 } 655 656 @Test getImage_withValidId()657 public void getImage_withValidId() throws Exception { 658 openAidlClients(/* numClients= */ 1); 659 int imageId = 1; 660 661 Bitmap imageTest = mTunerSessions[0].getImage(imageId); 662 663 assertWithMessage("Null image").that(imageTest).isEqualTo(null); 664 } 665 666 @Test getImage_whenHalThrowsException_fails()667 public void getImage_whenHalThrowsException_fails() throws Exception { 668 openAidlClients(/* numClients= */ 1); 669 String exceptionMessage = "HAL service died."; 670 when(mBroadcastRadioMock.getImage(anyInt())) 671 .thenThrow(new RemoteException(exceptionMessage)); 672 673 RuntimeException thrown = assertThrows(RuntimeException.class, () -> { 674 mTunerSessions[0].getImage(/* id= */ 1); 675 }); 676 677 assertWithMessage("Exception for getting image when HAL throws remote exception") 678 .that(thrown).hasMessageThat().contains(exceptionMessage); 679 } 680 681 @Test startBackgroundScan()682 public void startBackgroundScan() throws Exception { 683 openAidlClients(/* numClients= */ 1); 684 685 mTunerSessions[0].startBackgroundScan(); 686 687 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onBackgroundScanComplete(); 688 } 689 690 @Test startBackgroundScan_forNonCurrentUser_doesNotInvokesCallback()691 public void startBackgroundScan_forNonCurrentUser_doesNotInvokesCallback() throws Exception { 692 openAidlClients(/* numClients= */ 1); 693 doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser()); 694 695 mTunerSessions[0].startBackgroundScan(); 696 697 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)).onBackgroundScanComplete(); 698 } 699 700 @Test startProgramListUpdates_withEmptyFilter()701 public void startProgramListUpdates_withEmptyFilter() throws Exception { 702 openAidlClients(/* numClients= */ 1); 703 ProgramList.Filter filter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(), 704 /* includeCategories= */ true, /* excludeModifications= */ false); 705 ProgramFilter halFilter = ConversionUtils.filterToHalProgramFilter(filter); 706 List<RadioManager.ProgramInfo> modified = List.of(TEST_FM_INFO, TEST_RDS_INFO); 707 List<ProgramSelector.Identifier> removed = new ArrayList<>(); 708 ProgramListChunk halProgramList = AidlTestUtils.makeHalChunk(/* purge= */ true, 709 /* complete= */ true, modified, removed); 710 ProgramList.Chunk expectedProgramList = 711 AidlTestUtils.makeChunk(/* purge= */ true, /* complete= */ true, modified, removed); 712 713 mTunerSessions[0].startProgramListUpdates(filter); 714 mHalTunerCallback.onProgramListUpdated(halProgramList); 715 716 verify(mBroadcastRadioMock).startProgramListUpdates(halFilter); 717 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT) 718 .onProgramListUpdated(expectedProgramList); 719 } 720 721 @Test startProgramListUpdates_withCallbackCalledForMultipleTimes()722 public void startProgramListUpdates_withCallbackCalledForMultipleTimes() throws Exception { 723 openAidlClients(/* numClients= */ 1); 724 ProgramList.Filter filter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(), 725 /* includeCategories= */ true, /* excludeModifications= */ false); 726 mTunerSessions[0].startProgramListUpdates(filter); 727 mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ true, 728 /* complete= */ true, List.of(TEST_FM_INFO, TEST_RDS_INFO), new ArrayList<>())); 729 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onProgramListUpdated( 730 AidlTestUtils.makeChunk(/* purge= */ true, /* complete= */ true, 731 List.of(TEST_FM_INFO, TEST_RDS_INFO), new ArrayList<>())); 732 733 mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ false, 734 /* complete= */ true, List.of(TEST_FM_INFO_MODIFIED), List.of(TEST_RDS_PI_ID))); 735 736 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onProgramListUpdated( 737 AidlTestUtils.makeChunk(/* purge= */ false, /* complete= */ true, 738 List.of(TEST_FM_INFO_MODIFIED), List.of(TEST_RDS_PI_ID))); 739 } 740 741 @Test startProgramListUpdates_withTheSameFilterForMultipleTimes()742 public void startProgramListUpdates_withTheSameFilterForMultipleTimes() throws Exception { 743 openAidlClients(/* numClients= */ 1); 744 ProgramList.Filter filter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(), 745 /* includeCategories= */ true, /* excludeModifications= */ false); 746 mTunerSessions[0].startProgramListUpdates(filter); 747 mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ true, 748 /* complete= */ true, List.of(TEST_FM_INFO, TEST_RDS_INFO), new ArrayList<>())); 749 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onProgramListUpdated( 750 AidlTestUtils.makeChunk(/* purge= */ true, /* complete= */ true, 751 List.of(TEST_FM_INFO, TEST_RDS_INFO), new ArrayList<>())); 752 mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ false, 753 /* complete= */ true, List.of(TEST_FM_INFO_MODIFIED), List.of(TEST_RDS_PI_ID))); 754 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onProgramListUpdated( 755 AidlTestUtils.makeChunk(/* purge= */ false, /* complete= */ true, 756 List.of(TEST_FM_INFO_MODIFIED), List.of(TEST_RDS_PI_ID))); 757 758 mTunerSessions[0].startProgramListUpdates(filter); 759 760 verify(mBroadcastRadioMock).startProgramListUpdates(any()); 761 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onProgramListUpdated( 762 AidlTestUtils.makeChunk(/* purge= */ true, /* complete= */ true, 763 List.of(TEST_FM_INFO_MODIFIED), new ArrayList<>())); 764 } 765 766 @Test startProgramListUpdates_withNullFilter()767 public void startProgramListUpdates_withNullFilter() throws Exception { 768 openAidlClients(/* numClients= */ 1); 769 770 mTunerSessions[0].startProgramListUpdates(/* filter= */ null); 771 mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ true, 772 /* complete= */ true, List.of(TEST_FM_INFO, TEST_RDS_INFO), new ArrayList<>())); 773 774 verify(mBroadcastRadioMock).startProgramListUpdates(any()); 775 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onProgramListUpdated( 776 AidlTestUtils.makeChunk(/* purge= */ true, /* complete= */ true, 777 List.of(TEST_FM_INFO, TEST_RDS_INFO), new ArrayList<>())); 778 779 mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ false, 780 /* complete= */ true, List.of(TEST_FM_INFO_MODIFIED), List.of(TEST_RDS_PI_ID))); 781 782 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onProgramListUpdated( 783 AidlTestUtils.makeChunk(/* purge= */ false, /* complete= */ true, 784 List.of(TEST_FM_INFO_MODIFIED), List.of(TEST_RDS_PI_ID))); 785 } 786 787 @Test startProgramListUpdates_withIdFilter()788 public void startProgramListUpdates_withIdFilter() throws Exception { 789 openAidlClients(/* numClients= */ 1); 790 ProgramList.Filter idFilter = new ProgramList.Filter(new ArraySet<>(), 791 Set.of(TEST_RDS_PI_ID), /* includeCategories= */ true, 792 /* excludeModifications= */ true); 793 ProgramFilter halFilter = ConversionUtils.filterToHalProgramFilter(idFilter); 794 795 mTunerSessions[0].startProgramListUpdates(idFilter); 796 mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ false, 797 /* complete= */ true, List.of(TEST_RDS_INFO), new ArrayList<>())); 798 799 verify(mBroadcastRadioMock).startProgramListUpdates(halFilter); 800 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onProgramListUpdated( 801 AidlTestUtils.makeChunk(/* purge= */ false, /* complete= */ true, 802 List.of(TEST_RDS_INFO), new ArrayList<>())); 803 804 mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ false, 805 /* complete= */ true, List.of(TEST_FM_INFO), new ArrayList<>())); 806 807 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onProgramListUpdated(any()); 808 } 809 810 @Test startProgramListUpdates_withFilterExcludingModifications()811 public void startProgramListUpdates_withFilterExcludingModifications() throws Exception { 812 openAidlClients(/* numClients= */ 1); 813 ProgramList.Filter filterExcludingModifications = new ProgramList.Filter( 814 Set.of(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY), new ArraySet<>(), 815 /* includeCategories= */ true, /* excludeModifications= */ true); 816 ProgramFilter halFilter = 817 ConversionUtils.filterToHalProgramFilter(filterExcludingModifications); 818 819 mTunerSessions[0].startProgramListUpdates(filterExcludingModifications); 820 mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ false, 821 /* complete= */ true, List.of(TEST_FM_INFO), new ArrayList<>())); 822 823 verify(mBroadcastRadioMock).startProgramListUpdates(halFilter); 824 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onProgramListUpdated( 825 AidlTestUtils.makeChunk(/* purge= */ false, /* complete= */ true, 826 List.of(TEST_FM_INFO), new ArrayList<>())); 827 828 mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ false, 829 /* complete= */ true, List.of(TEST_FM_INFO_MODIFIED), new ArrayList<>())); 830 831 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onProgramListUpdated(any()); 832 } 833 834 @Test startProgramListUpdates_withFilterIncludingModifications()835 public void startProgramListUpdates_withFilterIncludingModifications() throws Exception { 836 openAidlClients(/* numClients= */ 1); 837 ProgramList.Filter filterIncludingModifications = new ProgramList.Filter( 838 Set.of(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY), new ArraySet<>(), 839 /* includeCategories= */ true, /* excludeModifications= */ false); 840 ProgramFilter halFilter = 841 ConversionUtils.filterToHalProgramFilter(filterIncludingModifications); 842 843 mTunerSessions[0].startProgramListUpdates(filterIncludingModifications); 844 mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ false, 845 /* complete= */ true, List.of(TEST_FM_INFO), new ArrayList<>())); 846 847 verify(mBroadcastRadioMock).startProgramListUpdates(halFilter); 848 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onProgramListUpdated( 849 AidlTestUtils.makeChunk(/* purge= */ false, /* complete= */ true, 850 List.of(TEST_FM_INFO), new ArrayList<>())); 851 852 mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ false, 853 /* complete= */ true, List.of(TEST_FM_INFO_MODIFIED), new ArrayList<>())); 854 855 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onProgramListUpdated( 856 AidlTestUtils.makeChunk(/* purge= */ false, /* complete= */ true, 857 List.of(TEST_FM_INFO_MODIFIED), new ArrayList<>())); 858 } 859 860 @Test onProgramListUpdated_afterSessionClosed_doesNotUpdates()861 public void onProgramListUpdated_afterSessionClosed_doesNotUpdates() throws Exception { 862 openAidlClients(/* numClients= */ 1); 863 ProgramList.Filter filter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(), 864 /* includeCategories= */ true, /* excludeModifications= */ false); 865 mTunerSessions[0].startProgramListUpdates(filter); 866 867 mTunerSessions[0].close(); 868 869 verify(mBroadcastRadioMock).stopProgramListUpdates(); 870 871 mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ false, 872 /* complete= */ true, List.of(TEST_FM_INFO), new ArrayList<>())); 873 874 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)).onProgramListUpdated(any()); 875 } 876 877 @Test startProgramListUpdates_forMultipleSessions()878 public void startProgramListUpdates_forMultipleSessions() throws Exception { 879 int numSessions = 3; 880 openAidlClients(numSessions); 881 ProgramList.Filter fmIdFilter = new ProgramList.Filter(new ArraySet<>(), 882 Set.of(TEST_FM_FREQUENCY_ID), /* includeCategories= */ false, 883 /* excludeModifications= */ true); 884 ProgramList.Filter filterExcludingCategories = new ProgramList.Filter(new ArraySet<>(), 885 new ArraySet<>(), /* includeCategories= */ true, 886 /* excludeModifications= */ true); 887 ProgramList.Filter rdsTypeFilter = new ProgramList.Filter( 888 Set.of(ProgramSelector.IDENTIFIER_TYPE_RDS_PI), new ArraySet<>(), 889 /* includeCategories= */ true, /* excludeModifications= */ false); 890 891 mTunerSessions[0].startProgramListUpdates(fmIdFilter); 892 893 ProgramFilter halFilter = ConversionUtils.filterToHalProgramFilter(fmIdFilter); 894 verify(mBroadcastRadioMock).startProgramListUpdates(halFilter); 895 896 mTunerSessions[1].startProgramListUpdates(filterExcludingCategories); 897 898 halFilter.identifiers = new android.hardware.broadcastradio.ProgramIdentifier[]{}; 899 halFilter.includeCategories = true; 900 verify(mBroadcastRadioMock).startProgramListUpdates(halFilter); 901 902 mTunerSessions[2].startProgramListUpdates(rdsTypeFilter); 903 904 halFilter.excludeModifications = false; 905 verify(mBroadcastRadioMock).startProgramListUpdates(halFilter); 906 } 907 908 @Test onProgramListUpdated_forMultipleSessions()909 public void onProgramListUpdated_forMultipleSessions() throws Exception { 910 int numSessions = 3; 911 openAidlClients(numSessions); 912 List<ProgramList.Filter> filters = List.of(new ProgramList.Filter( 913 Set.of(ProgramSelector.IDENTIFIER_TYPE_RDS_PI), new ArraySet<>(), 914 /* includeCategories= */ true, /* excludeModifications= */ false), 915 new ProgramList.Filter(new ArraySet<>(), Set.of(TEST_FM_FREQUENCY_ID), 916 /* includeCategories= */ false, /* excludeModifications= */ true), 917 new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(), 918 /* includeCategories= */ true, /* excludeModifications= */ true)); 919 920 for (int index = 0; index < numSessions; index++) { 921 mTunerSessions[index].startProgramListUpdates(filters.get(index)); 922 } 923 924 mHalTunerCallback.onProgramListUpdated(AidlTestUtils.makeHalChunk(/* purge= */ false, 925 /* complete= */ true, List.of(TEST_FM_INFO, TEST_RDS_INFO), new ArrayList<>())); 926 927 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT) 928 .onProgramListUpdated(AidlTestUtils.makeChunk(/* purge= */ false, 929 /* complete= */ true, List.of(TEST_RDS_INFO), new ArrayList<>())); 930 verify(mAidlTunerCallbackMocks[1], CALLBACK_TIMEOUT) 931 .onProgramListUpdated(AidlTestUtils.makeChunk(/* purge= */ false, 932 /* complete= */ true, List.of(TEST_FM_INFO), new ArrayList<>())); 933 verify(mAidlTunerCallbackMocks[2], CALLBACK_TIMEOUT) 934 .onProgramListUpdated(AidlTestUtils.makeChunk(/* purge= */ false, 935 /* complete= */ true, List.of(TEST_RDS_INFO, TEST_FM_INFO), 936 new ArrayList<>())); 937 } 938 939 @Test startProgramListUpdates_forNonCurrentUser_doesNotStartUpdates()940 public void startProgramListUpdates_forNonCurrentUser_doesNotStartUpdates() throws Exception { 941 openAidlClients(/* numClients= */ 1); 942 ProgramList.Filter filter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(), 943 /* includeCategories= */ true, /* excludeModifications= */ false); 944 doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser()); 945 946 mTunerSessions[0].startProgramListUpdates(filter); 947 948 verify(mBroadcastRadioMock, never()).startProgramListUpdates(any()); 949 } 950 951 @Test startProgramListUpdates_withUnknownErrorFromHal_fails()952 public void startProgramListUpdates_withUnknownErrorFromHal_fails() throws Exception { 953 openAidlClients(/* numClients= */ 1); 954 doThrow(new ServiceSpecificException(Result.UNKNOWN_ERROR)) 955 .when(mBroadcastRadioMock).startProgramListUpdates(any()); 956 957 ParcelableException thrown = assertThrows(ParcelableException.class, () -> { 958 mTunerSessions[0].startProgramListUpdates(/* filter= */ null); 959 }); 960 961 assertWithMessage("Unknown error HAL exception when updating program list") 962 .that(thrown).hasMessageThat().contains("UNKNOWN_ERROR"); 963 } 964 965 @Test stopProgramListUpdates()966 public void stopProgramListUpdates() throws Exception { 967 openAidlClients(/* numClients= */ 1); 968 ProgramList.Filter filter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(), 969 /* includeCategories= */ true, /* excludeModifications= */ false); 970 mTunerSessions[0].startProgramListUpdates(filter); 971 972 mTunerSessions[0].stopProgramListUpdates(); 973 974 verify(mBroadcastRadioMock).stopProgramListUpdates(); 975 } 976 977 @Test stopProgramListUpdates_forNonCurrentUser_doesNotStopUpdates()978 public void stopProgramListUpdates_forNonCurrentUser_doesNotStopUpdates() throws Exception { 979 openAidlClients(/* numClients= */ 1); 980 ProgramList.Filter filter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(), 981 /* includeCategories= */ true, /* excludeModifications= */ false); 982 mTunerSessions[0].startProgramListUpdates(filter); 983 doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser()); 984 985 mTunerSessions[0].stopProgramListUpdates(); 986 987 verify(mBroadcastRadioMock, never()).stopProgramListUpdates(); 988 } 989 990 @Test isConfigFlagSupported_withUnsupportedFlag_returnsFalse()991 public void isConfigFlagSupported_withUnsupportedFlag_returnsFalse() throws Exception { 992 openAidlClients(/* numClients= */ 1); 993 int flag = UNSUPPORTED_CONFIG_FLAG; 994 995 boolean isSupported = mTunerSessions[0].isConfigFlagSupported(flag); 996 997 verify(mBroadcastRadioMock).isConfigFlagSet(flag); 998 assertWithMessage("Config flag %s is supported", flag).that(isSupported).isFalse(); 999 } 1000 1001 @Test isConfigFlagSupported_withSupportedFlag_returnsTrue()1002 public void isConfigFlagSupported_withSupportedFlag_returnsTrue() throws Exception { 1003 openAidlClients(/* numClients= */ 1); 1004 int flag = UNSUPPORTED_CONFIG_FLAG + 1; 1005 1006 boolean isSupported = mTunerSessions[0].isConfigFlagSupported(flag); 1007 1008 verify(mBroadcastRadioMock).isConfigFlagSet(flag); 1009 assertWithMessage("Config flag %s is supported", flag).that(isSupported).isTrue(); 1010 } 1011 1012 @Test setConfigFlag_withUnsupportedFlag_throwsRuntimeException()1013 public void setConfigFlag_withUnsupportedFlag_throwsRuntimeException() throws Exception { 1014 openAidlClients(/* numClients= */ 1); 1015 int flag = UNSUPPORTED_CONFIG_FLAG; 1016 1017 RuntimeException thrown = assertThrows(RuntimeException.class, () -> { 1018 mTunerSessions[0].setConfigFlag(flag, /* value= */ true); 1019 }); 1020 1021 assertWithMessage("Exception for setting unsupported flag %s", flag) 1022 .that(thrown).hasMessageThat().contains("setConfigFlag: NOT_SUPPORTED"); 1023 } 1024 1025 @Test setConfigFlag_withFlagSetToTrue()1026 public void setConfigFlag_withFlagSetToTrue() throws Exception { 1027 openAidlClients(/* numClients= */ 1); 1028 int flag = UNSUPPORTED_CONFIG_FLAG + 1; 1029 1030 mTunerSessions[0].setConfigFlag(flag, /* value= */ true); 1031 1032 verify(mBroadcastRadioMock).setConfigFlag(flag, /* value= */ true); 1033 } 1034 1035 @Test setConfigFlag_withFlagSetToFalse()1036 public void setConfigFlag_withFlagSetToFalse() throws Exception { 1037 openAidlClients(/* numClients= */ 1); 1038 int flag = UNSUPPORTED_CONFIG_FLAG + 1; 1039 1040 mTunerSessions[0].setConfigFlag(flag, /* value= */ false); 1041 1042 verify(mBroadcastRadioMock).setConfigFlag(flag, /* value= */ false); 1043 } 1044 1045 @Test setConfigFlag_forNonCurrentUser_doesNotSetConfigFlag()1046 public void setConfigFlag_forNonCurrentUser_doesNotSetConfigFlag() throws Exception { 1047 openAidlClients(/* numClients= */ 1); 1048 int flag = UNSUPPORTED_CONFIG_FLAG + 1; 1049 doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser()); 1050 1051 mTunerSessions[0].setConfigFlag(flag, /* value= */ true); 1052 1053 verify(mBroadcastRadioMock, never()).setConfigFlag(flag, /* value= */ true); 1054 } 1055 1056 @Test isConfigFlagSet_withUnsupportedFlag_throwsRuntimeException()1057 public void isConfigFlagSet_withUnsupportedFlag_throwsRuntimeException() 1058 throws Exception { 1059 openAidlClients(/* numClients= */ 1); 1060 int flag = UNSUPPORTED_CONFIG_FLAG; 1061 1062 RuntimeException thrown = assertThrows(RuntimeException.class, () -> { 1063 mTunerSessions[0].isConfigFlagSet(flag); 1064 }); 1065 1066 assertWithMessage("Exception for checking if unsupported flag %s is set", flag) 1067 .that(thrown).hasMessageThat().contains("isConfigFlagSet: NOT_SUPPORTED"); 1068 } 1069 1070 @Test isConfigFlagSet_withSupportedFlag()1071 public void isConfigFlagSet_withSupportedFlag() throws Exception { 1072 openAidlClients(/* numClients= */ 1); 1073 int flag = UNSUPPORTED_CONFIG_FLAG + 1; 1074 boolean expectedConfigFlagValue = true; 1075 mTunerSessions[0].setConfigFlag(flag, /* value= */ expectedConfigFlagValue); 1076 1077 boolean isSet = mTunerSessions[0].isConfigFlagSet(flag); 1078 1079 assertWithMessage("Config flag %s is set", flag) 1080 .that(isSet).isEqualTo(expectedConfigFlagValue); 1081 } 1082 1083 @Test isConfigFlagSet_whenHalThrowsRemoteException_fails()1084 public void isConfigFlagSet_whenHalThrowsRemoteException_fails() throws Exception { 1085 openAidlClients(/* numClients= */ 1); 1086 int flag = UNSUPPORTED_CONFIG_FLAG + 1; 1087 doThrow(new RemoteException()).when(mBroadcastRadioMock).isConfigFlagSet(anyInt()); 1088 1089 RuntimeException thrown = assertThrows(RuntimeException.class, () -> { 1090 mTunerSessions[0].isConfigFlagSet(flag); 1091 }); 1092 1093 assertWithMessage("Exception for checking config flag when HAL throws remote exception") 1094 .that(thrown).hasMessageThat().contains("Failed to check flag"); 1095 } 1096 1097 @Test setParameters_withMockParameters()1098 public void setParameters_withMockParameters() throws Exception { 1099 openAidlClients(/* numClients= */ 1); 1100 Map<String, String> parametersSet = Map.of("mockParam1", "mockValue1", 1101 "mockParam2", "mockValue2"); 1102 1103 mTunerSessions[0].setParameters(parametersSet); 1104 1105 verify(mBroadcastRadioMock).setParameters( 1106 ConversionUtils.vendorInfoToHalVendorKeyValues(parametersSet)); 1107 } 1108 1109 @Test setParameters_forNonCurrentUser_doesNotSetParameters()1110 public void setParameters_forNonCurrentUser_doesNotSetParameters() throws Exception { 1111 openAidlClients(/* numClients= */ 1); 1112 Map<String, String> parametersSet = Map.of("mockParam1", "mockValue1", 1113 "mockParam2", "mockValue2"); 1114 doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser()); 1115 1116 mTunerSessions[0].setParameters(parametersSet); 1117 1118 verify(mBroadcastRadioMock, never()).setParameters(any()); 1119 } 1120 1121 @Test setParameters_whenHalThrowsRemoteException_fails()1122 public void setParameters_whenHalThrowsRemoteException_fails() throws Exception { 1123 openAidlClients(/* numClients= */ 1); 1124 Map<String, String> parametersSet = Map.of("mockParam1", "mockValue1", 1125 "mockParam2", "mockValue2"); 1126 String exceptionMessage = "HAL service died."; 1127 when(mBroadcastRadioMock.setParameters(any())) 1128 .thenThrow(new RemoteException(exceptionMessage)); 1129 1130 RuntimeException thrown = assertThrows(RuntimeException.class, () -> { 1131 mTunerSessions[0].setParameters(parametersSet); 1132 }); 1133 1134 assertWithMessage("Exception for setting parameters when HAL throws remote exception") 1135 .that(thrown).hasMessageThat().contains(exceptionMessage); 1136 } 1137 1138 @Test getParameters_withMockKeys()1139 public void getParameters_withMockKeys() throws Exception { 1140 openAidlClients(/* numClients= */ 1); 1141 List<String> parameterKeys = List.of("mockKey1", "mockKey2"); 1142 1143 mTunerSessions[0].getParameters(parameterKeys); 1144 1145 verify(mBroadcastRadioMock).getParameters(parameterKeys.toArray(new String[0])); 1146 } 1147 1148 @Test getParameters_whenServiceThrowsRemoteException_fails()1149 public void getParameters_whenServiceThrowsRemoteException_fails() throws Exception { 1150 openAidlClients(/* numClients= */ 1); 1151 List<String> parameterKeys = List.of("mockKey1", "mockKey2"); 1152 String exceptionMessage = "HAL service died."; 1153 when(mBroadcastRadioMock.getParameters(any())) 1154 .thenThrow(new RemoteException(exceptionMessage)); 1155 1156 RuntimeException thrown = assertThrows(RuntimeException.class, () -> { 1157 mTunerSessions[0].getParameters(parameterKeys); 1158 }); 1159 1160 assertWithMessage("Exception for getting parameters when HAL throws remote exception") 1161 .that(thrown).hasMessageThat().contains(exceptionMessage); 1162 } 1163 1164 @Test onCurrentProgramInfoChanged_withNoncurrentUser_doesNotInvokeCallback()1165 public void onCurrentProgramInfoChanged_withNoncurrentUser_doesNotInvokeCallback() 1166 throws Exception { 1167 openAidlClients(1); 1168 doReturn(USER_ID_2).when(() -> RadioServiceUserController.getCurrentUser()); 1169 1170 mHalTunerCallback.onCurrentProgramInfoChanged(AidlTestUtils.makeHalProgramInfo( 1171 AidlTestUtils.makeHalFmSelector(AM_FM_FREQUENCY_LIST[1]), SIGNAL_QUALITY)); 1172 1173 verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)) 1174 .onCurrentProgramInfoChanged(any()); 1175 } 1176 1177 @Test onTuneFailed_forTunerCallback()1178 public void onTuneFailed_forTunerCallback() throws Exception { 1179 int numSessions = 3; 1180 openAidlClients(numSessions); 1181 android.hardware.broadcastradio.ProgramSelector halSel = AidlTestUtils.makeHalFmSelector( 1182 AM_FM_FREQUENCY_LIST[1]); 1183 ProgramSelector sel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]); 1184 1185 mHalTunerCallback.onTuneFailed(Result.CANCELED, halSel); 1186 1187 for (int index = 0; index < numSessions; index++) { 1188 verify(mAidlTunerCallbackMocks[index], CALLBACK_TIMEOUT) 1189 .onTuneFailed(RadioTuner.TUNER_RESULT_CANCELED, sel); 1190 } 1191 } 1192 1193 @Test onAntennaStateChange_forTunerCallback()1194 public void onAntennaStateChange_forTunerCallback() throws Exception { 1195 int numSessions = 3; 1196 openAidlClients(numSessions); 1197 1198 mHalTunerCallback.onAntennaStateChange(/* connected= */ false); 1199 1200 for (int index = 0; index < numSessions; index++) { 1201 verify(mAidlTunerCallbackMocks[index], CALLBACK_TIMEOUT) 1202 .onAntennaState(/* connected= */ false); 1203 } 1204 } 1205 1206 @Test onConfigFlagUpdated_forTunerCallback()1207 public void onConfigFlagUpdated_forTunerCallback() throws Exception { 1208 int numSessions = 3; 1209 openAidlClients(numSessions); 1210 int flag = UNSUPPORTED_CONFIG_FLAG + 1; 1211 boolean configFlagValue = true; 1212 1213 mHalTunerCallback.onConfigFlagUpdated(flag, configFlagValue); 1214 1215 for (int index = 0; index < numSessions; index++) { 1216 verify(mAidlTunerCallbackMocks[index], CALLBACK_TIMEOUT) 1217 .onConfigFlagUpdated(flag, configFlagValue); 1218 } 1219 } 1220 1221 @Test onParametersUpdated_forTunerCallback()1222 public void onParametersUpdated_forTunerCallback() throws Exception { 1223 int numSessions = 3; 1224 openAidlClients(numSessions); 1225 VendorKeyValue[] parametersUpdates = { 1226 AidlTestUtils.makeVendorKeyValue("com.vendor.parameter1", "value1")}; 1227 Map<String, String> parametersExpected = Map.of("com.vendor.parameter1", "value1"); 1228 1229 mHalTunerCallback.onParametersUpdated(parametersUpdates); 1230 1231 for (int index = 0; index < numSessions; index++) { 1232 verify(mAidlTunerCallbackMocks[index], CALLBACK_TIMEOUT) 1233 .onParametersUpdated(parametersExpected); 1234 } 1235 } 1236 openAidlClients(int numClients)1237 private void openAidlClients(int numClients) throws Exception { 1238 mAidlTunerCallbackMocks = new android.hardware.radio.ITunerCallback[numClients]; 1239 mTunerSessions = new TunerSession[numClients]; 1240 for (int index = 0; index < numClients; index++) { 1241 mAidlTunerCallbackMocks[index] = mock(android.hardware.radio.ITunerCallback.class); 1242 mTunerSessions[index] = mRadioModule.openSession(mAidlTunerCallbackMocks[index]); 1243 } 1244 } 1245 getSeekFrequency(long currentFrequency, boolean seekDown)1246 private long getSeekFrequency(long currentFrequency, boolean seekDown) { 1247 long seekFrequency; 1248 if (seekDown) { 1249 seekFrequency = AM_FM_FREQUENCY_LIST[AM_FM_FREQUENCY_LIST.length - 1]; 1250 for (int i = AM_FM_FREQUENCY_LIST.length - 1; i >= 0; i--) { 1251 if (AM_FM_FREQUENCY_LIST[i] < currentFrequency) { 1252 seekFrequency = AM_FM_FREQUENCY_LIST[i]; 1253 break; 1254 } 1255 } 1256 } else { 1257 seekFrequency = AM_FM_FREQUENCY_LIST[0]; 1258 for (int index = 0; index < AM_FM_FREQUENCY_LIST.length; index++) { 1259 if (AM_FM_FREQUENCY_LIST[index] > currentFrequency) { 1260 seekFrequency = AM_FM_FREQUENCY_LIST[index]; 1261 break; 1262 } 1263 } 1264 } 1265 return seekFrequency; 1266 } 1267 } 1268