1 /**
2  * Copyright (C) 2017 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.hardware.radio;
18 
19 import android.annotation.IntDef;
20 import android.annotation.IntRange;
21 import android.annotation.NonNull;
22 import android.annotation.Nullable;
23 import android.annotation.SystemApi;
24 import android.os.Parcel;
25 import android.os.Parcelable;
26 
27 import java.lang.annotation.Retention;
28 import java.lang.annotation.RetentionPolicy;
29 import java.util.ArrayList;
30 import java.util.Arrays;
31 import java.util.List;
32 import java.util.Objects;
33 import java.util.stream.Stream;
34 
35 /**
36  * A set of identifiers necessary to tune to a given station.
37  *
38  * This can hold various identifiers, like
39  * - AM/FM frequency
40  * - HD Radio subchannel
41  * - DAB channel info
42  *
43  * The primary ID uniquely identifies a station and can be used for equality
44  * check. The secondary IDs are supplementary and can speed up tuning process,
45  * but the primary ID is sufficient (ie. after a full band scan).
46  *
47  * Two selectors with different secondary IDs, but the same primary ID are
48  * considered equal. In particular, secondary IDs vector may get updated for
49  * an entry on the program list (ie. when a better frequency for a given
50  * station is found).
51  *
52  * The primaryId of a given programType MUST be of a specific type:
53  * - AM, FM: RDS_PI if the station broadcasts RDS, AMFM_FREQUENCY otherwise;
54  * - AM_HD, FM_HD: HD_STATION_ID_EXT;
55  * - DAB: DAB_SIDECC;
56  * - DRMO: DRMO_SERVICE_ID;
57  * - SXM: SXM_SERVICE_ID;
58  * - VENDOR: VENDOR_PRIMARY.
59  * @hide
60  */
61 @SystemApi
62 public final class ProgramSelector implements Parcelable {
63     /** Invalid program type.
64      * @deprecated use {@link ProgramIdentifier} instead
65      */
66     @Deprecated
67     public static final int PROGRAM_TYPE_INVALID = 0;
68     /** Analogue AM radio (with or without RDS).
69      * @deprecated use {@link ProgramIdentifier} instead
70      */
71     @Deprecated
72     public static final int PROGRAM_TYPE_AM = 1;
73     /** analogue FM radio (with or without RDS).
74      * @deprecated use {@link ProgramIdentifier} instead
75      */
76     @Deprecated
77     public static final int PROGRAM_TYPE_FM = 2;
78     /** AM HD Radio.
79      * @deprecated use {@link ProgramIdentifier} instead
80      */
81     @Deprecated
82     public static final int PROGRAM_TYPE_AM_HD = 3;
83     /** FM HD Radio.
84      * @deprecated use {@link ProgramIdentifier} instead
85      */
86     @Deprecated
87     public static final int PROGRAM_TYPE_FM_HD = 4;
88     /** Digital audio broadcasting.
89      * @deprecated use {@link ProgramIdentifier} instead
90      */
91     @Deprecated
92     public static final int PROGRAM_TYPE_DAB = 5;
93     /** Digital Radio Mondiale.
94      * @deprecated use {@link ProgramIdentifier} instead
95      */
96     @Deprecated
97     public static final int PROGRAM_TYPE_DRMO = 6;
98     /** SiriusXM Satellite Radio.
99      * @deprecated use {@link ProgramIdentifier} instead
100      */
101     @Deprecated
102     public static final int PROGRAM_TYPE_SXM = 7;
103     /** Vendor-specific, not synced across devices.
104      * @deprecated use {@link ProgramIdentifier} instead
105      */
106     @Deprecated
107     public static final int PROGRAM_TYPE_VENDOR_START = 1000;
108     /** @deprecated use {@link ProgramIdentifier} instead */
109     @Deprecated
110     public static final int PROGRAM_TYPE_VENDOR_END = 1999;
111     /** @deprecated use {@link ProgramIdentifier} instead */
112     @Deprecated
113     @IntDef(prefix = { "PROGRAM_TYPE_" }, value = {
114         PROGRAM_TYPE_INVALID,
115         PROGRAM_TYPE_AM,
116         PROGRAM_TYPE_FM,
117         PROGRAM_TYPE_AM_HD,
118         PROGRAM_TYPE_FM_HD,
119         PROGRAM_TYPE_DAB,
120         PROGRAM_TYPE_DRMO,
121         PROGRAM_TYPE_SXM,
122     })
123     @IntRange(from = PROGRAM_TYPE_VENDOR_START, to = PROGRAM_TYPE_VENDOR_END)
124     @Retention(RetentionPolicy.SOURCE)
125     public @interface ProgramType {}
126 
127     public static final int IDENTIFIER_TYPE_INVALID = 0;
128     /** kHz */
129     public static final int IDENTIFIER_TYPE_AMFM_FREQUENCY = 1;
130     /** 16bit */
131     public static final int IDENTIFIER_TYPE_RDS_PI = 2;
132     /**
133      * 64bit compound primary identifier for HD Radio.
134      *
135      * Consists of (from the LSB):
136      * - 32bit: Station ID number;
137      * - 4bit: HD_SUBCHANNEL;
138      * - 18bit: AMFM_FREQUENCY.
139      * The remaining bits should be set to zeros when writing on the chip side
140      * and ignored when read.
141      */
142     public static final int IDENTIFIER_TYPE_HD_STATION_ID_EXT = 3;
143     /**
144      * HD Radio subchannel - a value of range 0-7.
145      *
146      * The subchannel index is 0-based (where 0 is MPS and 1..7 are SPS),
147      * as opposed to HD Radio standard (where it's 1-based).
148      *
149      * @deprecated use IDENTIFIER_TYPE_HD_STATION_ID_EXT instead
150      */
151     @Deprecated
152     public static final int IDENTIFIER_TYPE_HD_SUBCHANNEL = 4;
153     /**
154      * 64bit additional identifier for HD Radio.
155      *
156      * Due to Station ID abuse, some HD_STATION_ID_EXT identifiers may be not
157      * globally unique. To provide a best-effort solution, a short version of
158      * station name may be carried as additional identifier and may be used
159      * by the tuner hardware to double-check tuning.
160      *
161      * The name is limited to the first 8 A-Z0-9 characters (lowercase letters
162      * must be converted to uppercase). Encoded in little-endian ASCII:
163      * the first character of the name is the LSB.
164      *
165      * For example: "Abc" is encoded as 0x434241.
166      */
167     public static final int IDENTIFIER_TYPE_HD_STATION_NAME = 10004;
168     /**
169      * @see {@link IDENTIFIER_TYPE_DAB_SID_EXT}
170      */
171     public static final int IDENTIFIER_TYPE_DAB_SIDECC = 5;
172     /**
173      * 28bit compound primary identifier for Digital Audio Broadcasting.
174      *
175      * Consists of (from the LSB):
176      * - 16bit: SId;
177      * - 8bit: ECC code;
178      * - 4bit: SCIdS.
179      *
180      * SCIdS (Service Component Identifier within the Service) value
181      * of 0 represents the main service, while 1 and above represents
182      * secondary services.
183      *
184      * The remaining bits should be set to zeros when writing on the chip side
185      * and ignored when read.
186      */
187     public static final int IDENTIFIER_TYPE_DAB_SID_EXT = IDENTIFIER_TYPE_DAB_SIDECC;
188     /** 16bit */
189     public static final int IDENTIFIER_TYPE_DAB_ENSEMBLE = 6;
190     /** 12bit */
191     public static final int IDENTIFIER_TYPE_DAB_SCID = 7;
192     /** kHz */
193     public static final int IDENTIFIER_TYPE_DAB_FREQUENCY = 8;
194     /** 24bit */
195     public static final int IDENTIFIER_TYPE_DRMO_SERVICE_ID = 9;
196     /** kHz */
197     public static final int IDENTIFIER_TYPE_DRMO_FREQUENCY = 10;
198     /**
199      * 1: AM, 2:FM
200      * @deprecated use {@link IDENTIFIER_TYPE_DRMO_FREQUENCY} instead
201      */
202     @Deprecated
203     public static final int IDENTIFIER_TYPE_DRMO_MODULATION = 11;
204     /** 32bit */
205     public static final int IDENTIFIER_TYPE_SXM_SERVICE_ID = 12;
206     /** 0-999 range */
207     public static final int IDENTIFIER_TYPE_SXM_CHANNEL = 13;
208     /**
209      * Primary identifier for vendor-specific radio technology.
210      * The value format is determined by a vendor.
211      *
212      * It must not be used in any other programType than corresponding VENDOR
213      * type between VENDOR_START and VENDOR_END (eg. identifier type 1015 must
214      * not be used in any program type other than 1015).
215      */
216     public static final int IDENTIFIER_TYPE_VENDOR_START = PROGRAM_TYPE_VENDOR_START;
217     /**
218      * @see {@link IDENTIFIER_TYPE_VENDOR_START}
219      */
220     public static final int IDENTIFIER_TYPE_VENDOR_END = PROGRAM_TYPE_VENDOR_END;
221     /**
222      * @deprecated use {@link IDENTIFIER_TYPE_VENDOR_START} instead
223      */
224     @Deprecated
225     public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_START = IDENTIFIER_TYPE_VENDOR_START;
226     /**
227      * @deprecated use {@link IDENTIFIER_TYPE_VENDOR_END} instead
228      */
229     @Deprecated
230     public static final int IDENTIFIER_TYPE_VENDOR_PRIMARY_END = IDENTIFIER_TYPE_VENDOR_END;
231     @IntDef(prefix = { "IDENTIFIER_TYPE_" }, value = {
232         IDENTIFIER_TYPE_INVALID,
233         IDENTIFIER_TYPE_AMFM_FREQUENCY,
234         IDENTIFIER_TYPE_RDS_PI,
235         IDENTIFIER_TYPE_HD_STATION_ID_EXT,
236         IDENTIFIER_TYPE_HD_SUBCHANNEL,
237         IDENTIFIER_TYPE_HD_STATION_NAME,
238         IDENTIFIER_TYPE_DAB_SID_EXT,
239         IDENTIFIER_TYPE_DAB_SIDECC,
240         IDENTIFIER_TYPE_DAB_ENSEMBLE,
241         IDENTIFIER_TYPE_DAB_SCID,
242         IDENTIFIER_TYPE_DAB_FREQUENCY,
243         IDENTIFIER_TYPE_DRMO_SERVICE_ID,
244         IDENTIFIER_TYPE_DRMO_FREQUENCY,
245         IDENTIFIER_TYPE_DRMO_MODULATION,
246         IDENTIFIER_TYPE_SXM_SERVICE_ID,
247         IDENTIFIER_TYPE_SXM_CHANNEL,
248     })
249     @IntRange(from = IDENTIFIER_TYPE_VENDOR_START, to = IDENTIFIER_TYPE_VENDOR_END)
250     @Retention(RetentionPolicy.SOURCE)
251     public @interface IdentifierType {}
252 
253     private final @ProgramType int mProgramType;
254     private final @NonNull Identifier mPrimaryId;
255     private final @NonNull Identifier[] mSecondaryIds;
256     private final @NonNull long[] mVendorIds;
257 
258     /**
259      * Constructor for ProgramSelector.
260      *
261      * It's not desired to modify selector objects, so all its fields are initialized at creation.
262      *
263      * Identifier lists must not contain any nulls, but can itself be null to be interpreted
264      * as empty list at object creation.
265      *
266      * @param programType type of a radio technology.
267      * @param primaryId primary program identifier.
268      * @param secondaryIds list of secondary program identifiers.
269      * @param vendorIds list of vendor-specific program identifiers.
270      */
ProgramSelector(@rogramType int programType, @NonNull Identifier primaryId, @Nullable Identifier[] secondaryIds, @Nullable long[] vendorIds)271     public ProgramSelector(@ProgramType int programType, @NonNull Identifier primaryId,
272             @Nullable Identifier[] secondaryIds, @Nullable long[] vendorIds) {
273         if (secondaryIds == null) secondaryIds = new Identifier[0];
274         if (vendorIds == null) vendorIds = new long[0];
275         if (Stream.of(secondaryIds).anyMatch(id -> id == null)) {
276             throw new IllegalArgumentException("secondaryIds list must not contain nulls");
277         }
278         mProgramType = programType;
279         mPrimaryId = Objects.requireNonNull(primaryId);
280         mSecondaryIds = secondaryIds;
281         mVendorIds = vendorIds;
282     }
283 
284     /**
285      * Type of a radio technology.
286      *
287      * @return program type.
288      * @deprecated use {@link getPrimaryId} instead
289      */
290     @Deprecated
getProgramType()291     public @ProgramType int getProgramType() {
292         return mProgramType;
293     }
294 
295     /**
296      * Primary program identifier uniquely identifies a station and is used to
297      * determine equality between two ProgramSelectors.
298      *
299      * @return primary identifier.
300      */
getPrimaryId()301     public @NonNull Identifier getPrimaryId() {
302         return mPrimaryId;
303     }
304 
305     /**
306      * Secondary program identifier is not required for tuning, but may make it
307      * faster or more reliable.
308      *
309      * @return secondary identifier list, must not be modified.
310      */
getSecondaryIds()311     public @NonNull Identifier[] getSecondaryIds() {
312         return mSecondaryIds;
313     }
314 
315     /**
316      * Looks up an identifier of a given type (either primary or secondary).
317      *
318      * If there are multiple identifiers if a given type, then first in order (where primary id is
319      * before any secondary) is selected.
320      *
321      * @param type type of identifier.
322      * @return identifier value, if found.
323      * @throws IllegalArgumentException, if not found.
324      */
getFirstId(@dentifierType int type)325     public long getFirstId(@IdentifierType int type) {
326         if (mPrimaryId.getType() == type) return mPrimaryId.getValue();
327         for (Identifier id : mSecondaryIds) {
328             if (id.getType() == type) return id.getValue();
329         }
330         throw new IllegalArgumentException("Identifier " + type + " not found");
331     }
332 
333     /**
334      * Looks up all identifier of a given type (either primary or secondary).
335      *
336      * Some identifiers may be provided multiple times, for example
337      * IDENTIFIER_TYPE_AMFM_FREQUENCY for FM Alternate Frequencies.
338      *
339      * @param type type of identifier.
340      * @return a list of identifiers, generated on each call. May be modified.
341      */
getAllIds(@dentifierType int type)342     public @NonNull Identifier[] getAllIds(@IdentifierType int type) {
343         List<Identifier> out = new ArrayList<>();
344 
345         if (mPrimaryId.getType() == type) out.add(mPrimaryId);
346         for (Identifier id : mSecondaryIds) {
347             if (id.getType() == type) out.add(id);
348         }
349 
350         return out.toArray(new Identifier[out.size()]);
351     }
352 
353     /**
354      * Vendor identifiers are passed as-is to the HAL implementation,
355      * preserving elements order.
356      *
357      * @return an array of vendor identifiers, must not be modified.
358      * @deprecated for HAL 1.x compatibility;
359      *             HAL 2.x uses standard primary/secondary lists for vendor IDs
360      */
361     @Deprecated
getVendorIds()362     public @NonNull long[] getVendorIds() {
363         return mVendorIds;
364     }
365 
366     /**
367      * Creates an equivalent ProgramSelector with a given secondary identifier preferred.
368      *
369      * Used to point to a specific physical identifier for technologies that may broadcast the same
370      * program on different channels. For example, with a DAB program broadcasted over multiple
371      * ensembles, the radio hardware may select the one with the strongest signal. The UI may select
372      * preferred ensemble though, so the radio hardware may try to use it in the first place.
373      *
374      * This is a best-effort hint for the tuner, not a guaranteed behavior.
375      *
376      * Setting the given secondary identifier as preferred means filtering out other secondary
377      * identifiers of its type and adding it to the list.
378      *
379      * @param preferred preferred secondary identifier
380      * @return a new ProgramSelector with a given secondary identifier preferred
381      */
withSecondaryPreferred(@onNull Identifier preferred)382     public @NonNull ProgramSelector withSecondaryPreferred(@NonNull Identifier preferred) {
383         int preferredType = preferred.getType();
384         Identifier[] secondaryIds = Stream.concat(
385             // remove other identifiers of that type
386             Arrays.stream(mSecondaryIds).filter(id -> id.getType() != preferredType),
387             // add preferred identifier instead
388             Stream.of(preferred)).toArray(Identifier[]::new);
389 
390         return new ProgramSelector(
391             mProgramType,
392             mPrimaryId,
393             secondaryIds,
394             mVendorIds
395         );
396     }
397 
398     /**
399      * Builds new ProgramSelector for AM/FM frequency.
400      *
401      * @param band the band.
402      * @param frequencyKhz the frequency in kHz.
403      * @return new ProgramSelector object representing given frequency.
404      * @throws IllegalArgumentException if provided frequency is out of bounds.
405      */
createAmFmSelector( @adioManager.Band int band, int frequencyKhz)406     public static @NonNull ProgramSelector createAmFmSelector(
407             @RadioManager.Band int band, int frequencyKhz) {
408         return createAmFmSelector(band, frequencyKhz, 0);
409     }
410 
411     /**
412      * Checks, if a given AM/FM frequency is roughly valid and in correct unit.
413      *
414      * It does not check the range precisely: it may provide false positives, but not false
415      * negatives. In particular, it may be way off for certain regions.
416      * The main purpose is to avoid passing inproper units, ie. MHz instead of kHz.
417      *
418      * @param isAm true, if AM, false if FM.
419      * @param frequencyKhz the frequency in kHz.
420      * @return true, if the frequency is rougly valid.
421      */
isValidAmFmFrequency(boolean isAm, int frequencyKhz)422     private static boolean isValidAmFmFrequency(boolean isAm, int frequencyKhz) {
423         if (isAm) {
424             return frequencyKhz > 150 && frequencyKhz <= 30000;
425         } else {
426             return frequencyKhz > 60000 && frequencyKhz < 110000;
427         }
428     }
429 
430     /**
431      * Builds new ProgramSelector for AM/FM frequency.
432      *
433      * This method variant supports HD Radio subchannels, but it's undesirable to
434      * select them manually. Instead, the value should be retrieved from program list.
435      *
436      * @param band the band.
437      * @param frequencyKhz the frequency in kHz.
438      * @param subChannel 1-based HD Radio subchannel.
439      * @return new ProgramSelector object representing given frequency.
440      * @throws IllegalArgumentException if provided frequency is out of bounds,
441      *         or tried setting a subchannel for analog AM/FM.
442      */
createAmFmSelector( @adioManager.Band int band, int frequencyKhz, int subChannel)443     public static @NonNull ProgramSelector createAmFmSelector(
444             @RadioManager.Band int band, int frequencyKhz, int subChannel) {
445         if (band == RadioManager.BAND_INVALID) {
446             // 50MHz is a rough boundary between AM (<30MHz) and FM (>60MHz).
447             if (frequencyKhz < 50000) {
448                 band = (subChannel <= 0) ? RadioManager.BAND_AM : RadioManager.BAND_AM_HD;
449             } else {
450                 band = (subChannel <= 0) ? RadioManager.BAND_FM : RadioManager.BAND_FM_HD;
451             }
452         }
453 
454         boolean isAm = (band == RadioManager.BAND_AM || band == RadioManager.BAND_AM_HD);
455         boolean isDigital = (band == RadioManager.BAND_AM_HD || band == RadioManager.BAND_FM_HD);
456         if (!isAm && !isDigital && band != RadioManager.BAND_FM) {
457             throw new IllegalArgumentException("Unknown band: " + band);
458         }
459         if (subChannel < 0 || subChannel > 8) {
460             throw new IllegalArgumentException("Invalid subchannel: " + subChannel);
461         }
462         if (subChannel > 0 && !isDigital) {
463             throw new IllegalArgumentException("Subchannels are not supported for non-HD radio");
464         }
465         if (!isValidAmFmFrequency(isAm, frequencyKhz)) {
466             throw new IllegalArgumentException("Provided value is not a valid AM/FM frequency: "
467                     + frequencyKhz);
468         }
469 
470         // We can't use AM_HD or FM_HD, because we don't know HD station ID.
471         @ProgramType int programType = isAm ? PROGRAM_TYPE_AM : PROGRAM_TYPE_FM;
472         Identifier primary = new Identifier(IDENTIFIER_TYPE_AMFM_FREQUENCY, frequencyKhz);
473 
474         Identifier[] secondary = null;
475         if (subChannel > 0) {
476             /* Stating sub channel for non-HD AM/FM does not give any guarantees,
477              * but we can't do much more without HD station ID.
478              *
479              * The legacy APIs had 1-based subChannels, while ProgramSelector is 0-based.
480              */
481             secondary = new Identifier[]{
482                     new Identifier(IDENTIFIER_TYPE_HD_SUBCHANNEL, subChannel - 1)};
483         }
484 
485         return new ProgramSelector(programType, primary, secondary, null);
486     }
487 
488     @NonNull
489     @Override
toString()490     public String toString() {
491         StringBuilder sb = new StringBuilder("ProgramSelector(type=").append(mProgramType)
492                 .append(", primary=").append(mPrimaryId);
493         if (mSecondaryIds.length > 0) sb.append(", secondary=").append(mSecondaryIds);
494         if (mVendorIds.length > 0) sb.append(", vendor=").append(mVendorIds);
495         sb.append(")");
496         return sb.toString();
497     }
498 
499     @Override
hashCode()500     public int hashCode() {
501         // secondaryIds and vendorIds are ignored for equality/hashing
502         return mPrimaryId.hashCode();
503     }
504 
505     @Override
equals(@ullable Object obj)506     public boolean equals(@Nullable Object obj) {
507         if (this == obj) return true;
508         if (!(obj instanceof ProgramSelector)) return false;
509         ProgramSelector other = (ProgramSelector) obj;
510         // secondaryIds and vendorIds are ignored for equality/hashing
511         // programType can be inferred from primaryId, thus not checked
512         return mPrimaryId.equals(other.getPrimaryId());
513     }
514 
ProgramSelector(Parcel in)515     private ProgramSelector(Parcel in) {
516         mProgramType = in.readInt();
517         mPrimaryId = in.readTypedObject(Identifier.CREATOR);
518         mSecondaryIds = in.createTypedArray(Identifier.CREATOR);
519         if (Stream.of(mSecondaryIds).anyMatch(id -> id == null)) {
520             throw new IllegalArgumentException("secondaryIds list must not contain nulls");
521         }
522         mVendorIds = in.createLongArray();
523     }
524 
525     @Override
writeToParcel(Parcel dest, int flags)526     public void writeToParcel(Parcel dest, int flags) {
527         dest.writeInt(mProgramType);
528         dest.writeTypedObject(mPrimaryId, 0);
529         dest.writeTypedArray(mSecondaryIds, 0);
530         dest.writeLongArray(mVendorIds);
531     }
532 
533     @Override
describeContents()534     public int describeContents() {
535         return 0;
536     }
537 
538     public static final @android.annotation.NonNull Parcelable.Creator<ProgramSelector> CREATOR =
539             new Parcelable.Creator<ProgramSelector>() {
540         public ProgramSelector createFromParcel(Parcel in) {
541             return new ProgramSelector(in);
542         }
543 
544         public ProgramSelector[] newArray(int size) {
545             return new ProgramSelector[size];
546         }
547     };
548 
549     /**
550      * A single program identifier component, eg. frequency or channel ID.
551      *
552      * The long value field holds the value in format described in comments for
553      * IdentifierType constants.
554      */
555     public static final class Identifier implements Parcelable {
556         private final @IdentifierType int mType;
557         private final long mValue;
558 
Identifier(@dentifierType int type, long value)559         public Identifier(@IdentifierType int type, long value) {
560             if (type == IDENTIFIER_TYPE_HD_STATION_NAME) {
561                 // see getType
562                 type = IDENTIFIER_TYPE_HD_SUBCHANNEL;
563             }
564             mType = type;
565             mValue = value;
566         }
567 
568         /**
569          * Type of an identifier.
570          *
571          * @return type of an identifier.
572          */
getType()573         public @IdentifierType int getType() {
574             if (mType == IDENTIFIER_TYPE_HD_SUBCHANNEL && mValue > 10) {
575                 /* HD_SUBCHANNEL and HD_STATION_NAME use the same identifier type, but they differ
576                  * in possible values: sub channel is 0-7, station name is greater than ASCII space
577                  * code (32).
578                  */
579                 return IDENTIFIER_TYPE_HD_STATION_NAME;
580             }
581             return mType;
582         }
583 
584         /**
585          * Returns whether this Identifier's type is considered a category when filtering
586          * ProgramLists for category entries.
587          *
588          * @see {@link ProgramList.Filter#areCategoriesIncluded()}
589          * @return False if this identifier's type is not tuneable (e.g. DAB ensemble or
590          *         vendor-specified type). True otherwise.
591          */
isCategoryType()592         public boolean isCategoryType() {
593             return (mType >= IDENTIFIER_TYPE_VENDOR_START && mType <= IDENTIFIER_TYPE_VENDOR_END)
594                     || mType == IDENTIFIER_TYPE_DAB_ENSEMBLE;
595         }
596 
597         /**
598          * Value of an identifier.
599          *
600          * Its meaning depends on identifier type, ie. for IDENTIFIER_TYPE_AMFM_FREQUENCY type,
601          * the value is a frequency in kHz.
602          *
603          * The range of a value depends on its type; it does not always require the whole long
604          * range. Casting to necessary type (ie. int) without range checking is correct in front-end
605          * code - any range violations are either errors in the framework or in the
606          * HAL implementation. For example, IDENTIFIER_TYPE_AMFM_FREQUENCY always fits in int,
607          * as Integer.MAX_VALUE would mean 2.1THz.
608          *
609          * @return value of an identifier.
610          */
getValue()611         public long getValue() {
612             return mValue;
613         }
614 
615         @NonNull
616         @Override
toString()617         public String toString() {
618             return "Identifier(" + mType + ", " + mValue + ")";
619         }
620 
621         @Override
hashCode()622         public int hashCode() {
623             return Objects.hash(mType, mValue);
624         }
625 
626         @Override
equals(@ullable Object obj)627         public boolean equals(@Nullable Object obj) {
628             if (this == obj) return true;
629             if (!(obj instanceof Identifier)) return false;
630             Identifier other = (Identifier) obj;
631             return other.getType() == mType && other.getValue() == mValue;
632         }
633 
Identifier(Parcel in)634         private Identifier(Parcel in) {
635             mType = in.readInt();
636             mValue = in.readLong();
637         }
638 
639         @Override
writeToParcel(Parcel dest, int flags)640         public void writeToParcel(Parcel dest, int flags) {
641             dest.writeInt(mType);
642             dest.writeLong(mValue);
643         }
644 
645         @Override
describeContents()646         public int describeContents() {
647             return 0;
648         }
649 
650         public static final @android.annotation.NonNull Parcelable.Creator<Identifier> CREATOR =
651                 new Parcelable.Creator<Identifier>() {
652             public Identifier createFromParcel(Parcel in) {
653                 return new Identifier(in);
654             }
655 
656             public Identifier[] newArray(int size) {
657                 return new Identifier[size];
658             }
659         };
660     }
661 }
662