diff --git a/core/java/android/hardware/radio/ProgramSelector.java b/core/java/android/hardware/radio/ProgramSelector.java index 7faa285a8fee9..727716e67f28b 100644 --- a/core/java/android/hardware/radio/ProgramSelector.java +++ b/core/java/android/hardware/radio/ProgramSelector.java @@ -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. + * + *
This identifier also contains band information: + *
Consists of (from the LSB): + *
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}. + * + *
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). + * + *
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), + *
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 + *
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. + *
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. + *
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. + *
Consists of (from the LSB): + *
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. + *
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. + *
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 + *
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).
*/
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/ProgramListTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/ProgramListTest.java
index 87f91fa436ed3..9e99b7c99e0d4 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/ProgramListTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/ProgramListTest.java
@@ -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
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/ProgramSelectorTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/ProgramSelectorTest.java
index 5bd018bea1d16..9399907cf33c4 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/ProgramSelectorTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/ProgramSelectorTest.java
@@ -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);
}
}
diff --git a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/RadioManagerTest.java b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/RadioManagerTest.java
index 03742eb93a3bb..7dd13394e48dd 100644
--- a/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/RadioManagerTest.java
+++ b/core/tests/BroadcastRadioTests/src/android/hardware/radio/tests/unittests/RadioManagerTest.java
@@ -105,17 +105,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 +859,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
@@ -1104,8 +1104,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);
}
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java
index a4212180d0b50..3037f345b5a36 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/AidlTestUtils.java
@@ -54,6 +54,13 @@ final class AidlTestUtils {
return makeProgramInfo(selector, 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) {
return makeProgramSelector(ProgramSelector.PROGRAM_TYPE_FM,
new ProgramSelector.Identifier(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY,
@@ -67,15 +74,17 @@ final class AidlTestUtils {
}
static android.hardware.broadcastradio.ProgramSelector makeHalFmSelector(int freq) {
- ProgramIdentifier halId = new ProgramIdentifier();
- halId.type = IdentifierType.AMFM_FREQUENCY_KHZ;
- halId.value = freq;
+ ProgramIdentifier halId = makeHalIdentifier(IdentifierType.AMFM_FREQUENCY_KHZ, freq);
+ return makeHalSelector(halId, /* secondaryIds= */ new ProgramIdentifier[0]);
+ }
- android.hardware.broadcastradio.ProgramSelector halSelector =
+ static android.hardware.broadcastradio.ProgramSelector makeHalSelector(
+ ProgramIdentifier primaryId, ProgramIdentifier[] secondaryIds) {
+ android.hardware.broadcastradio.ProgramSelector hwSelector =
new android.hardware.broadcastradio.ProgramSelector();
- halSelector.primaryId = halId;
- halSelector.secondaryIds = new ProgramIdentifier[0];
- return halSelector;
+ hwSelector.primaryId = primaryId;
+ hwSelector.secondaryIds = secondaryIds;
+ return hwSelector;
}
static ProgramInfo programInfoToHalProgramInfo(RadioManager.ProgramInfo info) {
@@ -95,10 +104,17 @@ final class AidlTestUtils {
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[]{};
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
index a1cebb630aac3..6604d2aea7c17 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/ConversionUtilsTest.java
@@ -20,6 +20,8 @@ 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.Properties;
import android.hardware.broadcastradio.VendorKeyValue;
import android.hardware.radio.Announcement;
@@ -57,6 +59,27 @@ 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 = 220352;
+ 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 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 int TEST_ENABLED_TYPE = Announcement.TYPE_EMERGENCY;
private static final int TEST_ANNOUNCEMENT_FREQUENCY = FM_LOWER_LIMIT + FM_SPACING;
@@ -158,6 +181,142 @@ 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() {
+ ProgramSelector dabSelector = new ProgramSelector(
+ ProgramSelector.PROGRAM_TYPE_DAB, TEST_DAB_SID_EXT_ID,
+ new ProgramSelector.Identifier[]{TEST_DAB_ENSEMBLE_ID, TEST_DAB_FREQUENCY_ID},
+ new long[0]);
+
+ android.hardware.broadcastradio.ProgramSelector halDabSelector =
+ ConversionUtils.programSelectorToHalProgramSelector(dabSelector);
+
+ 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 programSelectorMeetsSdkVersionRequirement_withLowerVersionId_returnsFalse() {
+ ProgramSelector dabSelector = new ProgramSelector(
+ ProgramSelector.PROGRAM_TYPE_DAB, TEST_DAB_SID_EXT_ID,
+ new ProgramSelector.Identifier[]{TEST_DAB_ENSEMBLE_ID, TEST_DAB_FREQUENCY_ID},
+ new long[0]);
+
+ expect.withMessage("Selector %s not meeting required SDK version", dabSelector)
+ .that(ConversionUtils.programSelectorMeetsSdkVersionRequirement(dabSelector,
+ Build.VERSION_CODES.TIRAMISU)).isFalse();
+ }
+
+ @Test
+ public void programSelectorMeetsSdkVersionRequirement_withRequiredVersionId_returnsTrue() {
+ ProgramSelector fmSelector = AidlTestUtils.makeFmSelector(/* freq= */ 97100);
+
+ expect.withMessage("Selector %s meeting required SDK version", fmSelector)
+ .that(ConversionUtils.programSelectorMeetsSdkVersionRequirement(fmSelector,
+ Build.VERSION_CODES.TIRAMISU)).isTrue();
+ }
+
@Test
public void announcementFromHalAnnouncement_typesMatch() {
expect.withMessage("Announcement type")
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
index c5c6349df07e4..201a50f49f881 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/aidl/TunerSessionTest.java
@@ -330,10 +330,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));
@@ -342,6 +349,21 @@ public final class TunerSessionTest extends ExtendedRadioMockitoTestCase {
.that(thrown).hasMessageThat().contains("tune: NOT_SUPPORTED");
}
+ @Test
+ 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_forCurrentUser_doesNotTune() throws Exception {
openAidlClients(/* numClients= */ 1);
diff --git a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
index db16c0362e7c9..6e54dcf3d451a 100644
--- a/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
+++ b/core/tests/BroadcastRadioTests/src/com/android/server/broadcastradio/hal2/TunerSessionHidlTest.java
@@ -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));
diff --git a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
index 3c0fda863cdc2..d580d5ce8fa66 100644
--- a/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
+++ b/services/core/java/com/android/server/broadcastradio/aidl/ConversionUtils.java
@@ -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