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