Merge changes from topic "radio-dab-u"

* changes:
  Add exception and conversion tests for AIDL client
  Add unit tests about throwing expection for radio
  Improve DAB support in radio service and manager
This commit is contained in:
Weilin Xu
2022-12-22 22:44:38 +00:00
committed by Android (Google) Code Review
14 changed files with 1220 additions and 138 deletions

View File

@@ -65,12 +65,12 @@ public final class ProgramSelector implements Parcelable {
*/
@Deprecated
public static final int PROGRAM_TYPE_INVALID = 0;
/** Analogue AM radio (with or without RDS).
/** Analog AM radio (with or without RDS).
* @deprecated use {@link ProgramIdentifier} instead
*/
@Deprecated
public static final int PROGRAM_TYPE_AM = 1;
/** analogue FM radio (with or without RDS).
/** analog FM radio (with or without RDS).
* @deprecated use {@link ProgramIdentifier} instead
*/
@Deprecated
@@ -125,25 +125,50 @@ public final class ProgramSelector implements Parcelable {
public @interface ProgramType {}
public static final int IDENTIFIER_TYPE_INVALID = 0;
/** kHz */
/**
* Primary identifier for analog (without RDS) AM/FM stations:
* frequency in kHz.
*
* <p>This identifier also contains band information:
* <li>
* <ul><500kHz: AM LW.
* <ul>500kHz - 1705kHz: AM MW.
* <ul>1.71MHz - 30MHz: AM SW.
* <ul>>60MHz: FM.
* </li>
*/
public static final int IDENTIFIER_TYPE_AMFM_FREQUENCY = 1;
/** 16bit */
/**
* 16bit primary identifier for FM RDS station.
*/
public static final int IDENTIFIER_TYPE_RDS_PI = 2;
/**
* 64bit compound primary identifier for HD Radio.
*
* Consists of (from the LSB):
* - 32bit: Station ID number;
* - 4bit: HD_SUBCHANNEL;
* - 18bit: AMFM_FREQUENCY.
* The remaining bits should be set to zeros when writing on the chip side
* <p>Consists of (from the LSB):
* <li>
* <ul>132bit: Station ID number.
* <ul>14bit: HD_SUBCHANNEL.
* <ul>18bit: AMFM_FREQUENCY.
* </li>
*
* <p>While station ID number should be unique globally, it sometimes gets
* abused by broadcasters (i.e. not being set at all). To ensure local
* uniqueness, AMFM_FREQUENCY_KHZ was added here. Global uniqueness is
* a best-effort - see {@link IDENTIFIER_TYPE_HD_STATION_NAME}.
*
* <p>HD Radio subchannel is a value in range of 0-7.
* This index is 0-based (where 0 is MPS and 1..7 are SPS),
* as opposed to HD Radio standard (where it's 1-based).
*
* <p>The remaining bits should be set to zeros when writing on the chip side
* and ignored when read.
*/
public static final int IDENTIFIER_TYPE_HD_STATION_ID_EXT = 3;
/**
* HD Radio subchannel - a value of range 0-7.
* HD Radio subchannel - a value in range of 0-7.
*
* The subchannel index is 0-based (where 0 is MPS and 1..7 are SPS),
* <p>The subchannel index is 0-based (where 0 is MPS and 1..7 are SPS),
* as opposed to HD Radio standard (where it's 1-based).
*
* @deprecated use IDENTIFIER_TYPE_HD_STATION_ID_EXT instead
@@ -153,16 +178,16 @@ public final class ProgramSelector implements Parcelable {
/**
* 64bit additional identifier for HD Radio.
*
* Due to Station ID abuse, some HD_STATION_ID_EXT identifiers may be not
* <p>Due to Station ID abuse, some HD_STATION_ID_EXT identifiers may be not
* globally unique. To provide a best-effort solution, a short version of
* station name may be carried as additional identifier and may be used
* by the tuner hardware to double-check tuning.
*
* The name is limited to the first 8 A-Z0-9 characters (lowercase letters
* must be converted to uppercase). Encoded in little-endian ASCII:
* the first character of the name is the LSB.
* <p>The name is limited to the first 8 A-Z0-9 characters (lowercase
* letters must be converted to uppercase). Encoded in little-endian
* ASCII: the first character of the name is the LSB.
*
* For example: "Abc" is encoded as 0x434241.
* <p>For example: "Abc" is encoded as 0x434241.
*/
public static final int IDENTIFIER_TYPE_HD_STATION_NAME = 10004;
/**
@@ -175,17 +200,19 @@ public final class ProgramSelector implements Parcelable {
/**
* 28bit compound primary identifier for Digital Audio Broadcasting.
*
* Consists of (from the LSB):
* - 16bit: SId;
* - 8bit: ECC code;
* - 4bit: SCIdS.
* <p>Consists of (from the LSB):
* <li>
* <ul>16bit: SId.
* <ul>8bit: ECC code.
* <ul>4bit: SCIdS.
* </li>
*
* SCIdS (Service Component Identifier within the Service) value
* <p>SCIdS (Service Component Identifier within the Service) value
* of 0 represents the main service, while 1 and above represents
* secondary services.
*
* The remaining bits should be set to zeros when writing on the chip side
* and ignored when read.
* <p>The remaining bits should be set to zeros when writing on the chip
* side and ignored when read.
*
* @deprecated use {@link #IDENTIFIER_TYPE_DAB_DMB_SID_EXT} instead
*/
@@ -197,7 +224,9 @@ public final class ProgramSelector implements Parcelable {
public static final int IDENTIFIER_TYPE_DAB_SCID = 7;
/** kHz */
public static final int IDENTIFIER_TYPE_DAB_FREQUENCY = 8;
/** 24bit */
/**
* 24bit primary identifier for Digital Radio Mondiale.
*/
public static final int IDENTIFIER_TYPE_DRMO_SERVICE_ID = 9;
/** kHz */
public static final int IDENTIFIER_TYPE_DRMO_FREQUENCY = 10;
@@ -207,7 +236,9 @@ public final class ProgramSelector implements Parcelable {
*/
@Deprecated
public static final int IDENTIFIER_TYPE_DRMO_MODULATION = 11;
/** 32bit */
/**
* 32bit primary identifier for SiriusXM Satellite Radio.
*/
public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 12;
/** 0-999 range */
public static final int IDENTIFIER_TYPE_SXM_CHANNEL = 13;
@@ -224,15 +255,15 @@ public final class ProgramSelector implements Parcelable {
* of 0 represents the main service, while 1 and above represents
* secondary services.
*
* The remaining bits should be set to zeros when writing on the chip side
* and ignored when read.
* <p>The remaining bits should be set to zeros when writing on the chip
* side and ignored when read.
*/
public static final int IDENTIFIER_TYPE_DAB_DMB_SID_EXT = 14;
/**
* Primary identifier for vendor-specific radio technology.
* The value format is determined by a vendor.
*
* It must not be used in any other programType than corresponding VENDOR
* <p>It must not be used in any other programType than corresponding VENDOR
* type between VENDOR_START and VENDOR_END (eg. identifier type 1015 must
* not be used in any program type other than 1015).
*/

View File

@@ -72,9 +72,9 @@ public final class ProgramListTest {
/* value= */ 94300);
private static final ProgramSelector.Identifier RDS_IDENTIFIER =
new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_RDS_PI, 15019);
private static final ProgramSelector.Identifier DAB_SID_EXT_IDENTIFIER =
new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT,
/* value= */ 0x10000111);
private static final ProgramSelector.Identifier DAB_DMB_SID_EXT_IDENTIFIER =
new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT,
/* value= */ 0xA000000111L);
private static final ProgramSelector.Identifier DAB_ENSEMBLE_IDENTIFIER =
new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE,
/* value= */ 0x1013);
@@ -89,7 +89,7 @@ public final class ProgramListTest {
private static final ProgramList.Chunk FM_RDS_ADD_CHUNK = new ProgramList.Chunk(IS_PURGE,
IS_COMPLETE, Set.of(FM_PROGRAM_INFO, RDS_PROGRAM_INFO),
Set.of(DAB_SID_EXT_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER));
Set.of(DAB_DMB_SID_EXT_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER));
private static final ProgramList.Chunk FM_ADD_INCOMPLETE_CHUNK = new ProgramList.Chunk(IS_PURGE,
/* complete= */ false, Set.of(FM_PROGRAM_INFO), new ArraySet<>());
private static final ProgramList.Filter TEST_FILTER = new ProgramList.Filter(
@@ -216,7 +216,7 @@ public final class ProgramListTest {
public void isPurge_forChunk() {
ProgramList.Chunk chunk = new ProgramList.Chunk(IS_PURGE, IS_COMPLETE,
Set.of(FM_PROGRAM_INFO, RDS_PROGRAM_INFO),
Set.of(DAB_SID_EXT_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER));
Set.of(DAB_DMB_SID_EXT_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER));
assertWithMessage("Puring chunk").that(chunk.isPurge()).isEqualTo(IS_PURGE);
}
@@ -225,7 +225,7 @@ public final class ProgramListTest {
public void isComplete_forChunk() {
ProgramList.Chunk chunk = new ProgramList.Chunk(IS_PURGE, IS_COMPLETE,
Set.of(FM_PROGRAM_INFO, RDS_PROGRAM_INFO),
Set.of(DAB_SID_EXT_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER));
Set.of(DAB_DMB_SID_EXT_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER));
assertWithMessage("Complete chunk").that(chunk.isComplete()).isEqualTo(IS_COMPLETE);
}
@@ -234,7 +234,7 @@ public final class ProgramListTest {
public void getModified_forChunk() {
ProgramList.Chunk chunk = new ProgramList.Chunk(IS_PURGE, IS_COMPLETE,
Set.of(FM_PROGRAM_INFO, RDS_PROGRAM_INFO),
Set.of(DAB_SID_EXT_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER));
Set.of(DAB_DMB_SID_EXT_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER));
assertWithMessage("Modified program info in chunk")
.that(chunk.getModified()).containsExactly(FM_PROGRAM_INFO, RDS_PROGRAM_INFO);
@@ -244,10 +244,10 @@ public final class ProgramListTest {
public void getRemoved_forChunk() {
ProgramList.Chunk chunk = new ProgramList.Chunk(IS_PURGE, IS_COMPLETE,
Set.of(FM_PROGRAM_INFO, RDS_PROGRAM_INFO),
Set.of(DAB_SID_EXT_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER));
Set.of(DAB_DMB_SID_EXT_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER));
assertWithMessage("Removed program identifiers in chunk").that(chunk.getRemoved())
.containsExactly(DAB_SID_EXT_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER);
.containsExactly(DAB_DMB_SID_EXT_IDENTIFIER, DAB_ENSEMBLE_IDENTIFIER);
}
@Test
@@ -275,6 +275,19 @@ public final class ProgramListTest {
assertWithMessage("Chunks").that(chunks).hasLength(CREATOR_ARRAY_SIZE);
}
@Test
public void getProgramList_forTunerAdapterWhenServiceDied_fails() throws Exception {
Map<String, String> parameters = Map.of("ParameterKeyMock", "ParameterValueMock");
createRadioTuner();
doThrow(new RemoteException()).when(mTunerMock).startProgramListUpdates(any());
RuntimeException thrown = assertThrows(RuntimeException.class,
() -> mRadioTuner.getProgramList(parameters));
assertWithMessage("Exception for getting program list when service is dead")
.that(thrown).hasMessageThat().contains("Service died");
}
@Test
public void getDynamicProgramList_forTunerAdapter() throws Exception {
createRadioTuner();

View File

@@ -36,18 +36,18 @@ public final class ProgramSelectorTest {
private static final long AM_FREQUENCY = 700;
private static final ProgramSelector.Identifier FM_IDENTIFIER = new ProgramSelector.Identifier(
ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, FM_FREQUENCY);
private static final ProgramSelector.Identifier DAB_SID_EXT_IDENTIFIER_1 =
new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT,
/* value= */ 0x1000011);
private static final ProgramSelector.Identifier DAB_SID_EXT_IDENTIFIER_2 =
new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT,
/* value= */ 0x10000112);
private static final ProgramSelector.Identifier DAB_DMB_SID_EXT_IDENTIFIER_1 =
new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT,
/* value= */ 0xA000000111L);
private static final ProgramSelector.Identifier DAB_DMB_SID_EXT_IDENTIFIER_2 =
new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT,
/* value= */ 0xA000000112L);
private static final ProgramSelector.Identifier DAB_ENSEMBLE_IDENTIFIER =
new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE,
/* value= */ 0x1001);
private static final ProgramSelector.Identifier DAB_FREQUENCY_IDENTIFIER =
new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY,
/* value= */ 94500);
/* value= */ 220352);
@Test
public void getType_forIdentifier() {
@@ -80,13 +80,13 @@ public final class ProgramSelectorTest {
@Test
public void equals_withDifferentTypesForIdentifiers_returnsFalse() {
assertWithMessage("Identifier with different identifier type")
.that(FM_IDENTIFIER).isNotEqualTo(DAB_SID_EXT_IDENTIFIER_1);
.that(FM_IDENTIFIER).isNotEqualTo(DAB_DMB_SID_EXT_IDENTIFIER_1);
}
@Test
public void equals_withDifferentValuesForIdentifiers_returnsFalse() {
assertWithMessage("Identifier with different identifier value")
.that(DAB_SID_EXT_IDENTIFIER_2).isNotEqualTo(DAB_SID_EXT_IDENTIFIER_1);
.that(DAB_DMB_SID_EXT_IDENTIFIER_2).isNotEqualTo(DAB_DMB_SID_EXT_IDENTIFIER_1);
}
@Test
@@ -168,19 +168,19 @@ public final class ProgramSelectorTest {
@Test
public void getFirstId_withIdInSelector() {
ProgramSelector.Identifier[] secondaryIds = new ProgramSelector.Identifier[]{
DAB_ENSEMBLE_IDENTIFIER, DAB_SID_EXT_IDENTIFIER_2, DAB_FREQUENCY_IDENTIFIER};
DAB_ENSEMBLE_IDENTIFIER, DAB_DMB_SID_EXT_IDENTIFIER_2, DAB_FREQUENCY_IDENTIFIER};
ProgramSelector selector = getDabSelector(secondaryIds, /* vendorIds= */ null);
long firstIdValue = selector.getFirstId(ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT);
long firstIdValue = selector.getFirstId(ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT);
assertWithMessage("Value of the first DAB_SID_EXT identifier")
.that(firstIdValue).isEqualTo(DAB_SID_EXT_IDENTIFIER_1.getValue());
.that(firstIdValue).isEqualTo(DAB_DMB_SID_EXT_IDENTIFIER_1.getValue());
}
@Test
public void getFirstId_withIdNotInSelector() {
ProgramSelector.Identifier[] secondaryIds = new ProgramSelector.Identifier[]{
DAB_ENSEMBLE_IDENTIFIER, DAB_SID_EXT_IDENTIFIER_2};
DAB_ENSEMBLE_IDENTIFIER, DAB_DMB_SID_EXT_IDENTIFIER_2};
ProgramSelector selector = getDabSelector(secondaryIds, /* vendorIds= */ null);
int idType = ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY;
@@ -195,13 +195,13 @@ public final class ProgramSelectorTest {
@Test
public void getAllIds_withIdInSelector() {
ProgramSelector.Identifier[] secondaryIds = new ProgramSelector.Identifier[]{
DAB_ENSEMBLE_IDENTIFIER, DAB_SID_EXT_IDENTIFIER_2, DAB_FREQUENCY_IDENTIFIER};
DAB_ENSEMBLE_IDENTIFIER, DAB_DMB_SID_EXT_IDENTIFIER_2, DAB_FREQUENCY_IDENTIFIER};
ProgramSelector.Identifier[] allIdsExpected =
{DAB_SID_EXT_IDENTIFIER_1, DAB_SID_EXT_IDENTIFIER_2};
{DAB_DMB_SID_EXT_IDENTIFIER_1, DAB_DMB_SID_EXT_IDENTIFIER_2};
ProgramSelector selector = getDabSelector(secondaryIds, /* vendorIds= */ null);
ProgramSelector.Identifier[] allIds =
selector.getAllIds(ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT);
selector.getAllIds(ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT);
assertWithMessage("All DAB_SID_EXT identifiers in selector")
.that(allIds).isEqualTo(allIdsExpected);
@@ -244,14 +244,14 @@ public final class ProgramSelectorTest {
@Test
public void withSecondaryPreferred() {
ProgramSelector.Identifier[] secondaryIds = new ProgramSelector.Identifier[]{
DAB_ENSEMBLE_IDENTIFIER, DAB_SID_EXT_IDENTIFIER_2, DAB_FREQUENCY_IDENTIFIER};
DAB_ENSEMBLE_IDENTIFIER, DAB_DMB_SID_EXT_IDENTIFIER_2, DAB_FREQUENCY_IDENTIFIER};
long[] vendorIdsExpected = {12345, 678};
ProgramSelector selector = getDabSelector(secondaryIds, vendorIdsExpected);
ProgramSelector.Identifier[] secondaryIdsExpected = new ProgramSelector.Identifier[]{
DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER, DAB_SID_EXT_IDENTIFIER_1};
DAB_ENSEMBLE_IDENTIFIER, DAB_FREQUENCY_IDENTIFIER, DAB_DMB_SID_EXT_IDENTIFIER_1};
ProgramSelector selectorPreferred =
selector.withSecondaryPreferred(DAB_SID_EXT_IDENTIFIER_1);
selector.withSecondaryPreferred(DAB_DMB_SID_EXT_IDENTIFIER_1);
assertWithMessage("Program type")
.that(selectorPreferred.getProgramType()).isEqualTo(selector.getProgramType());
@@ -458,7 +458,7 @@ public final class ProgramSelectorTest {
private ProgramSelector getDabSelector(@Nullable ProgramSelector.Identifier[] secondaryIds,
@Nullable long[] vendorIds) {
return new ProgramSelector(DAB_PROGRAM_TYPE, DAB_SID_EXT_IDENTIFIER_1, secondaryIds,
return new ProgramSelector(DAB_PROGRAM_TYPE, DAB_DMB_SID_EXT_IDENTIFIER_1, secondaryIds,
vendorIds);
}
}

View File

@@ -18,7 +18,9 @@ package android.hardware.radio.tests.unittests;
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.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.never;
@@ -105,17 +107,17 @@ public final class RadioManagerTest {
private static final int INFO_FLAGS = 0b110001;
private static final int SIGNAL_QUALITY = 2;
private static final ProgramSelector.Identifier DAB_SID_EXT_IDENTIFIER =
new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT,
/* value= */ 0x10000111);
new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT,
/* value= */ 0xA000000111L);
private static final ProgramSelector.Identifier DAB_SID_EXT_IDENTIFIER_RELATED =
new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT,
/* value= */ 0x10000113);
new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT,
/* value= */ 0xA000000113L);
private static final ProgramSelector.Identifier DAB_ENSEMBLE_IDENTIFIER =
new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE,
/* value= */ 0x1013);
private static final ProgramSelector.Identifier DAB_FREQUENCY_IDENTIFIER =
new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY,
/* value= */ 95500);
/* value= */ 220352);
private static final ProgramSelector DAB_SELECTOR =
new ProgramSelector(ProgramSelector.PROGRAM_TYPE_DAB, DAB_SID_EXT_IDENTIFIER,
new ProgramSelector.Identifier[]{
@@ -859,13 +861,13 @@ public final class RadioManagerTest {
@Test
public void getLogicallyTunedTo_forProgramInfo() {
assertWithMessage("Identifier logically tuned to in DAB program info")
.that(DAB_PROGRAM_INFO.getLogicallyTunedTo()).isEqualTo(DAB_FREQUENCY_IDENTIFIER);
.that(DAB_PROGRAM_INFO.getLogicallyTunedTo()).isEqualTo(DAB_SID_EXT_IDENTIFIER);
}
@Test
public void getPhysicallyTunedTo_forProgramInfo() {
assertWithMessage("Identifier physically tuned to DAB program info")
.that(DAB_PROGRAM_INFO.getPhysicallyTunedTo()).isEqualTo(DAB_SID_EXT_IDENTIFIER);
.that(DAB_PROGRAM_INFO.getPhysicallyTunedTo()).isEqualTo(DAB_FREQUENCY_IDENTIFIER);
}
@Test
@@ -1005,6 +1007,35 @@ public final class RadioManagerTest {
.that(modules).containsExactly(AMFM_PROPERTIES);
}
@Test
public void listModules_forRadioManagerWithNullListAsInput_fails() throws Exception {
createRadioManager();
assertWithMessage("Status when listing module with empty list input")
.that(mRadioManager.listModules(null)).isEqualTo(RadioManager.STATUS_BAD_VALUE);
}
@Test
public void listModules_withNullListFromService_fails() throws Exception {
createRadioManager();
when(mRadioServiceMock.listModules()).thenReturn(null);
List<RadioManager.ModuleProperties> modules = new ArrayList<>();
assertWithMessage("Status for listing module when getting null list from HAL client")
.that(mRadioManager.listModules(modules)).isEqualTo(RadioManager.STATUS_ERROR);
}
@Test
public void listModules_whenServiceDied_fails() throws Exception {
createRadioManager();
when(mRadioServiceMock.listModules()).thenThrow(new RemoteException());
List<RadioManager.ModuleProperties> modules = new ArrayList<>();
assertWithMessage("Status for listing module when HAL client service is dead")
.that(mRadioManager.listModules(modules))
.isEqualTo(RadioManager.STATUS_DEAD_OBJECT);
}
@Test
public void openTuner_forRadioModule() throws Exception {
createRadioManager();
@@ -1018,6 +1049,18 @@ public final class RadioManagerTest {
anyInt());
}
@Test
public void openTuner_whenServiceDied_returnsNull() throws Exception {
createRadioManager();
when(mRadioServiceMock.openTuner(anyInt(), any(), anyBoolean(), any(), anyInt()))
.thenThrow(new RemoteException());
RadioTuner nullTuner = mRadioManager.openTuner(/* moduleId= */ 0, FM_BAND_CONFIG,
/* withAudio= */ true, mCallbackMock, /* handler= */ null);
assertWithMessage("Radio tuner when service is dead").that(nullTuner).isNull();
}
@Test
public void addAnnouncementListener_withListenerNotAddedBefore() throws Exception {
createRadioManager();
@@ -1048,6 +1091,21 @@ public final class RadioManagerTest {
verify(mCloseHandleMock).close();
}
@Test
public void addAnnouncementListener_whenServiceDied_throwException() throws Exception {
createRadioManager();
String exceptionMessage = "service is dead";
when(mRadioServiceMock.addAnnouncementListener(any(), any()))
.thenThrow(new RemoteException(exceptionMessage));
Set<Integer> enableTypeSet = createAnnouncementTypeSet(EVENT_ANNOUNCEMENT_TYPE);
RuntimeException thrown = assertThrows(RuntimeException.class,
() -> mRadioManager.addAnnouncementListener(enableTypeSet, mEventListener));
assertWithMessage("Exception for adding announcement listener with dead service")
.that(thrown).hasMessageThat().contains(exceptionMessage);
}
@Test
public void removeAnnouncementListener_withListenerNotAddedBefore_ignores() throws Exception {
createRadioManager();
@@ -1104,8 +1162,8 @@ public final class RadioManagerTest {
}
private static RadioManager.ProgramInfo createDabProgramInfo(ProgramSelector selector) {
return new RadioManager.ProgramInfo(selector, DAB_FREQUENCY_IDENTIFIER,
DAB_SID_EXT_IDENTIFIER, Arrays.asList(DAB_SID_EXT_IDENTIFIER_RELATED), INFO_FLAGS,
return new RadioManager.ProgramInfo(selector, DAB_SID_EXT_IDENTIFIER,
DAB_FREQUENCY_IDENTIFIER, Arrays.asList(DAB_SID_EXT_IDENTIFIER_RELATED), INFO_FLAGS,
SIGNAL_QUALITY, METADATA, /* vendorInfo= */ null);
}

View File

@@ -18,10 +18,13 @@ package android.hardware.radio.tests.unittests;
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.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -37,6 +40,7 @@ import android.hardware.radio.RadioManager;
import android.hardware.radio.RadioMetadata;
import android.hardware.radio.RadioTuner;
import android.os.Build;
import android.os.RemoteException;
import org.junit.After;
import org.junit.Before;
@@ -46,7 +50,6 @@ import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
@@ -130,6 +133,24 @@ public final class TunerAdapterTest {
.that(status).isEqualTo(RadioManager.STATUS_OK);
}
@Test
public void setConfiguration_withInvalidParameters_fails() throws Exception {
doThrow(new IllegalArgumentException()).when(mTunerMock).setConfiguration(any());
assertWithMessage("Status for setting configuration with invalid parameters")
.that(mRadioTuner.setConfiguration(TEST_BAND_CONFIG))
.isEqualTo(RadioManager.STATUS_BAD_VALUE);
}
@Test
public void setConfiguration_whenServiceDied_fails() throws Exception {
doThrow(new RemoteException()).when(mTunerMock).setConfiguration(any());
assertWithMessage("Status for setting configuration when service is dead")
.that(mRadioTuner.setConfiguration(TEST_BAND_CONFIG))
.isEqualTo(RadioManager.STATUS_DEAD_OBJECT);
}
@Test
public void getConfiguration_forTunerAdapter() throws Exception {
when(mTunerMock.getConfiguration()).thenReturn(TEST_BAND_CONFIG);
@@ -143,14 +164,52 @@ public final class TunerAdapterTest {
.that(bandConfigs[0]).isEqualTo(TEST_BAND_CONFIG);
}
@Test
public void getConfiguration_withInvalidParameters_fails() throws Exception {
RadioManager.BandConfig[] bandConfigs = new RadioManager.BandConfig[0];
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
() -> mRadioTuner.getConfiguration(bandConfigs));
assertWithMessage("Exception for getting configuration with invalid parameters")
.that(thrown).hasMessageThat().contains("must be an array of length 1");
}
@Test
public void getConfiguration_whenServiceDied_fails() throws Exception {
doThrow(new RemoteException()).when(mTunerMock).getConfiguration();
RadioManager.BandConfig[] bandConfigs = new RadioManager.BandConfig[1];
assertWithMessage("Status for getting configuration when service is dead")
.that(mRadioTuner.getConfiguration(bandConfigs))
.isEqualTo(RadioManager.STATUS_DEAD_OBJECT);
}
@Test
public void setMute_forTunerAdapter() {
int status = mRadioTuner.setMute(/* mute= */ true);
int status = mRadioTuner.setMute(true);
assertWithMessage("Status for setting mute")
.that(status).isEqualTo(RadioManager.STATUS_OK);
}
@Test
public void setMute_whenIllegalState_fails() throws Exception {
doThrow(new IllegalStateException()).when(mTunerMock).setMuted(anyBoolean());
assertWithMessage("Status for setting muted when service is in illegal state")
.that(mRadioTuner.setMute(true)).isEqualTo(RadioManager.STATUS_ERROR);
}
@Test
public void setMute_whenServiceDied_fails() throws Exception {
doThrow(new RemoteException()).when(mTunerMock).setMuted(anyBoolean());
assertWithMessage("Status for setting muted when service is dead")
.that(mRadioTuner.setMute(true))
.isEqualTo(RadioManager.STATUS_DEAD_OBJECT);
}
@Test
public void getMute_forTunerAdapter() throws Exception {
when(mTunerMock.isMuted()).thenReturn(true);
@@ -160,6 +219,14 @@ public final class TunerAdapterTest {
assertWithMessage("Mute status").that(muteStatus).isTrue();
}
@Test
public void getMute_whenServiceDied_returnsTrue() throws Exception {
when(mTunerMock.isMuted()).thenThrow(new RemoteException());
assertWithMessage("Status for getting muted when service is dead")
.that(mRadioTuner.getMute()).isEqualTo(true);
}
@Test
public void step_forTunerAdapter_succeeds() throws Exception {
doAnswer(invocation -> {
@@ -175,6 +242,24 @@ public final class TunerAdapterTest {
verify(mCallbackMock, timeout(CALLBACK_TIMEOUT_MS)).onProgramInfoChanged(FM_PROGRAM_INFO);
}
@Test
public void step_whenIllegalState_fails() throws Exception {
doThrow(new IllegalStateException()).when(mTunerMock).step(anyBoolean(), anyBoolean());
assertWithMessage("Status for stepping when service is in illegal state")
.that(mRadioTuner.step(RadioTuner.DIRECTION_UP, /* skipSubChannel= */ false))
.isEqualTo(RadioManager.STATUS_INVALID_OPERATION);
}
@Test
public void step_whenServiceDied_fails() throws Exception {
doThrow(new RemoteException()).when(mTunerMock).step(anyBoolean(), anyBoolean());
assertWithMessage("Status for stepping when service is dead")
.that(mRadioTuner.step(RadioTuner.DIRECTION_UP, /* skipSubChannel= */ false))
.isEqualTo(RadioManager.STATUS_DEAD_OBJECT);
}
@Test
public void scan_forTunerAdapter_succeeds() throws Exception {
doAnswer(invocation -> {
@@ -190,6 +275,24 @@ public final class TunerAdapterTest {
verify(mCallbackMock, timeout(CALLBACK_TIMEOUT_MS)).onProgramInfoChanged(FM_PROGRAM_INFO);
}
@Test
public void scan_whenIllegalState_fails() throws Exception {
doThrow(new IllegalStateException()).when(mTunerMock).seek(anyBoolean(), anyBoolean());
assertWithMessage("Status for scanning when service is in illegal state")
.that(mRadioTuner.scan(RadioTuner.DIRECTION_UP, /* skipSubChannel= */ false))
.isEqualTo(RadioManager.STATUS_INVALID_OPERATION);
}
@Test
public void scan_whenServiceDied_fails() throws Exception {
doThrow(new RemoteException()).when(mTunerMock).seek(anyBoolean(), anyBoolean());
assertWithMessage("Status for scan when service is dead")
.that(mRadioTuner.scan(RadioTuner.DIRECTION_UP, /* skipSubChannel= */ true))
.isEqualTo(RadioManager.STATUS_DEAD_OBJECT);
}
@Test
public void seek_forTunerAdapter_succeeds() throws Exception {
doAnswer(invocation -> {
@@ -197,7 +300,7 @@ public final class TunerAdapterTest {
return RadioManager.STATUS_OK;
}).when(mTunerMock).seek(anyBoolean(), anyBoolean());
int scanStatus = mRadioTuner.scan(RadioTuner.DIRECTION_DOWN, /* skipSubChannel= */ false);
int scanStatus = mRadioTuner.seek(RadioTuner.DIRECTION_DOWN, /* skipSubChannel= */ false);
verify(mTunerMock).seek(/* directionDown= */ true, /* skipSubChannel= */ false);
assertWithMessage("Status for seeking")
@@ -212,12 +315,30 @@ public final class TunerAdapterTest {
return RadioManager.STATUS_OK;
}).when(mTunerMock).seek(anyBoolean(), anyBoolean());
mRadioTuner.scan(RadioTuner.DIRECTION_UP, /* skipSubChannel*/ true);
mRadioTuner.seek(RadioTuner.DIRECTION_UP, /* skipSubChannel= */ true);
verify(mCallbackMock, timeout(CALLBACK_TIMEOUT_MS)).onTuneFailed(
RadioTuner.TUNER_RESULT_TIMEOUT, FM_SELECTOR);
}
@Test
public void seek_whenIllegalState_fails() throws Exception {
doThrow(new IllegalStateException()).when(mTunerMock).seek(anyBoolean(), anyBoolean());
assertWithMessage("Status for seeking when service is in illegal state")
.that(mRadioTuner.seek(RadioTuner.DIRECTION_UP, /* skipSubChannel= */ false))
.isEqualTo(RadioManager.STATUS_INVALID_OPERATION);
}
@Test
public void seek_whenServiceDied_fails() throws Exception {
doThrow(new RemoteException()).when(mTunerMock).seek(anyBoolean(), anyBoolean());
assertWithMessage("Status for seeking when service is dead")
.that(mRadioTuner.seek(RadioTuner.DIRECTION_UP, /* skipSubChannel= */ true))
.isEqualTo(RadioManager.STATUS_DEAD_OBJECT);
}
@Test
public void tune_withChannelsForTunerAdapter_succeeds() {
int status = mRadioTuner.tune(/* channel= */ 92300, /* subChannel= */ 0);
@@ -227,6 +348,33 @@ public final class TunerAdapterTest {
verify(mCallbackMock, timeout(CALLBACK_TIMEOUT_MS)).onProgramInfoChanged(FM_PROGRAM_INFO);
}
@Test
public void tune_withInvalidChannel_fails() throws Exception {
doThrow(new IllegalArgumentException()).when(mTunerMock).tune(any());
assertWithMessage("Status for tuning when service is in illegal state")
.that(mRadioTuner.tune(/* channel= */ 300, /* subChannel= */ 0))
.isEqualTo(RadioManager.STATUS_BAD_VALUE);
}
@Test
public void tune_withChannelsWhenIllegalState_fails() throws Exception {
doThrow(new IllegalStateException()).when(mTunerMock).tune(any());
assertWithMessage("Status for tuning when service is in illegal state")
.that(mRadioTuner.tune(/* channel= */ 92300, /* subChannel= */ 0))
.isEqualTo(RadioManager.STATUS_INVALID_OPERATION);
}
@Test
public void tune_withChannelsWhenServiceDied_fails() throws Exception {
doThrow(new RemoteException()).when(mTunerMock).tune(any());
assertWithMessage("Status for tuning when service is dead")
.that(mRadioTuner.tune(/* channel= */ 92300, /* subChannel= */ 0))
.isEqualTo(RadioManager.STATUS_DEAD_OBJECT);
}
@Test
public void tune_withValidSelectorForTunerAdapter_succeeds() throws Exception {
mRadioTuner.tune(FM_SELECTOR);
@@ -249,6 +397,17 @@ public final class TunerAdapterTest {
.onTuneFailed(RadioTuner.TUNER_RESULT_INVALID_ARGUMENTS, invalidSelector);
}
@Test
public void tune_withSelectorWhenServiceDied_fails() throws Exception {
doThrow(new RemoteException()).when(mTunerMock).tune(any());
RuntimeException thrown = assertThrows(RuntimeException.class,
() -> mRadioTuner.tune(FM_SELECTOR));
assertWithMessage("Exception for tuning when service is dead")
.that(thrown).hasMessageThat().contains("Service died");
}
@Test
public void cancel_forTunerAdapter() throws Exception {
mRadioTuner.tune(FM_SELECTOR);
@@ -258,6 +417,22 @@ public final class TunerAdapterTest {
verify(mTunerMock).cancel();
}
@Test
public void cancel_whenIllegalState_fails() throws Exception {
doThrow(new IllegalStateException()).when(mTunerMock).cancel();
assertWithMessage("Status for canceling when service is in illegal state")
.that(mRadioTuner.cancel()).isEqualTo(RadioManager.STATUS_INVALID_OPERATION);
}
@Test
public void cancel_forTunerAdapterWhenServiceDied_fails() throws Exception {
doThrow(new RemoteException()).when(mTunerMock).cancel();
assertWithMessage("Status for canceling when service is dead")
.that(mRadioTuner.cancel()).isEqualTo(RadioManager.STATUS_DEAD_OBJECT);
}
@Test
public void cancelAnnouncement_forTunerAdapter() throws Exception {
mRadioTuner.cancelAnnouncement();
@@ -265,6 +440,17 @@ public final class TunerAdapterTest {
verify(mTunerMock).cancelAnnouncement();
}
@Test
public void cancelAnnouncement_whenServiceDied_fails() throws Exception {
doThrow(new RemoteException()).when(mTunerMock).cancelAnnouncement();
RuntimeException thrown = assertThrows(RuntimeException.class,
() -> mRadioTuner.cancelAnnouncement());
assertWithMessage("Exception for canceling announcement when service is dead")
.that(thrown).hasMessageThat().contains("Service died");
}
@Test
public void getProgramInfo_beforeProgramInfoSetForTunerAdapter() {
RadioManager.ProgramInfo[] programInfoArray = new RadioManager.ProgramInfo[1];
@@ -295,12 +481,23 @@ public final class TunerAdapterTest {
when(mTunerMock.getImage(anyInt())).thenReturn(bitmapExpected);
int imageId = 1;
Bitmap image = mRadioTuner.getMetadataImage(/* id= */ imageId);
Bitmap image = mRadioTuner.getMetadataImage(imageId);
assertWithMessage("Image obtained from id %s", imageId)
.that(image).isEqualTo(bitmapExpected);
}
@Test
public void getMetadataImage_whenServiceDied_fails() throws Exception {
when(mTunerMock.getImage(anyInt())).thenThrow(new RemoteException());
RuntimeException thrown = assertThrows(RuntimeException.class,
() -> mRadioTuner.getMetadataImage(/* id= */ 1));
assertWithMessage("Exception for getting metadata image when service is dead")
.that(thrown).hasMessageThat().contains("Service died");
}
@Test
public void startBackgroundScan_forTunerAdapter() throws Exception {
when(mTunerMock.startBackgroundScan()).thenReturn(false);
@@ -311,6 +508,17 @@ public final class TunerAdapterTest {
assertWithMessage("Status for starting background scan").that(scanStatus).isFalse();
}
@Test
public void startBackgroundScan_whenServiceDied_fails() throws Exception {
when(mTunerMock.startBackgroundScan()).thenThrow(new RemoteException());
RuntimeException thrown = assertThrows(RuntimeException.class,
() -> mRadioTuner.startBackgroundScan());
assertWithMessage("Exception for background scan when service is dead")
.that(thrown).hasMessageThat().contains("Service died");
}
@Test
public void isAnalogForced_forTunerAdapter() throws Exception {
when(mTunerMock.isConfigFlagSet(RadioManager.CONFIG_FORCE_ANALOG)).thenReturn(true);
@@ -321,6 +529,19 @@ public final class TunerAdapterTest {
.that(isAnalogForced).isTrue();
}
@Test
public void isAnalogForced_whenNotSupported_fails() throws Exception {
String errorMessage = "Analog forced switch is not supported";
when(mTunerMock.isConfigFlagSet(RadioManager.CONFIG_FORCE_ANALOG))
.thenThrow(new UnsupportedOperationException(errorMessage));
IllegalStateException thrown = assertThrows(IllegalStateException.class,
() -> mRadioTuner.isAnalogForced());
assertWithMessage("Exception for checking analog playback switch when not supported")
.that(thrown).hasMessageThat().contains(errorMessage);
}
@Test
public void setAnalogForced_forTunerAdapter() throws Exception {
boolean analogForced = true;
@@ -330,6 +551,19 @@ public final class TunerAdapterTest {
verify(mTunerMock).setConfigFlag(RadioManager.CONFIG_FORCE_ANALOG, analogForced);
}
@Test
public void setAnalogForced_whenNotSupported_fails() throws Exception {
String errorMessage = "Analog forced switch is not supported";
doThrow(new UnsupportedOperationException(errorMessage))
.when(mTunerMock).setConfigFlag(eq(RadioManager.CONFIG_FORCE_ANALOG), anyBoolean());
IllegalStateException thrown = assertThrows(IllegalStateException.class,
() -> mRadioTuner.setAnalogForced(/* isForced= */ false));
assertWithMessage("Exception for setting analog playback switch when not supported")
.that(thrown).hasMessageThat().contains(errorMessage);
}
@Test
public void isConfigFlagSupported_forTunerAdapter() throws Exception {
when(mTunerMock.isConfigFlagSupported(RadioManager.CONFIG_DAB_DAB_LINKING))
@@ -342,6 +576,17 @@ public final class TunerAdapterTest {
.that(dabFmSoftLinking).isTrue();
}
@Test
public void isConfigFlagSupported_whenServiceDied_fails() throws Exception {
when(mTunerMock.isConfigFlagSupported(anyInt())).thenThrow(new RemoteException());
RuntimeException thrown = assertThrows(RuntimeException.class,
() -> mRadioTuner.isConfigFlagSupported(RadioManager.CONFIG_DAB_DAB_LINKING));
assertWithMessage("Exception for checking config flag support when service is dead")
.that(thrown).hasMessageThat().contains("Service died");
}
@Test
public void isConfigFlagSet_forTunerAdapter() throws Exception {
when(mTunerMock.isConfigFlagSet(RadioManager.CONFIG_DAB_FM_SOFT_LINKING))
@@ -354,6 +599,17 @@ public final class TunerAdapterTest {
.that(dabFmSoftLinking).isTrue();
}
@Test
public void isConfigFlagSet_whenServiceDied_fails() throws Exception {
when(mTunerMock.isConfigFlagSet(anyInt())).thenThrow(new RemoteException());
RuntimeException thrown = assertThrows(RuntimeException.class,
() -> mRadioTuner.isConfigFlagSet(RadioManager.CONFIG_DAB_DAB_LINKING));
assertWithMessage("Exception for getting config flag when service is dead")
.that(thrown).hasMessageThat().contains("Service died");
}
@Test
public void setConfigFlag_forTunerAdapter() throws Exception {
boolean dabFmLinking = true;
@@ -363,9 +619,21 @@ public final class TunerAdapterTest {
verify(mTunerMock).setConfigFlag(RadioManager.CONFIG_DAB_FM_LINKING, dabFmLinking);
}
@Test
public void setConfigFlag_whenServiceDied_fails() throws Exception {
doThrow(new RemoteException()).when(mTunerMock).setConfigFlag(anyInt(), anyBoolean());
RuntimeException thrown = assertThrows(RuntimeException.class,
() -> mRadioTuner.setConfigFlag(RadioManager.CONFIG_DAB_DAB_LINKING,
/* value= */ true));
assertWithMessage("Exception for setting config flag when service is dead")
.that(thrown).hasMessageThat().contains("Service died");
}
@Test
public void getParameters_forTunerAdapter() throws Exception {
List<String> parameterKeys = Arrays.asList("ParameterKeyMock");
List<String> parameterKeys = List.of("ParameterKeyMock");
Map<String, String> parameters = Map.of("ParameterKeyMock", "ParameterValueMock");
when(mTunerMock.getParameters(parameterKeys)).thenReturn(parameters);
@@ -373,6 +641,18 @@ public final class TunerAdapterTest {
.that(mRadioTuner.getParameters(parameterKeys)).isEqualTo(parameters);
}
@Test
public void getParameters_whenServiceDied_fails() throws Exception {
List<String> parameterKeys = List.of("ParameterKeyMock");
when(mTunerMock.getParameters(parameterKeys)).thenThrow(new RemoteException());
RuntimeException thrown = assertThrows(RuntimeException.class,
() -> mRadioTuner.getParameters(parameterKeys));
assertWithMessage("Exception for getting parameters when service is dead")
.that(thrown).hasMessageThat().contains("Service died");
}
@Test
public void setParameters_forTunerAdapter() throws Exception {
Map<String, String> parameters = Map.of("ParameterKeyMock", "ParameterValueMock");
@@ -382,6 +662,18 @@ public final class TunerAdapterTest {
.that(mRadioTuner.setParameters(parameters)).isEqualTo(parameters);
}
@Test
public void setParameters_whenServiceDied_fails() throws Exception {
Map<String, String> parameters = Map.of("ParameterKeyMock", "ParameterValueMock");
when(mTunerMock.setParameters(parameters)).thenThrow(new RemoteException());
RuntimeException thrown = assertThrows(RuntimeException.class,
() -> mRadioTuner.setParameters(parameters));
assertWithMessage("Exception for setting parameters when service is dead")
.that(thrown).hasMessageThat().contains("Service died");
}
@Test
public void isAntennaConnected_forTunerAdapter() throws Exception {
mTunerCallback.onAntennaState(/* connected= */ false);
@@ -390,6 +682,15 @@ public final class TunerAdapterTest {
.that(mRadioTuner.isAntennaConnected()).isFalse();
}
@Test
public void onError_forTunerAdapter() throws Exception {
int errorStatus = RadioTuner.ERROR_HARDWARE_FAILURE;
mTunerCallback.onError(errorStatus);
verify(mCallbackMock, timeout(CALLBACK_TIMEOUT_MS)).onError(errorStatus);
}
@Test
public void hasControl_forTunerAdapter() throws Exception {
when(mTunerMock.isClosed()).thenReturn(true);
@@ -397,6 +698,14 @@ public final class TunerAdapterTest {
assertWithMessage("Control on tuner").that(mRadioTuner.hasControl()).isFalse();
}
@Test
public void hasControl_whenServiceDied_returnsFalse() throws Exception {
when(mTunerMock.isClosed()).thenThrow(new RemoteException());
assertWithMessage("Control on tuner when service is dead")
.that(mRadioTuner.hasControl()).isFalse();
}
@Test
public void onConfigurationChanged_forTunerCallbackAdapter() throws Exception {
mTunerCallback.onConfigurationChanged(TEST_BAND_CONFIG);

View File

@@ -19,6 +19,7 @@ import android.hardware.broadcastradio.IdentifierType;
import android.hardware.broadcastradio.Metadata;
import android.hardware.broadcastradio.ProgramIdentifier;
import android.hardware.broadcastradio.ProgramInfo;
import android.hardware.broadcastradio.ProgramListChunk;
import android.hardware.broadcastradio.VendorKeyValue;
import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
@@ -41,17 +42,25 @@ final class AidlTestUtils {
/* dabFrequencyTable= */ null, /* vendorInfo= */ null);
}
static RadioManager.ProgramInfo makeProgramInfo(ProgramSelector selector, int signalQuality) {
static RadioManager.ProgramInfo makeProgramInfo(ProgramSelector selector,
ProgramSelector.Identifier logicallyTunedTo,
ProgramSelector.Identifier physicallyTunedTo, int signalQuality) {
return new RadioManager.ProgramInfo(selector,
selector.getPrimaryId(), selector.getPrimaryId(), /* relatedContents= */ null,
logicallyTunedTo, physicallyTunedTo, /* relatedContents= */ null,
/* infoFlags= */ 0, signalQuality,
new RadioMetadata.Builder().build(), new ArrayMap<>());
}
static RadioManager.ProgramInfo makeProgramInfo(int programType,
ProgramSelector.Identifier identifier, int signalQuality) {
ProgramSelector selector = makeProgramSelector(programType, identifier);
return makeProgramInfo(selector, signalQuality);
static RadioManager.ProgramInfo makeProgramInfo(ProgramSelector selector, int signalQuality) {
return makeProgramInfo(selector, selector.getPrimaryId(), selector.getPrimaryId(),
signalQuality);
}
static ProgramIdentifier makeHalIdentifier(@IdentifierType int type, long value) {
ProgramIdentifier halDabId = new ProgramIdentifier();
halDabId.type = type;
halDabId.value = value;
return halDabId;
}
static ProgramSelector makeFmSelector(long freq) {
@@ -67,44 +76,48 @@ final class AidlTestUtils {
}
static android.hardware.broadcastradio.ProgramSelector makeHalFmSelector(int freq) {
ProgramIdentifier halId = new ProgramIdentifier();
halId.type = IdentifierType.AMFM_FREQUENCY_KHZ;
halId.value = freq;
android.hardware.broadcastradio.ProgramSelector halSelector =
new android.hardware.broadcastradio.ProgramSelector();
halSelector.primaryId = halId;
halSelector.secondaryIds = new ProgramIdentifier[0];
return halSelector;
ProgramIdentifier halId = makeHalIdentifier(IdentifierType.AMFM_FREQUENCY_KHZ, freq);
return makeHalSelector(halId, /* secondaryIds= */ new ProgramIdentifier[0]);
}
static ProgramInfo programInfoToHalProgramInfo(RadioManager.ProgramInfo info) {
// Note that because ConversionUtils does not by design provide functions for all
// conversions, this function only copies fields that are set by makeProgramInfo().
ProgramInfo hwInfo = new ProgramInfo();
hwInfo.selector = ConversionUtils.programSelectorToHalProgramSelector(info.getSelector());
hwInfo.logicallyTunedTo =
ConversionUtils.identifierToHalProgramIdentifier(info.getLogicallyTunedTo());
hwInfo.physicallyTunedTo =
ConversionUtils.identifierToHalProgramIdentifier(info.getPhysicallyTunedTo());
hwInfo.signalQuality = info.getSignalStrength();
hwInfo.relatedContent = new ProgramIdentifier[]{};
hwInfo.metadata = new Metadata[]{};
return hwInfo;
static android.hardware.broadcastradio.ProgramSelector makeHalSelector(
ProgramIdentifier primaryId, ProgramIdentifier[] secondaryIds) {
android.hardware.broadcastradio.ProgramSelector hwSelector =
new android.hardware.broadcastradio.ProgramSelector();
hwSelector.primaryId = primaryId;
hwSelector.secondaryIds = secondaryIds;
return hwSelector;
}
static ProgramInfo makeHalProgramInfo(
android.hardware.broadcastradio.ProgramSelector hwSel, int hwSignalQuality) {
return makeHalProgramInfo(hwSel, hwSel.primaryId, hwSel.primaryId, hwSignalQuality);
}
static ProgramInfo makeHalProgramInfo(
android.hardware.broadcastradio.ProgramSelector hwSel,
ProgramIdentifier logicallyTunedTo, ProgramIdentifier physicallyTunedTo,
int hwSignalQuality) {
ProgramInfo hwInfo = new ProgramInfo();
hwInfo.selector = hwSel;
hwInfo.logicallyTunedTo = hwSel.primaryId;
hwInfo.physicallyTunedTo = hwSel.primaryId;
hwInfo.logicallyTunedTo = logicallyTunedTo;
hwInfo.physicallyTunedTo = physicallyTunedTo;
hwInfo.signalQuality = hwSignalQuality;
hwInfo.relatedContent = new ProgramIdentifier[]{};
hwInfo.metadata = new Metadata[]{};
return hwInfo;
}
static ProgramListChunk makeProgramListChunk(boolean purge, boolean complete,
ProgramInfo[] modified, ProgramIdentifier[] removed) {
ProgramListChunk halChunk = new ProgramListChunk();
halChunk.purge = purge;
halChunk.complete = complete;
halChunk.modified = modified;
halChunk.removed = removed;
return halChunk;
}
static VendorKeyValue makeVendorKeyValue(String vendorKey, String vendorValue) {
VendorKeyValue vendorKeyValue = new VendorKeyValue();
vendorKeyValue.key = vendorKey;

View File

@@ -172,6 +172,19 @@ public final class BroadcastRadioServiceImplTest extends ExtendedRadioMockitoTes
.that(thrown).hasMessageThat().contains("Cannot open session for non-current user");
}
@Test
public void openSession_withoutAudio_fails() throws Exception {
createBroadcastRadioService();
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
() -> mBroadcastRadioService.openSession(FM_RADIO_MODULE_ID,
/* legacyConfig= */ null, /* withAudio= */ false, mTunerCallbackMock,
TARGET_SDK_VERSION));
assertWithMessage("Exception for opening session without audio")
.that(thrown).hasMessageThat().contains("not supported");
}
@Test
public void binderDied_forDeathRecipient() throws Exception {
createBroadcastRadioService();

View File

@@ -20,9 +20,13 @@ import android.hardware.broadcastradio.AmFmBandRange;
import android.hardware.broadcastradio.AmFmRegionConfig;
import android.hardware.broadcastradio.DabTableEntry;
import android.hardware.broadcastradio.IdentifierType;
import android.hardware.broadcastradio.ProgramIdentifier;
import android.hardware.broadcastradio.ProgramInfo;
import android.hardware.broadcastradio.ProgramListChunk;
import android.hardware.broadcastradio.Properties;
import android.hardware.broadcastradio.VendorKeyValue;
import android.hardware.radio.Announcement;
import android.hardware.radio.ProgramList;
import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
import android.os.Build;
@@ -33,19 +37,20 @@ import org.junit.Rule;
import org.junit.Test;
import java.util.Map;
import java.util.Set;
public final class ConversionUtilsTest {
private static final int FM_LOWER_LIMIT = 87500;
private static final int FM_UPPER_LIMIT = 108000;
private static final int FM_LOWER_LIMIT = 87_500;
private static final int FM_UPPER_LIMIT = 108_000;
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_UPPER_LIMIT = 1_700;
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 int DAB_ENTRY_FREQUENCY_1 = 174_928;
private static final String DAB_ENTRY_LABEL_2 = "12D";
private static final int DAB_ENTRY_FREQUENCY_2 = 229072;
private static final int DAB_ENTRY_FREQUENCY_2 = 229_072;
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";
@@ -57,6 +62,50 @@ public final class ConversionUtilsTest {
private static final String TEST_VERSION = "versionMock";
private static final String TEST_SERIAL = "serialMock";
private static final int TEST_SIGNAL_QUALITY = 1;
private static final long TEST_DAB_DMB_SID_EXT_VALUE = 0xA000000111L;
private static final long TEST_DAB_ENSEMBLE_VALUE = 0x1001;
private static final long TEST_DAB_FREQUENCY_VALUE = 220_352;
private static final long TEST_FM_FREQUENCY_VALUE = 92_100;
private static final long TEST_VENDOR_ID_VALUE = 9_901;
private static final ProgramSelector.Identifier TEST_DAB_SID_EXT_ID =
new ProgramSelector.Identifier(
ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT, TEST_DAB_DMB_SID_EXT_VALUE);
private static final ProgramSelector.Identifier TEST_DAB_ENSEMBLE_ID =
new ProgramSelector.Identifier(
ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE, TEST_DAB_ENSEMBLE_VALUE);
private static final ProgramSelector.Identifier TEST_DAB_FREQUENCY_ID =
new ProgramSelector.Identifier(
ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY, TEST_DAB_FREQUENCY_VALUE);
private static final ProgramSelector.Identifier TEST_FM_FREQUENCY_ID =
new ProgramSelector.Identifier(
ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY, TEST_FM_FREQUENCY_VALUE);
private static final ProgramSelector.Identifier TEST_VENDOR_ID =
new ProgramSelector.Identifier(
ProgramSelector.IDENTIFIER_TYPE_VENDOR_START, TEST_VENDOR_ID_VALUE);
private static final ProgramIdentifier TEST_HAL_DAB_SID_EXT_ID =
AidlTestUtils.makeHalIdentifier(IdentifierType.DAB_SID_EXT, TEST_DAB_DMB_SID_EXT_VALUE);
private static final ProgramIdentifier TEST_HAL_DAB_ENSEMBLE_ID =
AidlTestUtils.makeHalIdentifier(IdentifierType.DAB_ENSEMBLE, TEST_DAB_ENSEMBLE_VALUE);
private static final ProgramIdentifier TEST_HAL_DAB_FREQUENCY_ID =
AidlTestUtils.makeHalIdentifier(IdentifierType.DAB_FREQUENCY_KHZ,
TEST_DAB_FREQUENCY_VALUE);
private static final ProgramIdentifier TEST_HAL_FM_FREQUENCY_ID =
AidlTestUtils.makeHalIdentifier(IdentifierType.AMFM_FREQUENCY_KHZ,
TEST_FM_FREQUENCY_VALUE);
private static final ProgramIdentifier TEST_HAL_VENDOR_ID =
AidlTestUtils.makeHalIdentifier(IdentifierType.VENDOR_START,
TEST_VENDOR_ID_VALUE);
private static final ProgramSelector TEST_DAB_SELECTOR = new ProgramSelector(
ProgramSelector.PROGRAM_TYPE_DAB, TEST_DAB_SID_EXT_ID,
new ProgramSelector.Identifier[]{TEST_DAB_FREQUENCY_ID, TEST_DAB_ENSEMBLE_ID},
/* vendorIds= */ null);
private static final ProgramSelector TEST_FM_SELECTOR =
AidlTestUtils.makeFmSelector(TEST_FM_FREQUENCY_VALUE);
private static final int TEST_ENABLED_TYPE = Announcement.TYPE_EMERGENCY;
private static final int TEST_ANNOUNCEMENT_FREQUENCY = FM_LOWER_LIMIT + FM_SPACING;
@@ -158,6 +207,246 @@ public final class ConversionUtilsTest {
.that(bands[1].getSpacing()).isEqualTo(AM_SPACING);
}
@Test
public void identifierToHalProgramIdentifier_withDabId() {
ProgramIdentifier halDabId =
ConversionUtils.identifierToHalProgramIdentifier(TEST_DAB_SID_EXT_ID);
expect.withMessage("Converted HAL DAB identifier").that(halDabId)
.isEqualTo(TEST_HAL_DAB_SID_EXT_ID);
}
@Test
public void identifierFromHalProgramIdentifier_withDabId() {
ProgramSelector.Identifier dabId =
ConversionUtils.identifierFromHalProgramIdentifier(TEST_HAL_DAB_SID_EXT_ID);
expect.withMessage("Converted DAB identifier").that(dabId).isEqualTo(TEST_DAB_SID_EXT_ID);
}
@Test
public void programSelectorToHalProgramSelector_withValidSelector() {
android.hardware.broadcastradio.ProgramSelector halDabSelector =
ConversionUtils.programSelectorToHalProgramSelector(TEST_DAB_SELECTOR);
expect.withMessage("Primary identifier of converted HAL DAB selector")
.that(halDabSelector.primaryId).isEqualTo(TEST_HAL_DAB_SID_EXT_ID);
expect.withMessage("Secondary identifiers of converted HAL DAB selector")
.that(halDabSelector.secondaryIds).asList()
.containsExactly(TEST_HAL_DAB_FREQUENCY_ID, TEST_HAL_DAB_ENSEMBLE_ID);
}
@Test
public void programSelectorToHalProgramSelector_withInvalidDabSelector_returnsNull() {
ProgramSelector invalidDbSelector = new ProgramSelector(ProgramSelector.PROGRAM_TYPE_DAB,
TEST_DAB_SID_EXT_ID,
new ProgramSelector.Identifier[0],
new long[0]);
android.hardware.broadcastradio.ProgramSelector invalidHalDabSelector =
ConversionUtils.programSelectorToHalProgramSelector(invalidDbSelector);
expect.withMessage("Invalid HAL DAB selector without required secondary ids")
.that(invalidHalDabSelector).isNull();
}
@Test
public void programSelectorFromHalProgramSelector_withValidSelector() {
android.hardware.broadcastradio.ProgramSelector halDabSelector =
AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{
TEST_HAL_DAB_ENSEMBLE_ID, TEST_HAL_DAB_FREQUENCY_ID});
ProgramSelector dabSelector =
ConversionUtils.programSelectorFromHalProgramSelector(halDabSelector);
expect.withMessage("Primary identifier of converted DAB selector")
.that(dabSelector.getPrimaryId()).isEqualTo(TEST_DAB_SID_EXT_ID);
expect.withMessage("Secondary identifiers of converted DAB selector")
.that(dabSelector.getSecondaryIds()).asList()
.containsExactly(TEST_DAB_FREQUENCY_ID, TEST_DAB_ENSEMBLE_ID);
}
@Test
public void programSelectorFromHalProgramSelector_withInvalidSelector_returnsNull() {
android.hardware.broadcastradio.ProgramSelector invalidHalDabSelector =
AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{});
ProgramSelector invalidDabSelector =
ConversionUtils.programSelectorFromHalProgramSelector(invalidHalDabSelector);
expect.withMessage("Invalid DAB selector without required secondary ids")
.that(invalidDabSelector).isNull();
}
@Test
public void programInfoFromHalProgramInfo_withValidProgramInfo() {
android.hardware.broadcastradio.ProgramSelector halDabSelector =
AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{
TEST_HAL_DAB_ENSEMBLE_ID, TEST_HAL_DAB_FREQUENCY_ID});
ProgramInfo halProgramInfo = AidlTestUtils.makeHalProgramInfo(halDabSelector,
TEST_HAL_DAB_SID_EXT_ID, TEST_HAL_DAB_FREQUENCY_ID, TEST_SIGNAL_QUALITY);
RadioManager.ProgramInfo programInfo =
ConversionUtils.programInfoFromHalProgramInfo(halProgramInfo);
expect.withMessage("Primary id of selector of converted program info")
.that(programInfo.getSelector().getPrimaryId()).isEqualTo(TEST_DAB_SID_EXT_ID);
expect.withMessage("Secondary id of selector of converted program info")
.that(programInfo.getSelector().getSecondaryIds()).asList()
.containsExactly(TEST_DAB_ENSEMBLE_ID, TEST_DAB_FREQUENCY_ID);
expect.withMessage("Logically tuned identifier of converted program info")
.that(programInfo.getLogicallyTunedTo()).isEqualTo(TEST_DAB_SID_EXT_ID);
expect.withMessage("Physically tuned identifier of converted program info")
.that(programInfo.getPhysicallyTunedTo()).isEqualTo(TEST_DAB_FREQUENCY_ID);
expect.withMessage("Signal quality of converted program info")
.that(programInfo.getSignalStrength()).isEqualTo(TEST_SIGNAL_QUALITY);
}
@Test
public void programInfoFromHalProgramInfo_withInvalidDabProgramInfo() {
android.hardware.broadcastradio.ProgramSelector invalidHalDabSelector =
AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID,
new ProgramIdentifier[]{TEST_HAL_DAB_ENSEMBLE_ID, TEST_HAL_DAB_FREQUENCY_ID});
ProgramInfo halProgramInfo = AidlTestUtils.makeHalProgramInfo(invalidHalDabSelector,
TEST_HAL_DAB_SID_EXT_ID, TEST_HAL_DAB_ENSEMBLE_ID, TEST_SIGNAL_QUALITY);
RadioManager.ProgramInfo programInfo =
ConversionUtils.programInfoFromHalProgramInfo(halProgramInfo);
expect.withMessage("Invalid DAB program info with incorrect type of physically tuned to id")
.that(programInfo).isNull();
}
@Test
public void chunkFromHalProgramListChunk_withValidChunk() {
boolean purge = false;
boolean complete = true;
android.hardware.broadcastradio.ProgramSelector halDabSelector =
AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{
TEST_HAL_DAB_ENSEMBLE_ID, TEST_HAL_DAB_FREQUENCY_ID});
ProgramInfo halDabInfo = AidlTestUtils.makeHalProgramInfo(halDabSelector,
TEST_HAL_DAB_SID_EXT_ID, TEST_HAL_DAB_FREQUENCY_ID, TEST_SIGNAL_QUALITY);
RadioManager.ProgramInfo dabInfo =
ConversionUtils.programInfoFromHalProgramInfo(halDabInfo);
ProgramListChunk halChunk = AidlTestUtils.makeProgramListChunk(purge, complete,
new ProgramInfo[]{halDabInfo},
new ProgramIdentifier[]{TEST_HAL_VENDOR_ID, TEST_HAL_FM_FREQUENCY_ID});
ProgramList.Chunk chunk = ConversionUtils.chunkFromHalProgramListChunk(halChunk);
expect.withMessage("Purged state of the converted valid program list chunk")
.that(chunk.isPurge()).isEqualTo(purge);
expect.withMessage("Completion state of the converted valid program list chunk")
.that(chunk.isComplete()).isEqualTo(complete);
expect.withMessage("Modified program info in the converted valid program list chunk")
.that(chunk.getModified()).containsExactly(dabInfo);
expect.withMessage("Removed program ides in the converted valid program list chunk")
.that(chunk.getRemoved()).containsExactly(TEST_VENDOR_ID, TEST_FM_FREQUENCY_ID);
}
@Test
public void chunkFromHalProgramListChunk_withInvalidModifiedProgramInfo() {
boolean purge = true;
boolean complete = false;
android.hardware.broadcastradio.ProgramSelector halDabSelector =
AidlTestUtils.makeHalSelector(TEST_HAL_DAB_SID_EXT_ID, new ProgramIdentifier[]{
TEST_HAL_DAB_ENSEMBLE_ID, TEST_HAL_DAB_FREQUENCY_ID});
ProgramInfo halDabInfo = AidlTestUtils.makeHalProgramInfo(halDabSelector,
TEST_HAL_DAB_SID_EXT_ID, TEST_HAL_DAB_ENSEMBLE_ID, TEST_SIGNAL_QUALITY);
ProgramListChunk halChunk = AidlTestUtils.makeProgramListChunk(purge, complete,
new ProgramInfo[]{halDabInfo}, new ProgramIdentifier[]{TEST_HAL_FM_FREQUENCY_ID});
ProgramList.Chunk chunk = ConversionUtils.chunkFromHalProgramListChunk(halChunk);
expect.withMessage("Purged state of the converted invalid program list chunk")
.that(chunk.isPurge()).isEqualTo(purge);
expect.withMessage("Completion state of the converted invalid program list chunk")
.that(chunk.isComplete()).isEqualTo(complete);
expect.withMessage("Modified program info in the converted invalid program list chunk")
.that(chunk.getModified()).isEmpty();
expect.withMessage("Removed program ids in the converted invalid program list chunk")
.that(chunk.getRemoved()).containsExactly(TEST_FM_FREQUENCY_ID);
}
@Test
public void programSelectorMeetsSdkVersionRequirement_withLowerVersionId_returnsFalse() {
expect.withMessage("Selector %s without required SDK version", TEST_DAB_SELECTOR)
.that(ConversionUtils.programSelectorMeetsSdkVersionRequirement(TEST_DAB_SELECTOR,
Build.VERSION_CODES.TIRAMISU)).isFalse();
}
@Test
public void programSelectorMeetsSdkVersionRequirement_withRequiredVersionId_returnsTrue() {
expect.withMessage("Selector %s with required SDK version", TEST_FM_SELECTOR)
.that(ConversionUtils.programSelectorMeetsSdkVersionRequirement(TEST_FM_SELECTOR,
Build.VERSION_CODES.TIRAMISU)).isTrue();
}
@Test
public void programInfoMeetsSdkVersionRequirement_withLowerVersionId_returnsFalse() {
RadioManager.ProgramInfo dabProgramInfo = AidlTestUtils.makeProgramInfo(TEST_DAB_SELECTOR,
TEST_DAB_SID_EXT_ID, TEST_DAB_FREQUENCY_ID, TEST_SIGNAL_QUALITY);
expect.withMessage("Program info %s without required SDK version", dabProgramInfo)
.that(ConversionUtils.programInfoMeetsSdkVersionRequirement(dabProgramInfo,
Build.VERSION_CODES.TIRAMISU)).isFalse();
}
@Test
public void programInfoMeetsSdkVersionRequirement_withRequiredVersionId_returnsTrue() {
RadioManager.ProgramInfo fmProgramInfo = AidlTestUtils.makeProgramInfo(TEST_FM_SELECTOR,
TEST_SIGNAL_QUALITY);
expect.withMessage("Program info %s with required SDK version", fmProgramInfo)
.that(ConversionUtils.programInfoMeetsSdkVersionRequirement(fmProgramInfo,
Build.VERSION_CODES.TIRAMISU)).isTrue();
}
@Test
public void convertChunkToTargetSdkVersion_withLowerSdkVersion() {
RadioManager.ProgramInfo dabProgramInfo = AidlTestUtils.makeProgramInfo(TEST_DAB_SELECTOR,
TEST_DAB_SID_EXT_ID, TEST_DAB_FREQUENCY_ID, TEST_SIGNAL_QUALITY);
RadioManager.ProgramInfo fmProgramInfo = AidlTestUtils.makeProgramInfo(TEST_FM_SELECTOR,
TEST_SIGNAL_QUALITY);
ProgramList.Chunk chunk = new ProgramList.Chunk(/* purge= */ true,
/* complete= */ true, Set.of(dabProgramInfo, fmProgramInfo),
Set.of(TEST_DAB_SID_EXT_ID, TEST_DAB_ENSEMBLE_ID, TEST_VENDOR_ID));
ProgramList.Chunk convertedChunk = ConversionUtils.convertChunkToTargetSdkVersion(chunk,
Build.VERSION_CODES.TIRAMISU);
expect.withMessage(
"Purged state of the converted program list chunk with lower SDK version")
.that(convertedChunk.isPurge()).isEqualTo(chunk.isPurge());
expect.withMessage(
"Completion state of the converted program list chunk with lower SDK version")
.that(convertedChunk.isComplete()).isEqualTo(chunk.isComplete());
expect.withMessage(
"Modified program info in the converted program list chunk with lower SDK version")
.that(convertedChunk.getModified()).containsExactly(fmProgramInfo);
expect.withMessage(
"Removed program ids in the converted program list chunk with lower SDK version")
.that(convertedChunk.getRemoved())
.containsExactly(TEST_DAB_ENSEMBLE_ID, TEST_VENDOR_ID);
}
@Test
public void convertChunkToTargetSdkVersion_withRequiredSdkVersion() {
RadioManager.ProgramInfo dabProgramInfo = AidlTestUtils.makeProgramInfo(TEST_DAB_SELECTOR,
TEST_DAB_SID_EXT_ID, TEST_DAB_FREQUENCY_ID, TEST_SIGNAL_QUALITY);
RadioManager.ProgramInfo fmProgramInfo = AidlTestUtils.makeProgramInfo(TEST_FM_SELECTOR,
TEST_SIGNAL_QUALITY);
ProgramList.Chunk chunk = new ProgramList.Chunk(/* purge= */ true,
/* complete= */ true, Set.of(dabProgramInfo, fmProgramInfo),
Set.of(TEST_DAB_SID_EXT_ID, TEST_DAB_ENSEMBLE_ID, TEST_VENDOR_ID));
ProgramList.Chunk convertedChunk = ConversionUtils.convertChunkToTargetSdkVersion(chunk,
Build.VERSION_CODES.CUR_DEVELOPMENT);
expect.withMessage("Converted program list chunk with required SDK version")
.that(convertedChunk).isEqualTo(chunk);
}
@Test
public void announcementFromHalAnnouncement_typesMatch() {
expect.withMessage("Announcement type")

View File

@@ -26,7 +26,9 @@ import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doThrow;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.timeout;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
@@ -43,6 +45,8 @@ import android.hardware.radio.ProgramSelector;
import android.hardware.radio.RadioManager;
import android.hardware.radio.RadioTuner;
import android.os.Build;
import android.os.ParcelableException;
import android.os.RemoteException;
import android.os.ServiceSpecificException;
import android.util.ArrayMap;
import android.util.ArraySet;
@@ -57,7 +61,6 @@ import org.junit.Test;
import org.mockito.Mock;
import org.mockito.verification.VerificationWithTimeout;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
@@ -71,10 +74,10 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase {
timeout(/* millis= */ 200);
private static final int SIGNAL_QUALITY = 1;
private static final long AM_FM_FREQUENCY_SPACING = 500;
private static final long[] AM_FM_FREQUENCY_LIST = {97500, 98100, 99100};
private static final long[] AM_FM_FREQUENCY_LIST = {97_500, 98_100, 99_100};
private static final RadioManager.FmBandDescriptor FM_BAND_DESCRIPTOR =
new RadioManager.FmBandDescriptor(RadioManager.REGION_ITU_1, RadioManager.BAND_FM,
/* lowerLimit= */ 87500, /* upperLimit= */ 108000, /* spacing= */ 100,
/* lowerLimit= */ 87_500, /* upperLimit= */ 108_000, /* spacing= */ 100,
/* stereo= */ false, /* rds= */ false, /* ta= */ false, /* af= */ false,
/* ea= */ false);
private static final RadioManager.BandConfig FM_BAND_CONFIG =
@@ -199,6 +202,17 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase {
verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onConfigurationChanged(FM_BAND_CONFIG);
}
@Test
public void setConfiguration_forNonCurrentUser_doesNotInvokesCallback() throws Exception {
openAidlClients(/* numClients= */ 1);
doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
mTunerSessions[0].setConfiguration(FM_BAND_CONFIG);
verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0))
.onConfigurationChanged(FM_BAND_CONFIG);
}
@Test
public void getConfiguration() throws Exception {
openAidlClients(/* numClients= */ 1);
@@ -330,10 +344,17 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase {
@Test
public void tune_withUnsupportedSelector_throwsException() throws Exception {
ProgramSelector.Identifier dabPrimaryId =
new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT,
/* value= */ 0xA000000111L);
ProgramSelector.Identifier[] dabSecondaryIds = new ProgramSelector.Identifier[]{
new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE,
/* value= */ 1337),
new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY,
/* value= */ 225648)};
ProgramSelector unsupportedSelector = new ProgramSelector(ProgramSelector.PROGRAM_TYPE_DAB,
dabPrimaryId, dabSecondaryIds, /* vendorIds= */ null);
openAidlClients(/* numClients= */ 1);
ProgramSelector unsupportedSelector = AidlTestUtils.makeProgramSelector(
ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY, new ProgramSelector.Identifier(
ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY, /* value= */ 300));
UnsupportedOperationException thrown = assertThrows(UnsupportedOperationException.class,
() -> mTunerSessions[0].tune(unsupportedSelector));
@@ -343,7 +364,22 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase {
}
@Test
public void tune_forCurrentUser_doesNotTune() throws Exception {
public void tune_withInvalidSelector_throwsIllegalArgumentException() throws Exception {
openAidlClients(/* numClients= */ 1);
ProgramSelector.Identifier invalidDabId = new ProgramSelector.Identifier(
ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE, /* value= */ 0x1001);
ProgramSelector invalidSel = new ProgramSelector(ProgramSelector.PROGRAM_TYPE_DAB,
invalidDabId, new ProgramSelector.Identifier[0], new long[0]);
IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
() -> mTunerSessions[0].tune(invalidSel));
assertWithMessage("Exception for tuning on DAB selector without DAB_SID_EXT primary id")
.that(thrown).hasMessageThat().contains("tune: INVALID_ARGUMENTS");
}
@Test
public void tune_forNonCurrentUser_doesNotTune() throws Exception {
openAidlClients(/* numClients= */ 1);
doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
@@ -356,6 +392,21 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase {
.onCurrentProgramInfoChanged(tuneInfo);
}
@Test
public void tune_withHalHasUnknownError_fails() throws Exception {
openAidlClients(/* numClients= */ 1);
ProgramSelector sel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
doThrow(new ServiceSpecificException(Result.UNKNOWN_ERROR))
.when(mBroadcastRadioMock).tune(any());
ParcelableException thrown = assertThrows(ParcelableException.class, () -> {
mTunerSessions[0].tune(sel);
});
assertWithMessage("Exception for tuning when HAL has unknown error")
.that(thrown).hasMessageThat().contains("UNKNOWN_ERROR");
}
@Test
public void step_withDirectionUp() throws Exception {
long initFreq = AM_FM_FREQUENCY_LIST[1];
@@ -390,6 +441,35 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase {
.onCurrentProgramInfoChanged(stepDownInfo);
}
@Test
public void step_forNonCurrentUser_doesNotStep() throws Exception {
long initFreq = AM_FM_FREQUENCY_LIST[1];
ProgramSelector initialSel = AidlTestUtils.makeFmSelector(initFreq);
openAidlClients(/* numClients= */ 1);
mHalCurrentInfo = AidlTestUtils.makeHalProgramInfo(
ConversionUtils.programSelectorToHalProgramSelector(initialSel), SIGNAL_QUALITY);
doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
mTunerSessions[0].step(/* directionDown= */ true, /* skipSubChannel= */ false);
verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0))
.onCurrentProgramInfoChanged(any());
}
@Test
public void step_withHalInInvalidState_fails() throws Exception {
openAidlClients(/* numClients= */ 1);
doThrow(new ServiceSpecificException(Result.INVALID_STATE))
.when(mBroadcastRadioMock).step(anyBoolean());
IllegalStateException thrown = assertThrows(IllegalStateException.class, () -> {
mTunerSessions[0].step(/* directionDown= */ true, /* skipSubChannel= */ false);
});
assertWithMessage("Exception for stepping when HAL is in invalid state")
.that(thrown).hasMessageThat().contains("INVALID_STATE");
}
@Test
public void seek_withDirectionUp() throws Exception {
long initFreq = AM_FM_FREQUENCY_LIST[2];
@@ -432,10 +512,43 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase {
ConversionUtils.programSelectorToHalProgramSelector(initialSel), SIGNAL_QUALITY);
mTunerSessions[0].seek(/* directionDown= */ true, /* skipSubChannel= */ false);
verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT)
.onCurrentProgramInfoChanged(seekUpInfo);
}
@Test
public void seek_forNonCurrentUser_doesNotSeek() throws Exception {
long initFreq = AM_FM_FREQUENCY_LIST[2];
ProgramSelector initialSel = AidlTestUtils.makeFmSelector(initFreq);
RadioManager.ProgramInfo seekUpInfo = AidlTestUtils.makeProgramInfo(
AidlTestUtils.makeFmSelector(getSeekFrequency(initFreq, /* seekDown= */ true)),
SIGNAL_QUALITY);
openAidlClients(/* numClients= */ 1);
mHalCurrentInfo = AidlTestUtils.makeHalProgramInfo(
ConversionUtils.programSelectorToHalProgramSelector(initialSel), SIGNAL_QUALITY);
doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
mTunerSessions[0].seek(/* directionDown= */ true, /* skipSubChannel= */ false);
verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0))
.onCurrentProgramInfoChanged(seekUpInfo);
}
@Test
public void seek_withHalHasInternalError_fails() throws Exception {
openAidlClients(/* numClients= */ 1);
doThrow(new ServiceSpecificException(Result.INTERNAL_ERROR))
.when(mBroadcastRadioMock).seek(anyBoolean(), anyBoolean());
ParcelableException thrown = assertThrows(ParcelableException.class, () -> {
mTunerSessions[0].seek(/* directionDown= */ true, /* skipSubChannel= */ false);
});
assertWithMessage("Exception for seeking when HAL has internal error")
.that(thrown).hasMessageThat().contains("INTERNAL_ERROR");
}
@Test
public void cancel() throws Exception {
openAidlClients(/* numClients= */ 1);
@@ -447,6 +560,32 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase {
verify(mBroadcastRadioMock).cancel();
}
@Test
public void cancel_forNonCurrentUser_doesNotCancel() throws Exception {
openAidlClients(/* numClients= */ 1);
ProgramSelector initialSel = AidlTestUtils.makeFmSelector(AM_FM_FREQUENCY_LIST[1]);
mTunerSessions[0].tune(initialSel);
doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
mTunerSessions[0].cancel();
verify(mBroadcastRadioMock, never()).cancel();
}
@Test
public void cancel_whenHalThrowsRemoteException_fails() throws Exception {
openAidlClients(/* numClients= */ 1);
String exceptionMessage = "HAL service died.";
doThrow(new RemoteException(exceptionMessage)).when(mBroadcastRadioMock).cancel();
RuntimeException thrown = assertThrows(RuntimeException.class, () -> {
mTunerSessions[0].cancel();
});
assertWithMessage("Exception for canceling when HAL throws remote exception")
.that(thrown).hasMessageThat().contains(exceptionMessage);
}
@Test
public void getImage_withInvalidId_throwsIllegalArgumentException() throws Exception {
openAidlClients(/* numClients= */ 1);
@@ -470,6 +609,21 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase {
assertWithMessage("Null image").that(imageTest).isEqualTo(null);
}
@Test
public void getImage_whenHalThrowsException_fails() throws Exception {
openAidlClients(/* numClients= */ 1);
String exceptionMessage = "HAL service died.";
when(mBroadcastRadioMock.getImage(anyInt()))
.thenThrow(new RemoteException(exceptionMessage));
RuntimeException thrown = assertThrows(RuntimeException.class, () -> {
mTunerSessions[0].getImage(/* id= */ 1);
});
assertWithMessage("Exception for getting image when HAL throws remote exception")
.that(thrown).hasMessageThat().contains(exceptionMessage);
}
@Test
public void startBackgroundScan() throws Exception {
openAidlClients(/* numClients= */ 1);
@@ -479,6 +633,16 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase {
verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT).onBackgroundScanComplete();
}
@Test
public void startBackgroundScan_forNonCurrentUser_doesNotInvokesCallback() throws Exception {
openAidlClients(/* numClients= */ 1);
doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
mTunerSessions[0].startBackgroundScan();
verify(mAidlTunerCallbackMocks[0], CALLBACK_TIMEOUT.times(0)).onBackgroundScanComplete();
}
@Test
public void stopProgramListUpdates() throws Exception {
openAidlClients(/* numClients= */ 1);
@@ -491,6 +655,19 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase {
verify(mBroadcastRadioMock).stopProgramListUpdates();
}
@Test
public void stopProgramListUpdates_forNonCurrentUser_doesNotStopUpdates() throws Exception {
openAidlClients(/* numClients= */ 1);
ProgramList.Filter aidlFilter = new ProgramList.Filter(new ArraySet<>(), new ArraySet<>(),
/* includeCategories= */ true, /* excludeModifications= */ false);
mTunerSessions[0].startProgramListUpdates(aidlFilter);
doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
mTunerSessions[0].stopProgramListUpdates();
verify(mBroadcastRadioMock, never()).stopProgramListUpdates();
}
@Test
public void isConfigFlagSupported_withUnsupportedFlag_returnsFalse() throws Exception {
openAidlClients(/* numClients= */ 1);
@@ -546,6 +723,17 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase {
verify(mBroadcastRadioMock).setConfigFlag(flag, /* value= */ false);
}
@Test
public void setConfigFlag_forNonCurrentUser_doesNotSetConfigFlag() throws Exception {
openAidlClients(/* numClients= */ 1);
int flag = UNSUPPORTED_CONFIG_FLAG + 1;
doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
mTunerSessions[0].setConfigFlag(flag, /* value= */ true);
verify(mBroadcastRadioMock, never()).setConfigFlag(flag, /* value= */ true);
}
@Test
public void isConfigFlagSet_withUnsupportedFlag_throwsRuntimeException()
throws Exception {
@@ -556,7 +744,7 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase {
mTunerSessions[0].isConfigFlagSet(flag);
});
assertWithMessage("Exception for check if unsupported flag %s is set", flag)
assertWithMessage("Exception for checking if unsupported flag %s is set", flag)
.that(thrown).hasMessageThat().contains("isConfigFlagSet: NOT_SUPPORTED");
}
@@ -573,6 +761,20 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase {
.that(isSet).isEqualTo(expectedConfigFlagValue);
}
@Test
public void isConfigFlagSet_whenHalThrowsRemoteException_fails() throws Exception {
openAidlClients(/* numClients= */ 1);
int flag = UNSUPPORTED_CONFIG_FLAG + 1;
doThrow(new RemoteException()).when(mBroadcastRadioMock).isConfigFlagSet(anyInt());
RuntimeException thrown = assertThrows(RuntimeException.class, () -> {
mTunerSessions[0].isConfigFlagSet(flag);
});
assertWithMessage("Exception for checking config flag when HAL throws remote exception")
.that(thrown).hasMessageThat().contains("Failed to check flag");
}
@Test
public void setParameters_withMockParameters() throws Exception {
openAidlClients(/* numClients= */ 1);
@@ -585,17 +787,59 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase {
ConversionUtils.vendorInfoToHalVendorKeyValues(parametersSet));
}
@Test
public void setParameters_forNonCurrentUser_doesNotSetParameters() throws Exception {
openAidlClients(/* numClients= */ 1);
Map<String, String> parametersSet = Map.of("mockParam1", "mockValue1",
"mockParam2", "mockValue2");
doReturn(false).when(() -> RadioServiceUserController.isCurrentOrSystemUser());
mTunerSessions[0].setParameters(parametersSet);
verify(mBroadcastRadioMock, never()).setParameters(any());
}
@Test
public void setParameters_whenHalThrowsRemoteException_fails() throws Exception {
openAidlClients(/* numClients= */ 1);
Map<String, String> parametersSet = Map.of("mockParam1", "mockValue1",
"mockParam2", "mockValue2");
String exceptionMessage = "HAL service died.";
when(mBroadcastRadioMock.setParameters(any()))
.thenThrow(new RemoteException(exceptionMessage));
RuntimeException thrown = assertThrows(RuntimeException.class, () -> {
mTunerSessions[0].setParameters(parametersSet);
});
assertWithMessage("Exception for setting parameters when HAL throws remote exception")
.that(thrown).hasMessageThat().contains(exceptionMessage);
}
@Test
public void getParameters_withMockKeys() throws Exception {
openAidlClients(/* numClients= */ 1);
List<String> parameterKeys = new ArrayList<>(2);
parameterKeys.add("mockKey1");
parameterKeys.add("mockKey2");
List<String> parameterKeys = List.of("mockKey1", "mockKey2");
mTunerSessions[0].getParameters(parameterKeys);
verify(mBroadcastRadioMock).getParameters(
parameterKeys.toArray(new String[0]));
verify(mBroadcastRadioMock).getParameters(parameterKeys.toArray(new String[0]));
}
@Test
public void getParameters_whenServiceThrowsRemoteException_fails() throws Exception {
openAidlClients(/* numClients= */ 1);
List<String> parameterKeys = List.of("mockKey1", "mockKey2");
String exceptionMessage = "HAL service died.";
when(mBroadcastRadioMock.getParameters(any()))
.thenThrow(new RemoteException(exceptionMessage));
RuntimeException thrown = assertThrows(RuntimeException.class, () -> {
mTunerSessions[0].getParameters(parameterKeys);
});
assertWithMessage("Exception for getting parameters when HAL throws remote exception")
.that(thrown).hasMessageThat().contains(exceptionMessage);
}
@Test

View File

@@ -324,10 +324,17 @@ public final class TunerSessionHidlTest extends ExtendedRadioMockitoTestCase {
@Test
public void tune_withUnsupportedSelector_throwsException() throws Exception {
ProgramSelector.Identifier dabPrimaryId =
new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT,
/* value= */ 0xA00111);
ProgramSelector.Identifier[] dabSecondaryIds = new ProgramSelector.Identifier[]{
new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE,
/* value= */ 1337),
new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY,
/* value= */ 225648)};
ProgramSelector unsupportedSelector = new ProgramSelector(ProgramSelector.PROGRAM_TYPE_DAB,
dabPrimaryId, dabSecondaryIds, /* vendorIds= */ null);
openAidlClients(/* numClients= */ 1);
ProgramSelector unsupportedSelector = TestUtils.makeProgramSelector(
ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY, new ProgramSelector.Identifier(
ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY, /* value= */ 300));
UnsupportedOperationException thrown = assertThrows(UnsupportedOperationException.class,
() -> mTunerSessions[0].tune(unsupportedSelector));

View File

@@ -307,6 +307,9 @@ final class ConversionUtils {
static ProgramIdentifier identifierToHalProgramIdentifier(ProgramSelector.Identifier id) {
ProgramIdentifier hwId = new ProgramIdentifier();
hwId.type = id.getType();
if (hwId.type == ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT) {
hwId.type = IdentifierType.DAB_SID_EXT;
}
hwId.value = id.getValue();
return hwId;
}
@@ -317,9 +320,49 @@ final class ConversionUtils {
if (id.type == IdentifierType.INVALID) {
return null;
}
return new ProgramSelector.Identifier(id.type, id.value);
int idType;
if (id.type == IdentifierType.DAB_SID_EXT) {
idType = ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT;
} else {
idType = id.type;
}
return new ProgramSelector.Identifier(idType, id.value);
}
private static boolean isVendorIdentifierType(int idType) {
return idType >= IdentifierType.VENDOR_START && idType <= IdentifierType.VENDOR_END;
}
private static boolean isValidHalProgramSelector(
android.hardware.broadcastradio.ProgramSelector sel) {
if (sel.primaryId.type != IdentifierType.AMFM_FREQUENCY_KHZ
&& sel.primaryId.type != IdentifierType.RDS_PI
&& sel.primaryId.type != IdentifierType.HD_STATION_ID_EXT
&& sel.primaryId.type != IdentifierType.DAB_SID_EXT
&& sel.primaryId.type != IdentifierType.DRMO_SERVICE_ID
&& sel.primaryId.type != IdentifierType.SXM_SERVICE_ID
&& !isVendorIdentifierType(sel.primaryId.type)) {
return false;
}
if (sel.primaryId.type == IdentifierType.DAB_SID_EXT) {
boolean hasEnsemble = false;
boolean hasFrequency = false;
for (int i = 0; i < sel.secondaryIds.length; i++) {
if (sel.secondaryIds[i].type == IdentifierType.DAB_ENSEMBLE) {
hasEnsemble = true;
} else if (sel.secondaryIds[i].type == IdentifierType.DAB_FREQUENCY_KHZ) {
hasFrequency = true;
}
if (hasEnsemble && hasFrequency) {
return true;
}
}
return false;
}
return true;
}
@Nullable
static android.hardware.broadcastradio.ProgramSelector programSelectorToHalProgramSelector(
ProgramSelector sel) {
android.hardware.broadcastradio.ProgramSelector hwSel =
@@ -332,6 +375,9 @@ final class ConversionUtils {
secondaryIdList.add(identifierToHalProgramIdentifier(secondaryIds[i]));
}
hwSel.secondaryIds = secondaryIdList.toArray(ProgramIdentifier[]::new);
if (!isValidHalProgramSelector(hwSel)) {
return null;
}
return hwSel;
}
@@ -344,7 +390,7 @@ final class ConversionUtils {
@Nullable
static ProgramSelector programSelectorFromHalProgramSelector(
android.hardware.broadcastradio.ProgramSelector sel) {
if (isEmpty(sel)) {
if (isEmpty(sel) || !isValidHalProgramSelector(sel)) {
return null;
}
@@ -432,7 +478,34 @@ final class ConversionUtils {
return builder.build();
}
private static boolean isValidLogicallyTunedTo(ProgramIdentifier id) {
return id.type == IdentifierType.AMFM_FREQUENCY_KHZ || id.type == IdentifierType.RDS_PI
|| id.type == IdentifierType.HD_STATION_ID_EXT
|| id.type == IdentifierType.DAB_SID_EXT
|| id.type == IdentifierType.DRMO_SERVICE_ID
|| id.type == IdentifierType.SXM_SERVICE_ID
|| isVendorIdentifierType(id.type);
}
private static boolean isValidPhysicallyTunedTo(ProgramIdentifier id) {
return id.type == IdentifierType.AMFM_FREQUENCY_KHZ
|| id.type == IdentifierType.DAB_FREQUENCY_KHZ
|| id.type == IdentifierType.DRMO_FREQUENCY_KHZ
|| id.type == IdentifierType.SXM_CHANNEL
|| isVendorIdentifierType(id.type);
}
private static boolean isValidHalProgramInfo(ProgramInfo info) {
return isValidHalProgramSelector(info.selector)
&& isValidLogicallyTunedTo(info.logicallyTunedTo)
&& isValidPhysicallyTunedTo(info.physicallyTunedTo);
}
@Nullable
static RadioManager.ProgramInfo programInfoFromHalProgramInfo(ProgramInfo info) {
if (!isValidHalProgramInfo(info)) {
return null;
}
Collection<ProgramSelector.Identifier> relatedContent = new ArrayList<>();
if (info.relatedContent != null) {
for (int i = 0; i < info.relatedContent.length; i++) {
@@ -485,7 +558,14 @@ final class ConversionUtils {
static ProgramList.Chunk chunkFromHalProgramListChunk(ProgramListChunk chunk) {
Set<RadioManager.ProgramInfo> modified = new ArraySet<>(chunk.modified.length);
for (int i = 0; i < chunk.modified.length; i++) {
modified.add(programInfoFromHalProgramInfo(chunk.modified[i]));
RadioManager.ProgramInfo modifiedInfo =
programInfoFromHalProgramInfo(chunk.modified[i]);
if (modifiedInfo == null) {
Slogf.w(TAG, "Program info %s in program list chunk is not valid",
chunk.modified[i]);
continue;
}
modified.add(modifiedInfo);
}
Set<ProgramSelector.Identifier> removed = new ArraySet<>();
if (chunk.removed != null) {
@@ -547,10 +627,22 @@ final class ConversionUtils {
if (isAtLeastU(targetSdkVersion)) {
return chunk;
}
Set<RadioManager.ProgramInfo> modified = chunk.getModified();
modified.removeIf(info -> !programInfoMeetsSdkVersionRequirement(info, targetSdkVersion));
Set<ProgramSelector.Identifier> removed = chunk.getRemoved();
removed.removeIf(id -> isNewIdentifierInU(id));
Set<RadioManager.ProgramInfo> modified = new ArraySet<>();
Iterator<RadioManager.ProgramInfo> modifiedIterator = chunk.getModified().iterator();
while (modifiedIterator.hasNext()) {
RadioManager.ProgramInfo info = modifiedIterator.next();
if (programInfoMeetsSdkVersionRequirement(info, targetSdkVersion)) {
modified.add(info);
}
}
Set<ProgramSelector.Identifier> removed = new ArraySet<>();
Iterator<ProgramSelector.Identifier> removedIterator = chunk.getRemoved().iterator();
while (removedIterator.hasNext()) {
ProgramSelector.Identifier id = removedIterator.next();
if (!isNewIdentifierInU(id)) {
removed.add(id);
}
}
return new ProgramList.Chunk(chunk.isPurge(), chunk.isComplete(), modified, removed);
}
@@ -558,7 +650,7 @@ final class ConversionUtils {
Announcement hwAnnouncement) {
return new android.hardware.radio.Announcement(
Objects.requireNonNull(programSelectorFromHalProgramSelector(
hwAnnouncement.selector)),
hwAnnouncement.selector), "Program selector can not be null"),
hwAnnouncement.type,
vendorInfoFromHalVendorKeyValues(hwAnnouncement.vendorInfo)
);

View File

@@ -24,6 +24,7 @@ import android.util.ArrayMap;
import android.util.ArraySet;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.utils.Slogf;
import java.util.ArrayList;
import java.util.Collection;
@@ -37,6 +38,7 @@ import java.util.Set;
*/
final class ProgramInfoCache {
private static final String TAG = "BcRadioAidlSrv.cache";
/**
* Maximum number of {@link RadioManager#ProgramInfo} elements that will be put into a
* ProgramList.Chunk.mModified array. Used to try to ensure a single ProgramList.Chunk
@@ -124,6 +126,10 @@ final class ProgramInfoCache {
for (int i = 0; i < chunk.modified.length; i++) {
RadioManager.ProgramInfo programInfo =
ConversionUtils.programInfoFromHalProgramInfo(chunk.modified[i]);
if (programInfo == null) {
Slogf.e(TAG, "Program info in program info %s in chunk is not valid",
chunk.modified[i]);
}
mProgramInfoMap.put(programInfo.getSelector().getPrimaryId(), programInfo);
}
if (chunk.removed != null) {

View File

@@ -97,10 +97,10 @@ final class RadioModule {
public void onTuneFailed(int result, ProgramSelector programSelector) {
fireLater(() -> {
android.hardware.radio.ProgramSelector csel =
ConversionUtils.programSelectorFromHalProgramSelector(programSelector);
int tunerResult = ConversionUtils.halResultToTunerResult(result);
synchronized (mLock) {
android.hardware.radio.ProgramSelector csel =
ConversionUtils.programSelectorFromHalProgramSelector(programSelector);
int tunerResult = ConversionUtils.halResultToTunerResult(result);
fanoutAidlCallbackLocked((cb, sdkVersion) -> {
if (csel != null && !ConversionUtils
.programSelectorMeetsSdkVersionRequirement(csel, sdkVersion)) {
@@ -117,10 +117,12 @@ final class RadioModule {
@Override
public void onCurrentProgramInfoChanged(ProgramInfo halProgramInfo) {
fireLater(() -> {
RadioManager.ProgramInfo currentProgramInfo =
ConversionUtils.programInfoFromHalProgramInfo(halProgramInfo);
Objects.requireNonNull(currentProgramInfo,
"Program info from AIDL HAL is invalid");
synchronized (mLock) {
mCurrentProgramInfo =
ConversionUtils.programInfoFromHalProgramInfo(halProgramInfo);
RadioManager.ProgramInfo currentProgramInfo = mCurrentProgramInfo;
mCurrentProgramInfo = currentProgramInfo;
fanoutAidlCallbackLocked((cb, sdkVersion) -> {
if (!ConversionUtils.programInfoMeetsSdkVersionRequirement(
currentProgramInfo, sdkVersion)) {

View File

@@ -203,10 +203,15 @@ final class TunerSession extends ITuner.Stub {
Slogf.w(TAG, "Cannot tune on AIDL HAL client from non-current user");
return;
}
android.hardware.broadcastradio.ProgramSelector hwSel =
ConversionUtils.programSelectorToHalProgramSelector(selector);
if (hwSel == null) {
throw new IllegalArgumentException("tune: INVALID_ARGUMENTS for program selector");
}
synchronized (mLock) {
checkNotClosedLocked();
try {
mService.tune(ConversionUtils.programSelectorToHalProgramSelector(selector));
mService.tune(hwSel);
} catch (RuntimeException ex) {
throw ConversionUtils.throwOnError(ex, /* action= */ "tune");
}