summaryrefslogtreecommitdiff
path: root/car-broadcastradio-support/src/com/android/car/broadcastradio/support/platform/ProgramSelectorExt.java
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2024-01-17 22:14:18 -0800
committerXin Li <delphij@google.com>2024-01-17 22:14:18 -0800
commit9b0e4dd209cba5e01706b0b80f135e84e777f66f (patch)
tree1f62dfb2dec5ac3a1322453dedfa107bac2f844b /car-broadcastradio-support/src/com/android/car/broadcastradio/support/platform/ProgramSelectorExt.java
parent34e33e461d20302c3672472e6f64d04d95173cf7 (diff)
parent6e3901f05074dda88fd426c49511a1ea0901f440 (diff)
downloadsystemlibs-9b0e4dd209cba5e01706b0b80f135e84e777f66f.tar.gz
Merge Android 24Q1 Release (ab/11220357)
Bug: 319669529 Merged-In: I796c209ea3e4085e60d2ca2ba104b9cf88811894 Change-Id: Ia56aae54675e45ffe56c5506bcc73fff5d04cd31
Diffstat (limited to 'car-broadcastradio-support/src/com/android/car/broadcastradio/support/platform/ProgramSelectorExt.java')
-rw-r--r--car-broadcastradio-support/src/com/android/car/broadcastradio/support/platform/ProgramSelectorExt.java187
1 files changed, 177 insertions, 10 deletions
diff --git a/car-broadcastradio-support/src/com/android/car/broadcastradio/support/platform/ProgramSelectorExt.java b/car-broadcastradio-support/src/com/android/car/broadcastradio/support/platform/ProgramSelectorExt.java
index 4b3583b..555176d 100644
--- a/car-broadcastradio-support/src/com/android/car/broadcastradio/support/platform/ProgramSelectorExt.java
+++ b/car-broadcastradio-support/src/com/android/car/broadcastradio/support/platform/ProgramSelectorExt.java
@@ -29,6 +29,7 @@ import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.text.DecimalFormat;
import java.util.ArrayList;
+import java.util.Comparator;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@@ -81,6 +82,11 @@ public class ProgramSelectorExt {
@Retention(RetentionPolicy.SOURCE)
public @interface NameFlag {}
+ /**
+ * Invalid value for a {@link ProgramSelector.Identifier}
+ */
+ public static int INVALID_IDENTIFIER_VALUE = 0;
+
private static final String URI_SCHEME_BROADCASTRADIO = "broadcastradio";
private static final String URI_AUTHORITY_PROGRAM = "program";
private static final String URI_VENDOR_PREFIX = "VENDOR_";
@@ -251,26 +257,81 @@ public class ProgramSelectorExt {
}
/**
+ * Get frequency from a {@link ProgramSelector}.
+ *
+ * @param selector Program selector
+ * @return frequency of the first {@link ProgramSelector#IDENTIFIER_TYPE_AMFM_FREQUENCY} id in
+ * program selector if it exists, otherwise the AM/FM frequency in
+ * {@link ProgramSelector#IDENTIFIER_TYPE_HD_STATION_ID_EXT} id if it is the primary id,
+ * {@link #INVALID_IDENTIFIER_VALUE} otherwise.
+ */
+ public static int getFrequency(@NonNull ProgramSelector selector) {
+ if (ProgramSelectorExt.hasId(selector, ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY)) {
+ return (int) selector.getFirstId(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY);
+ } else if (selector.getPrimaryId().getType()
+ == ProgramSelector.IDENTIFIER_TYPE_HD_STATION_ID_EXT) {
+ return IdentifierExt.asHdPrimary(selector.getPrimaryId()).getFrequency();
+ } else if (selector.getPrimaryId().getType()
+ == ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT
+ || selector.getPrimaryId().getType()
+ == ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT) {
+ try {
+ return (int) selector.getFirstId(ProgramSelector.IDENTIFIER_TYPE_DAB_FREQUENCY);
+ } catch (IllegalArgumentException e) {
+ return INVALID_IDENTIFIER_VALUE;
+ }
+ }
+ return INVALID_IDENTIFIER_VALUE;
+ }
+
+ /**
+ * Get ensemble value from a DAB-type {@link ProgramSelector}.
+ *
+ * @param selector Program selector
+ * @return Value of the first {@link ProgramSelector#IDENTIFIER_TYPE_DAB_ENSEMBLE} identifier,
+ * 0 otherwise
+ */
+ public static int getDabEnsemble(@NonNull ProgramSelector selector) {
+ try {
+ return (int) selector.getFirstId(ProgramSelector.IDENTIFIER_TYPE_DAB_ENSEMBLE);
+ } catch (IllegalArgumentException e) {
+ return INVALID_IDENTIFIER_VALUE;
+ }
+ }
+
+ /**
* Returns a channel name that can be displayed to the user.
*
- * It's implemented only for radio technologies where the channel is meant
- * to be presented to the user.
+ * <p>It's implemented only for radio technologies where the channel is meant
+ * to be presented to the user, such as FM/AM and HD radio.
+ *
+ * <p>For HD radio, the display name is prefix with "-HD[NUMBER]" where the number is the
+ * sub channel.
*
* @param sel the program selector
- * @return Channel name or null, if radio technology doesn't present channel names to the user.
+ * @return Channel name or {@code null}, if radio technology doesn't present channel names to
+ * the user.
*/
public static @Nullable String getDisplayName(@NonNull ProgramSelector sel,
@NameFlag int flags) {
boolean noProgramTypeFallback = (flags & NAME_NO_PROGRAM_TYPE_FALLBACK) != 0;
if (isAmFmProgram(sel)) {
- if (!hasId(sel, ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY)) {
+ long freq;
+ String hdSuffix = "";
+ if (sel.getPrimaryId().getType()
+ == ProgramSelector.IDENTIFIER_TYPE_HD_STATION_ID_EXT) {
+ IdentifierExt.HdPrimary hdIdExt = IdentifierExt.asHdPrimary(sel.getPrimaryId());
+ freq = hdIdExt.getFrequency();
+ hdSuffix = "-HD" + (hdIdExt.getSubchannel() + 1);
+ } else if (hasId(sel, ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY)) {
+ freq = sel.getFirstId(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY);
+ } else {
if (noProgramTypeFallback) return null;
// if there is no frequency assigned, let's assume it's a malformed RDS selector
return "FM";
}
- long freq = sel.getFirstId(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY);
- return formatAmFmFrequency(freq, flags);
+ return formatAmFmFrequency(freq, flags) + hdSuffix;
}
if ((flags & NAME_MODULATION_ONLY) != 0) return null;
@@ -285,7 +346,8 @@ public class ProgramSelectorExt {
switch (sel.getPrimaryId().getType()) {
case ProgramSelector.IDENTIFIER_TYPE_SXM_SERVICE_ID:
return "SXM";
- case ProgramSelector.IDENTIFIER_TYPE_DAB_SIDECC:
+ case ProgramSelector.IDENTIFIER_TYPE_DAB_SID_EXT:
+ case ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT:
return "DAB";
case ProgramSelector.IDENTIFIER_TYPE_DRMO_SERVICE_ID:
return "DRMO";
@@ -442,10 +504,10 @@ public class ProgramSelectorExt {
*/
public static class IdentifierExt {
/**
- * Decode {@link ProgramSelector#IDENTIFIER_TYPE_HD_STATION_ID_EXT} value.
+ * Decoder of {@link ProgramSelector#IDENTIFIER_TYPE_HD_STATION_ID_EXT} value.
*
- * @param id identifier to decode
- * @return value decoder
+ * When pushed to the framework, it will be non-static class referring
+ * to the original value.
*/
public static @Nullable HdPrimary asHdPrimary(@NonNull Identifier id) {
if (id.getType() == ProgramSelector.IDENTIFIER_TYPE_HD_STATION_ID_EXT) {
@@ -482,5 +544,110 @@ public class ProgramSelectorExt {
return (int) ((mValue >>> (32 + 4)) & 0x3FFFF);
}
}
+
+ /**
+ * Decoder of {@link ProgramSelector#IDENTIFIER_TYPE_DAB_DMB_SID_EXT} value.
+ *
+ * <p>When pushed to the framework, it will be non-static class referring
+ * to the original value.
+ * @param id Identifier to be decoded
+ * @return {@link DabPrimary} object if the identifier is DAB-type, {@code null} otherwise
+ */
+ public static @Nullable DabPrimary asDabPrimary(@NonNull Identifier id) {
+ if (id.getType() == ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT) {
+ return new DabPrimary(id.getValue());
+ }
+ return null;
+ }
+
+ /**
+ * Decoder of {@link ProgramSelector#IDENTIFIER_TYPE_DAB_DMB_SID_EXT} value.
+ *
+ * <p>When pushed to the framework, it will be non-static class referring
+ * to the original value.
+ */
+ public static class DabPrimary {
+ private final long mValue;
+
+ private DabPrimary(long value) {
+ mValue = value;
+ }
+
+ /**
+ * Get Service Identifier (SId).
+ * @return SId value
+ */
+ public int getSId() {
+ return (int) (mValue & 0xFFFFFFFF);
+ }
+
+ /**
+ * Get Extended Country Code (ECC)
+ * @return Extended Country Code
+ */
+ public int getEcc() {
+ return (int) ((mValue >>> 32) & 0xFF);
+ }
+
+ /**
+ * Get SCIdS (Service Component Identifier within the Service) value
+ * @return SCIdS value
+ */
+ public int getSCIdS() {
+ return (int) ((mValue >>> (32 + 8)) & 0xF);
+ }
+ }
+ }
+
+ public static class ProgramSelectorComparator implements Comparator<ProgramSelector> {
+ @Override
+ public int compare(ProgramSelector selector1, ProgramSelector selector2) {
+ int type1 = selector1.getPrimaryId().getType();
+ int type2 = selector2.getPrimaryId().getType();
+ int frequency1 = getFrequency(selector1);
+ int frequency2 = getFrequency(selector2);
+ if (isAmFmProgram(selector1) && isAmFmProgram(selector2)) {
+ if (frequency1 != frequency2) {
+ return frequency1 > frequency2 ? 1 : -1;
+ }
+ int subchannel1 = selector1.getPrimaryId().getType()
+ == ProgramSelector.IDENTIFIER_TYPE_HD_STATION_ID_EXT
+ ? IdentifierExt.asHdPrimary(selector1.getPrimaryId()).getSubchannel() : 0;
+ int subchannel2 = selector2.getPrimaryId().getType()
+ == ProgramSelector.IDENTIFIER_TYPE_HD_STATION_ID_EXT
+ ? IdentifierExt.asHdPrimary(selector2.getPrimaryId()).getSubchannel() : 0;
+ if (subchannel1 != subchannel2) {
+ return subchannel1 > subchannel2 ? 1 : -1;
+ }
+ return selector1.getPrimaryId().getType() - selector2.getPrimaryId().getType();
+ } else if (type1 == ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT
+ && type2 == ProgramSelector.IDENTIFIER_TYPE_DAB_DMB_SID_EXT) {
+ if (frequency1 != frequency2) {
+ return frequency1 > frequency2 ? 1 : -1;
+ }
+ IdentifierExt.DabPrimary dabPrimary1 = IdentifierExt.asDabPrimary(
+ selector1.getPrimaryId());
+ IdentifierExt.DabPrimary dabPrimary2 = IdentifierExt.asDabPrimary(
+ selector2.getPrimaryId());
+ int ecc1 = dabPrimary1.getEcc();
+ int ecc2 = dabPrimary2.getEcc();
+ if (ecc1 != ecc2) {
+ return ecc1 > ecc2 ? 1 : -1;
+ }
+ int sId1 = dabPrimary1.getSId();
+ int sId2 = dabPrimary2.getSId();
+ if (sId1 != sId2) {
+ return sId1 > sId2 ? 1 : -1;
+ }
+ int sCIds1 = dabPrimary1.getSCIdS();
+ int sCIds2 = dabPrimary2.getSCIdS();
+ if (sCIds1 != sCIds2) {
+ return sCIds1 > sCIds2 ? 1 : -1;
+ }
+ return getDabEnsemble(selector1) > getDabEnsemble(selector2) ? 1 : -1;
+ }
+ return type1 > type2 || (type1 == type2 && selector1.getPrimaryId().getValue()
+ > selector2.getPrimaryId().getValue()) ? 1 : -1;
+ }
}
}