From f967e626777f86e7236a3daed735957351df47aa Mon Sep 17 00:00:00 2001 From: Weilin Xu Date: Mon, 7 Nov 2022 21:23:11 +0000 Subject: [PATCH] Add test for announcement and utils HIDL radio Unit tests for announcement aggregator and utils classes in HIDL 2.0 broadcast radio HAL client was added. Bug: 258034192 Test: atest com.android.server.broadcastradio.hal2 Change-Id: Ie605de2f1a1090a8861bfcd03370057ba96e9d8c --- .../aidl/AnnouncementAggregatorTest.java | 13 +- .../hal2/AnnouncementAggregatorHidlTest.java | 169 +++++++++++++ .../broadcastradio/hal2/ConvertTest.java | 224 ++++++++++++++++++ .../server/broadcastradio/hal2/TestUtils.java | 38 +++ .../server/broadcastradio/hal2/Convert.java | 5 + .../server/broadcastradio/hal2/Utils.java | 5 + 6 files changed, 446 insertions(+), 8 deletions(-) create mode 100644 core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/AnnouncementAggregatorHidlTest.java create mode 100644 core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AnnouncementAggregatorTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AnnouncementAggregatorTest.java index 699212a5c9f52..9a1af191fec4e 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AnnouncementAggregatorTest.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AnnouncementAggregatorTest.java @@ -19,7 +19,6 @@ package com.android.server.broadcastradio.aidl; import static com.google.common.truth.Truth.assertWithMessage; import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; @@ -57,11 +56,8 @@ public final class AnnouncementAggregatorTest { private IAnnouncementListener mListenerMock; @Mock private IBinder mBinderMock; - // Array of mocked radio modules private RadioModule[] mRadioModuleMocks; - // Array of mocked close handles private ICloseHandle[] mCloseHandleMocks; - // Array of mocked announcements private Announcement[] mAnnouncementMocks; @Before @@ -72,7 +68,7 @@ public final class AnnouncementAggregatorTest { mAnnouncementAggregator = new AnnouncementAggregator(mListenerMock, mLock); - verify(mBinderMock).linkToDeath(deathRecipientCaptor.capture(), anyInt()); + verify(mBinderMock).linkToDeath(deathRecipientCaptor.capture(), eq(0)); mDeathRecipient = deathRecipientCaptor.getValue(); } @@ -105,7 +101,8 @@ public final class AnnouncementAggregatorTest { moduleWatcherCaptor.getValue().onListUpdated(Arrays.asList(mAnnouncementMocks[index])); verify(mListenerMock, times(index + 1)).onListUpdated(announcementsCaptor.capture()); - assertWithMessage("Number of announcements %s", announcementsCaptor.getValue()) + assertWithMessage("Number of announcements %s after %s announcements were updated", + announcementsCaptor.getValue(), index + 1) .that(announcementsCaptor.getValue().size()).isEqualTo(index + 1); } } @@ -117,7 +114,7 @@ public final class AnnouncementAggregatorTest { mAnnouncementAggregator.close(); verify(mCloseHandleMocks[0]).close(); - verify(mBinderMock).unlinkToDeath(eq(mDeathRecipient), anyInt()); + verify(mBinderMock).unlinkToDeath(mDeathRecipient, 0); } @Test @@ -140,7 +137,7 @@ public final class AnnouncementAggregatorTest { mAnnouncementAggregator.close(); verify(mCloseHandleMocks[0]).close(); - verify(mBinderMock).unlinkToDeath(eq(mDeathRecipient), anyInt()); + verify(mBinderMock).unlinkToDeath(mDeathRecipient, 0); } @Test diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/AnnouncementAggregatorHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/AnnouncementAggregatorHidlTest.java new file mode 100644 index 0000000000000..b68e65fb7674d --- /dev/null +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/AnnouncementAggregatorHidlTest.java @@ -0,0 +1,169 @@ +/* + * 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.google.common.truth.Truth.assertWithMessage; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.hardware.radio.Announcement; +import android.hardware.radio.IAnnouncementListener; +import android.hardware.radio.ICloseHandle; +import android.os.IBinder; +import android.os.RemoteException; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.junit.MockitoJUnitRunner; + +import java.util.Arrays; +import java.util.List; + +/** + * Tests for HIDL 2.0 HAL AnnouncementAggregator. + */ +@RunWith(MockitoJUnitRunner.class) +public final class AnnouncementAggregatorHidlTest { + + private static final int[] TEST_ENABLED_TYPES = new int[]{Announcement.TYPE_TRAFFIC}; + + private final Object mLock = new Object(); + private AnnouncementAggregator mAnnouncementAggregator; + private IBinder.DeathRecipient mDeathRecipient; + + @Mock + private IAnnouncementListener mListenerMock; + @Mock + private IBinder mBinderMock; + private RadioModule[] mRadioModuleMocks; + private ICloseHandle[] mCloseHandleMocks; + private Announcement[] mAnnouncementMocks; + + @Before + public void setUp() throws Exception { + ArgumentCaptor deathRecipientCaptor = + ArgumentCaptor.forClass(IBinder.DeathRecipient.class); + when(mListenerMock.asBinder()).thenReturn(mBinderMock); + + mAnnouncementAggregator = new AnnouncementAggregator(mListenerMock, mLock); + + verify(mBinderMock).linkToDeath(deathRecipientCaptor.capture(), eq(0)); + mDeathRecipient = deathRecipientCaptor.getValue(); + } + + @Test + public void onListUpdated_withOneModuleWatcher() throws Exception { + ArgumentCaptor moduleWatcherCaptor = + ArgumentCaptor.forClass(IAnnouncementListener.class); + watchModules(/* moduleNumber= */ 1); + + verify(mRadioModuleMocks[0]).addAnnouncementListener(any(), moduleWatcherCaptor.capture()); + + moduleWatcherCaptor.getValue().onListUpdated(Arrays.asList(mAnnouncementMocks[0])); + + verify(mListenerMock).onListUpdated(any()); + } + + @Test + public void onListUpdated_withMultipleModuleWatchers() throws Exception { + int moduleNumber = 3; + watchModules(moduleNumber); + + for (int index = 0; index < moduleNumber; index++) { + ArgumentCaptor moduleWatcherCaptor = + ArgumentCaptor.forClass(IAnnouncementListener.class); + ArgumentCaptor> announcementsCaptor = + ArgumentCaptor.forClass(List.class); + verify(mRadioModuleMocks[index]) + .addAnnouncementListener(any(), moduleWatcherCaptor.capture()); + + moduleWatcherCaptor.getValue().onListUpdated(Arrays.asList(mAnnouncementMocks[index])); + + verify(mListenerMock, times(index + 1)).onListUpdated(announcementsCaptor.capture()); + assertWithMessage("Number of announcements %s after %s announcements were updated", + announcementsCaptor.getValue(), index + 1) + .that(announcementsCaptor.getValue().size()).isEqualTo(index + 1); + } + } + + @Test + public void close_withOneModuleWatcher_invokesCloseHandle() throws Exception { + watchModules(/* moduleNumber= */ 1); + + mAnnouncementAggregator.close(); + + verify(mCloseHandleMocks[0]).close(); + verify(mBinderMock).unlinkToDeath(mDeathRecipient, 0); + } + + @Test + public void close_withMultipleModuleWatcher_invokesCloseHandles() throws Exception { + int moduleNumber = 3; + watchModules(moduleNumber); + + mAnnouncementAggregator.close(); + + for (int index = 0; index < moduleNumber; index++) { + verify(mCloseHandleMocks[index]).close(); + } + } + + @Test + public void close_twice_invokesCloseHandleOnce() throws Exception { + watchModules(/* moduleNumber= */ 1); + + mAnnouncementAggregator.close(); + mAnnouncementAggregator.close(); + + verify(mCloseHandleMocks[0]).close(); + verify(mBinderMock).unlinkToDeath(mDeathRecipient, 0); + } + + @Test + public void binderDied_forDeathRecipient_invokesCloseHandle() throws Exception { + watchModules(/* moduleNumber= */ 1); + + mDeathRecipient.binderDied(); + + verify(mCloseHandleMocks[0]).close(); + + } + + private void watchModules(int moduleNumber) throws RemoteException { + mRadioModuleMocks = new RadioModule[moduleNumber]; + mCloseHandleMocks = new ICloseHandle[moduleNumber]; + mAnnouncementMocks = new Announcement[moduleNumber]; + + for (int index = 0; index < moduleNumber; index++) { + mRadioModuleMocks[index] = mock(RadioModule.class); + mCloseHandleMocks[index] = mock(ICloseHandle.class); + mAnnouncementMocks[index] = mock(Announcement.class); + + when(mRadioModuleMocks[index].addAnnouncementListener(any(), any())) + .thenReturn(mCloseHandleMocks[index]); + mAnnouncementAggregator.watchModule(mRadioModuleMocks[index], TEST_ENABLED_TYPES); + } + } +} diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java new file mode 100644 index 0000000000000..3de4f5d7dcd9d --- /dev/null +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/ConvertTest.java @@ -0,0 +1,224 @@ +/* + * 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 android.hardware.broadcastradio.V2_0.AmFmBandRange; +import android.hardware.broadcastradio.V2_0.AmFmRegionConfig; +import android.hardware.broadcastradio.V2_0.DabTableEntry; +import android.hardware.broadcastradio.V2_0.IdentifierType; +import android.hardware.broadcastradio.V2_0.Properties; +import android.hardware.broadcastradio.V2_0.VendorKeyValue; +import android.hardware.radio.Announcement; +import android.hardware.radio.ProgramSelector; +import android.hardware.radio.RadioManager; + +import com.google.common.truth.Expect; + +import org.junit.Rule; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; + +public final class ConvertTest { + + private static final int FM_LOWER_LIMIT = 87500; + private static final int FM_UPPER_LIMIT = 108000; + private static final int FM_SPACING = 200; + private static final int AM_LOWER_LIMIT = 540; + private static final int AM_UPPER_LIMIT = 1700; + private static final int AM_SPACING = 10; + private static final String DAB_ENTRY_LABEL_1 = "5A"; + private static final int DAB_ENTRY_FREQUENCY_1 = 174928; + private static final String DAB_ENTRY_LABEL_2 = "12D"; + private static final int DAB_ENTRY_FREQUENCY_2 = 229072; + private static final String VENDOR_INFO_KEY_1 = "vendorKey1"; + private static final String VENDOR_INFO_VALUE_1 = "vendorValue1"; + private static final String VENDOR_INFO_KEY_2 = "vendorKey2"; + private static final String VENDOR_INFO_VALUE_2 = "vendorValue2"; + private static final String TEST_SERVICE_NAME = "serviceMock"; + private static final int TEST_ID = 1; + private static final String TEST_MAKER = "makerMock"; + private static final String TEST_PRODUCT = "productMock"; + private static final String TEST_VERSION = "versionMock"; + private static final String TEST_SERIAL = "serialMock"; + + private static final int TEST_ENABLED_TYPE = Announcement.TYPE_EMERGENCY; + private static final int TEST_ANNOUNCEMENT_FREQUENCY = FM_LOWER_LIMIT + FM_SPACING; + + private static final RadioManager.ModuleProperties MODULE_PROPERTIES = + convertToModuleProperties(); + private static final Announcement ANNOUNCEMENT = + Convert.announcementFromHal( + TestUtils.makeAnnouncement(TEST_ENABLED_TYPE, TEST_ANNOUNCEMENT_FREQUENCY)); + + @Rule + public final Expect expect = Expect.create(); + + @Test + public void propertiesFromHalProperties_idsMatch() { + expect.withMessage("Properties id") + .that(MODULE_PROPERTIES.getId()).isEqualTo(TEST_ID); + } + + @Test + public void propertiesFromHalProperties_serviceNamesMatch() { + expect.withMessage("Service name") + .that(MODULE_PROPERTIES.getServiceName()).isEqualTo(TEST_SERVICE_NAME); + } + + @Test + public void propertiesFromHalProperties_implementorsMatch() { + expect.withMessage("Implementor") + .that(MODULE_PROPERTIES.getImplementor()).isEqualTo(TEST_MAKER); + } + + + @Test + public void propertiesFromHalProperties_productsMatch() { + expect.withMessage("Product") + .that(MODULE_PROPERTIES.getProduct()).isEqualTo(TEST_PRODUCT); + } + + @Test + public void propertiesFromHalProperties_versionsMatch() { + expect.withMessage("Version") + .that(MODULE_PROPERTIES.getVersion()).isEqualTo(TEST_VERSION); + } + + @Test + public void propertiesFromHalProperties_serialsMatch() { + expect.withMessage("Serial") + .that(MODULE_PROPERTIES.getSerial()).isEqualTo(TEST_SERIAL); + } + + @Test + public void propertiesFromHalProperties_dabTableInfoMatch() { + Map dabTableExpected = Map.of(DAB_ENTRY_LABEL_1, DAB_ENTRY_FREQUENCY_1, + DAB_ENTRY_LABEL_2, DAB_ENTRY_FREQUENCY_2); + + expect.withMessage("Supported program types") + .that(MODULE_PROPERTIES.getDabFrequencyTable()) + .containsExactlyEntriesIn(dabTableExpected); + } + + @Test + public void propertiesFromHalProperties_vendorInfoMatch() { + Map vendorInfoExpected = Map.of(VENDOR_INFO_KEY_1, VENDOR_INFO_VALUE_1, + VENDOR_INFO_KEY_2, VENDOR_INFO_VALUE_2); + + expect.withMessage("Vendor info").that(MODULE_PROPERTIES.getVendorInfo()) + .containsExactlyEntriesIn(vendorInfoExpected); + } + + @Test + public void propertiesFromHalProperties_bandsMatch() { + RadioManager.BandDescriptor[] bands = MODULE_PROPERTIES.getBands(); + + expect.withMessage("Band descriptors").that(bands).hasLength(2); + + expect.withMessage("FM band frequency lower limit") + .that(bands[0].getLowerLimit()).isEqualTo(FM_LOWER_LIMIT); + expect.withMessage("FM band frequency upper limit") + .that(bands[0].getUpperLimit()).isEqualTo(FM_UPPER_LIMIT); + expect.withMessage("FM band frequency spacing") + .that(bands[0].getSpacing()).isEqualTo(FM_SPACING); + + expect.withMessage("AM band frequency lower limit") + .that(bands[1].getLowerLimit()).isEqualTo(AM_LOWER_LIMIT); + expect.withMessage("AM band frequency upper limit") + .that(bands[1].getUpperLimit()).isEqualTo(AM_UPPER_LIMIT); + expect.withMessage("AM band frequency spacing") + .that(bands[1].getSpacing()).isEqualTo(AM_SPACING); + } + + @Test + public void announcementFromHalAnnouncement_typesMatch() { + expect.withMessage("Announcement type") + .that(ANNOUNCEMENT.getType()).isEqualTo(TEST_ENABLED_TYPE); + } + + @Test + public void announcementFromHalAnnouncement_selectorsMatch() { + ProgramSelector.Identifier primaryIdExpected = new ProgramSelector.Identifier( + ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, TEST_ANNOUNCEMENT_FREQUENCY); + + ProgramSelector selector = ANNOUNCEMENT.getSelector(); + + expect.withMessage("Primary id of announcement selector") + .that(selector.getPrimaryId()).isEqualTo(primaryIdExpected); + expect.withMessage("Secondary ids of announcement selector") + .that(selector.getSecondaryIds()).isEmpty(); + } + + @Test + public void announcementFromHalAnnouncement_VendorInfoMatch() { + expect.withMessage("Announcement vendor info") + .that(ANNOUNCEMENT.getVendorInfo()).isEmpty(); + } + + private static RadioManager.ModuleProperties convertToModuleProperties() { + AmFmRegionConfig amFmConfig = createAmFmRegionConfig(); + List dabTableEntries = Arrays.asList( + createDabTableEntry(DAB_ENTRY_LABEL_1, DAB_ENTRY_FREQUENCY_1), + createDabTableEntry(DAB_ENTRY_LABEL_2, DAB_ENTRY_FREQUENCY_2)); + Properties properties = createHalProperties(); + + return Convert.propertiesFromHal(TEST_ID, TEST_SERVICE_NAME, properties, + amFmConfig, dabTableEntries); + } + + private static AmFmRegionConfig createAmFmRegionConfig() { + AmFmRegionConfig amFmRegionConfig = new AmFmRegionConfig(); + amFmRegionConfig.ranges = new ArrayList(Arrays.asList( + createAmFmBandRange(FM_LOWER_LIMIT, FM_UPPER_LIMIT, FM_SPACING), + createAmFmBandRange(AM_LOWER_LIMIT, AM_UPPER_LIMIT, AM_SPACING))); + return amFmRegionConfig; + } + + private static AmFmBandRange createAmFmBandRange(int lowerBound, int upperBound, int spacing) { + AmFmBandRange bandRange = new AmFmBandRange(); + bandRange.lowerBound = lowerBound; + bandRange.upperBound = upperBound; + bandRange.spacing = spacing; + bandRange.scanSpacing = bandRange.spacing; + return bandRange; + } + + private static DabTableEntry createDabTableEntry(String label, int value) { + DabTableEntry dabTableEntry = new DabTableEntry(); + dabTableEntry.label = label; + dabTableEntry.frequency = value; + return dabTableEntry; + } + + private static Properties createHalProperties() { + Properties halProperties = new Properties(); + halProperties.supportedIdentifierTypes = new ArrayList(Arrays.asList( + IdentifierType.AMFM_FREQUENCY, IdentifierType.RDS_PI, IdentifierType.DAB_SID_EXT)); + halProperties.maker = TEST_MAKER; + halProperties.product = TEST_PRODUCT; + halProperties.version = TEST_VERSION; + halProperties.serial = TEST_SERIAL; + halProperties.vendorInfo = new ArrayList(Arrays.asList( + TestUtils.makeVendorKeyValue(VENDOR_INFO_KEY_1, VENDOR_INFO_VALUE_1), + TestUtils.makeVendorKeyValue(VENDOR_INFO_KEY_2, VENDOR_INFO_VALUE_2))); + return halProperties; + } +} diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TestUtils.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TestUtils.java index 4944803eaafe7..392e140f24e00 100644 --- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TestUtils.java +++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TestUtils.java @@ -15,14 +15,23 @@ */ package com.android.server.broadcastradio.hal2; +import android.hardware.broadcastradio.V2_0.IdentifierType; +import android.hardware.broadcastradio.V2_0.ProgramIdentifier; import android.hardware.broadcastradio.V2_0.ProgramInfo; +import android.hardware.broadcastradio.V2_0.VendorKeyValue; import android.hardware.radio.ProgramSelector; import android.hardware.radio.RadioManager; import android.hardware.radio.RadioMetadata; +import java.util.ArrayList; import java.util.HashMap; final class TestUtils { + + private TestUtils() { + throw new UnsupportedOperationException("TestUtils class is noninstantiable"); + } + static RadioManager.ProgramInfo makeProgramInfo(int programType, ProgramSelector.Identifier identifier, int signalQuality) { // Note: If you set new fields, check if programInfoToHal() needs to be updated as well. @@ -39,4 +48,33 @@ final class TestUtils { hwInfo.signalQuality = info.getSignalStrength(); return hwInfo; } + + static android.hardware.broadcastradio.V2_0.ProgramSelector makeHalFmSelector(int freq) { + ProgramIdentifier halId = new ProgramIdentifier(); + halId.type = IdentifierType.AMFM_FREQUENCY; + halId.value = freq; + + android.hardware.broadcastradio.V2_0.ProgramSelector halSelector = + new android.hardware.broadcastradio.V2_0.ProgramSelector(); + halSelector.primaryId = halId; + halSelector.secondaryIds = new ArrayList<>(); + return halSelector; + } + + static VendorKeyValue makeVendorKeyValue(String vendorKey, String vendorValue) { + VendorKeyValue vendorKeyValue = new VendorKeyValue(); + vendorKeyValue.key = vendorKey; + vendorKeyValue.value = vendorValue; + return vendorKeyValue; + } + + static android.hardware.broadcastradio.V2_0.Announcement makeAnnouncement(int type, + int selectorFreq) { + android.hardware.broadcastradio.V2_0.Announcement halAnnouncement = + new android.hardware.broadcastradio.V2_0.Announcement(); + halAnnouncement.type = (byte) type; + halAnnouncement.selector = makeHalFmSelector(selectorFreq); + halAnnouncement.vendorInfo = new ArrayList<>(); + return halAnnouncement; + } } diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java index 726cdc334943d..3daf1dbd933ae 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/Convert.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/Convert.java @@ -52,8 +52,13 @@ import java.util.Set; import java.util.stream.Collectors; class Convert { + private static final String TAG = "BcRadio2Srv.convert"; + private Convert() { + throw new UnsupportedOperationException("Convert class is noninstantiable"); + } + static void throwOnError(String action, int result) { switch (result) { case Result.OK: diff --git a/services/core/java/com/android/server/broadcastradio/hal2/Utils.java b/services/core/java/com/android/server/broadcastradio/hal2/Utils.java index 384c9bab94b48..f6e90ef4197c3 100644 --- a/services/core/java/com/android/server/broadcastradio/hal2/Utils.java +++ b/services/core/java/com/android/server/broadcastradio/hal2/Utils.java @@ -28,6 +28,11 @@ enum FrequencyBand { }; class Utils { + + private Utils() { + throw new UnsupportedOperationException("Utils class is noninstantiable"); + } + private static final String TAG = "BcRadio2Srv.utils"; static FrequencyBand getBand(int freq) {