Add unit tests for HIDL broadcast radio service

Bug: 258034192
Test: atest com.android.server.broadcastradio
Change-Id: I76cd338d20be7fb5effb137ff9f0601927105056
This commit is contained in:
Weilin Xu
2022-11-09 08:35:53 +00:00
parent 65436d48fb
commit c58f580670
3 changed files with 236 additions and 3 deletions

View File

@@ -122,7 +122,7 @@ public final class BroadcastRadioServiceImplTest extends ExtendedRadioMockitoTes
createBroadcastRadioService();
ITuner session = mBroadcastRadioService.openSession(FM_RADIO_MODULE_ID,
/*legacyConfig= */ null, /* withAudio= */ true, mTunerCallbackMock);
/* legacyConfig= */ null, /* withAudio= */ true, mTunerCallbackMock);
assertWithMessage("Session opened in FM radio module")
.that(session).isEqualTo(mFmTunerSessionMock);
@@ -133,7 +133,7 @@ public final class BroadcastRadioServiceImplTest extends ExtendedRadioMockitoTes
createBroadcastRadioService();
ITuner session = mBroadcastRadioService.openSession(DAB_RADIO_MODULE_ID + 1,
/*legacyConfig= */ null, /* withAudio= */ true, mTunerCallbackMock);
/* legacyConfig= */ null, /* withAudio= */ true, mTunerCallbackMock);
assertWithMessage("Session opened with id not found").that(session).isNull();
}

View File

@@ -0,0 +1,220 @@
/*
* Copyright (C) 2022 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.broadcastradio.hal2;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.google.common.truth.Truth.assertWithMessage;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.hardware.broadcastradio.V2_0.IBroadcastRadio;
import android.hardware.radio.Announcement;
import android.hardware.radio.IAnnouncementListener;
import android.hardware.radio.ICloseHandle;
import android.hardware.radio.ITuner;
import android.hardware.radio.ITunerCallback;
import android.hardware.radio.RadioManager;
import android.hardware.radio.RadioTuner;
import android.hidl.manager.V1_0.IServiceManager;
import android.hidl.manager.V1_0.IServiceNotification;
import android.os.IBinder;
import android.os.IHwBinder.DeathRecipient;
import android.os.RemoteException;
import com.android.dx.mockito.inline.extended.StaticMockitoSessionBuilder;
import com.android.server.broadcastradio.ExtendedRadioMockitoTestCase;
import org.junit.Test;
import org.mockito.Mock;
import java.util.ArrayList;
import java.util.Arrays;
public final class BroadcastRadioServiceHidlTest extends ExtendedRadioMockitoTestCase {
private static final int FM_RADIO_MODULE_ID = 0;
private static final int DAB_RADIO_MODULE_ID = 1;
private static final ArrayList<String> SERVICE_LIST =
new ArrayList<>(Arrays.asList("FmService", "DabService"));
private static final int[] TEST_ENABLED_TYPES = new int[]{Announcement.TYPE_TRAFFIC};
private final Object mLock = new Object();
private BroadcastRadioService mBroadcastRadioService;
private DeathRecipient mFmDeathRecipient;
@Mock
private IServiceManager mServiceManagerMock;
@Mock
private RadioManager.ModuleProperties mFmModuleMock;
@Mock
private RadioManager.ModuleProperties mDabModuleMock;
@Mock
private RadioModule mFmRadioModuleMock;
@Mock
private RadioModule mDabRadioModuleMock;
@Mock
private IBroadcastRadio mFmHalServiceMock;
@Mock
private IBroadcastRadio mDabHalServiceMock;
@Mock
private TunerSession mFmTunerSessionMock;
@Mock
private ITunerCallback mTunerCallbackMock;
@Mock
private ICloseHandle mFmCloseHandleMock;
@Mock
private ICloseHandle mDabCloseHandleMock;
@Mock
private IAnnouncementListener mAnnouncementListenerMock;
@Mock
private IBinder mBinderMock;
@Override
protected void initializeSession(StaticMockitoSessionBuilder builder) {
builder.spyStatic(RadioModule.class);
}
@Test
public void listModules_withMultipleServiceNames() throws Exception {
createBroadcastRadioService();
assertWithMessage("Radio modules in HIDL broadcast radio HAL client")
.that(mBroadcastRadioService.listModules())
.containsExactly(mFmModuleMock, mDabModuleMock);
}
@Test
public void hasModules_withIdFoundInModules() throws Exception {
createBroadcastRadioService();
assertWithMessage("DAB radio module in HIDL broadcast radio HAL client")
.that(mBroadcastRadioService.hasModule(FM_RADIO_MODULE_ID)).isTrue();
}
@Test
public void hasModules_withIdNotFoundInModules() throws Exception {
createBroadcastRadioService();
assertWithMessage("Radio module of id not found in HIDL broadcast radio HAL client")
.that(mBroadcastRadioService.hasModule(DAB_RADIO_MODULE_ID + 1)).isFalse();
}
@Test
public void hasAnyModules_withModulesExist() throws Exception {
createBroadcastRadioService();
assertWithMessage("Any radio module in HIDL broadcast radio HAL client")
.that(mBroadcastRadioService.hasAnyModules()).isTrue();
}
@Test
public void openSession_withIdFound() throws Exception {
createBroadcastRadioService();
ITuner session = mBroadcastRadioService.openSession(FM_RADIO_MODULE_ID,
/* legacyConfig= */ null, /* withAudio= */ true, mTunerCallbackMock);
assertWithMessage("Session opened in FM radio module")
.that(session).isEqualTo(mFmTunerSessionMock);
}
@Test
public void openSession_withIdNotFound() throws Exception {
createBroadcastRadioService();
int moduleIdInvalid = DAB_RADIO_MODULE_ID + 1;
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () -> {
mBroadcastRadioService.openSession(moduleIdInvalid, /* legacyConfig= */ null,
/* withAudio= */ true, mTunerCallbackMock);
});
assertWithMessage("Exception for opening session with module id %s", moduleIdInvalid)
.that(thrown).hasMessageThat().contains("Invalid module ID");
}
@Test
public void addAnnouncementListener_addsOnAllRadioModules() throws Exception {
createBroadcastRadioService();
when(mAnnouncementListenerMock.asBinder()).thenReturn(mBinderMock);
when(mFmRadioModuleMock.addAnnouncementListener(any(), any()))
.thenReturn(mFmCloseHandleMock);
when(mDabRadioModuleMock.addAnnouncementListener(any(), any()))
.thenReturn(mDabCloseHandleMock);
mBroadcastRadioService.addAnnouncementListener(TEST_ENABLED_TYPES,
mAnnouncementListenerMock);
verify(mFmRadioModuleMock).addAnnouncementListener(any(), any());
verify(mDabRadioModuleMock).addAnnouncementListener(any(), any());
}
@Test
public void binderDied_forDeathRecipient() throws Exception {
createBroadcastRadioService();
mFmDeathRecipient.serviceDied(FM_RADIO_MODULE_ID);
verify(mFmRadioModuleMock).closeSessions(eq(RadioTuner.ERROR_HARDWARE_FAILURE));
assertWithMessage("FM radio module after FM broadcast radio HAL service died")
.that(mBroadcastRadioService.hasModule(FM_RADIO_MODULE_ID)).isFalse();
}
private void createBroadcastRadioService() throws RemoteException {
mockServiceManager();
mBroadcastRadioService = new BroadcastRadioService(/* nextModuleId= */ FM_RADIO_MODULE_ID,
mLock, mServiceManagerMock);
}
private void mockServiceManager() throws RemoteException {
doAnswer(invocation -> {
mFmDeathRecipient = (DeathRecipient) invocation.getArguments()[0];
return null;
}).when(mFmHalServiceMock).linkToDeath(any(), eq((long) FM_RADIO_MODULE_ID));
when(mServiceManagerMock.registerForNotifications(anyString(), anyString(),
any(IServiceNotification.class))).thenAnswer(invocation -> {
IServiceNotification serviceCallback =
(IServiceNotification) invocation.getArguments()[2];
for (int index = 0; index < SERVICE_LIST.size(); index++) {
serviceCallback.onRegistration(IBroadcastRadio.kInterfaceName,
SERVICE_LIST.get(index), /* b= */ false);
}
return true;
}).thenReturn(true);
doReturn(mFmRadioModuleMock).when(() -> RadioModule.tryLoadingModule(
eq(FM_RADIO_MODULE_ID), anyString(), any(Object.class)));
doReturn(mDabRadioModuleMock).when(() -> RadioModule.tryLoadingModule(
eq(DAB_RADIO_MODULE_ID), anyString(), any(Object.class)));
when(mFmRadioModuleMock.getProperties()).thenReturn(mFmModuleMock);
when(mDabRadioModuleMock.getProperties()).thenReturn(mDabModuleMock);
when(mFmRadioModuleMock.getService()).thenReturn(mFmHalServiceMock);
when(mDabRadioModuleMock.getService()).thenReturn(mDabHalServiceMock);
when(mFmRadioModuleMock.openSession(mTunerCallbackMock))
.thenReturn(mFmTunerSessionMock);
}
}

View File

@@ -33,6 +33,7 @@ import android.util.IndentingPrintWriter;
import android.util.Slog;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
import java.util.Collection;
import java.util.HashMap;
@@ -132,6 +133,18 @@ public class BroadcastRadioService {
}
}
@VisibleForTesting
BroadcastRadioService(int nextModuleId, Object lock, IServiceManager manager) {
mNextModuleId = nextModuleId;
mLock = lock;
Objects.requireNonNull(manager, "Service manager cannot be null");
try {
manager.registerForNotifications(IBroadcastRadio.kInterfaceName, "", mServiceListener);
} catch (RemoteException ex) {
Slog.e(TAG, "Failed to register for service notifications: ", ex);
}
}
public @NonNull Collection<RadioManager.ModuleProperties> listModules() {
Slog.v(TAG, "List HIDL 2.0 modules");
synchronized (mLock) {
@@ -154,7 +167,7 @@ public class BroadcastRadioService {
public ITuner openSession(int moduleId, @Nullable RadioManager.BandConfig legacyConfig,
boolean withAudio, @NonNull ITunerCallback callback) throws RemoteException {
Slog.v(TAG, "Open HIDL 2.0 session");
Slog.v(TAG, "Open HIDL 2.0 session with module id " + moduleId);
Objects.requireNonNull(callback);
if (!withAudio) {