1 /** 2 * Copyright (C) 2015 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.Manifest; 20 import android.annotation.CallbackExecutor; 21 import android.annotation.IntDef; 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.annotation.RequiresFeature; 25 import android.annotation.RequiresPermission; 26 import android.annotation.SystemApi; 27 import android.annotation.SystemService; 28 import android.content.Context; 29 import android.content.pm.PackageManager; 30 import android.os.Handler; 31 import android.os.Parcel; 32 import android.os.Parcelable; 33 import android.os.RemoteException; 34 import android.os.ServiceManager; 35 import android.os.ServiceManager.ServiceNotFoundException; 36 import android.text.TextUtils; 37 import android.util.Log; 38 39 import com.android.internal.util.Preconditions; 40 41 import java.lang.annotation.Retention; 42 import java.lang.annotation.RetentionPolicy; 43 import java.util.Arrays; 44 import java.util.Collection; 45 import java.util.Collections; 46 import java.util.HashMap; 47 import java.util.List; 48 import java.util.Map; 49 import java.util.Objects; 50 import java.util.Set; 51 import java.util.concurrent.Executor; 52 import java.util.stream.Collectors; 53 54 /** 55 * The RadioManager class allows to control a broadcast radio tuner present on the device. 56 * It provides data structures and methods to query for available radio modules, list their 57 * properties and open an interface to control tuning operations and receive callbacks when 58 * asynchronous operations complete or events occur. 59 * @hide 60 */ 61 @SystemApi 62 @SystemService(Context.RADIO_SERVICE) 63 @RequiresFeature(PackageManager.FEATURE_BROADCAST_RADIO) 64 public class RadioManager { 65 private static final String TAG = "BroadcastRadio.manager"; 66 67 /** Method return status: successful operation */ 68 public static final int STATUS_OK = 0; 69 /** Method return status: unspecified error */ 70 public static final int STATUS_ERROR = Integer.MIN_VALUE; 71 /** Method return status: permission denied */ 72 public static final int STATUS_PERMISSION_DENIED = -1; 73 /** Method return status: initialization failure */ 74 public static final int STATUS_NO_INIT = -19; 75 /** Method return status: invalid argument provided */ 76 public static final int STATUS_BAD_VALUE = -22; 77 /** Method return status: cannot reach service */ 78 public static final int STATUS_DEAD_OBJECT = -32; 79 /** Method return status: invalid or out of sequence operation */ 80 public static final int STATUS_INVALID_OPERATION = -38; 81 /** Method return status: time out before operation completion */ 82 public static final int STATUS_TIMED_OUT = -110; 83 84 85 // keep in sync with radio_class_t in /system/core/incluse/system/radio.h 86 /** Radio module class supporting FM (including HD radio) and AM */ 87 public static final int CLASS_AM_FM = 0; 88 /** Radio module class supporting satellite radio */ 89 public static final int CLASS_SAT = 1; 90 /** Radio module class supporting Digital terrestrial radio */ 91 public static final int CLASS_DT = 2; 92 93 public static final int BAND_INVALID = -1; 94 /** AM radio band (LW/MW/SW). 95 * @see BandDescriptor */ 96 public static final int BAND_AM = 0; 97 /** FM radio band. 98 * @see BandDescriptor */ 99 public static final int BAND_FM = 1; 100 /** FM HD radio or DRM band. 101 * @see BandDescriptor */ 102 public static final int BAND_FM_HD = 2; 103 /** AM HD radio or DRM band. 104 * @see BandDescriptor */ 105 public static final int BAND_AM_HD = 3; 106 @IntDef(prefix = { "BAND_" }, value = { 107 BAND_INVALID, 108 BAND_AM, 109 BAND_FM, 110 BAND_AM_HD, 111 BAND_FM_HD, 112 }) 113 @Retention(RetentionPolicy.SOURCE) 114 public @interface Band {} 115 116 // keep in sync with radio_region_t in /system/core/incluse/system/radio.h 117 /** Africa, Europe. 118 * @see BandDescriptor */ 119 public static final int REGION_ITU_1 = 0; 120 /** Americas. 121 * @see BandDescriptor */ 122 public static final int REGION_ITU_2 = 1; 123 /** Russia. 124 * @see BandDescriptor */ 125 public static final int REGION_OIRT = 2; 126 /** Japan. 127 * @see BandDescriptor */ 128 public static final int REGION_JAPAN = 3; 129 /** Korea. 130 * @see BandDescriptor */ 131 public static final int REGION_KOREA = 4; 132 133 /** 134 * Forces mono audio stream reception. 135 * 136 * Analog broadcasts can recover poor reception conditions by jointing 137 * stereo channels into one. Mainly for, but not limited to AM/FM. 138 */ 139 public static final int CONFIG_FORCE_MONO = 1; 140 /** 141 * Forces the analog playback for the supporting radio technology. 142 * 143 * User may disable digital playback for FM HD Radio or hybrid FM/DAB with 144 * this option. This is purely user choice, ie. does not reflect digital- 145 * analog handover state managed from the HAL implementation side. 146 * 147 * Some radio technologies may not support this, ie. DAB. 148 */ 149 public static final int CONFIG_FORCE_ANALOG = 2; 150 /** 151 * Forces the digital playback for the supporting radio technology. 152 * 153 * User may disable digital-analog handover that happens with poor 154 * reception conditions. With digital forced, the radio will remain silent 155 * instead of switching to analog channel if it's available. This is purely 156 * user choice, it does not reflect the actual state of handover. 157 */ 158 public static final int CONFIG_FORCE_DIGITAL = 3; 159 /** 160 * RDS Alternative Frequencies. 161 * 162 * If set and the currently tuned RDS station broadcasts on multiple 163 * channels, radio tuner automatically switches to the best available 164 * alternative. 165 */ 166 public static final int CONFIG_RDS_AF = 4; 167 /** 168 * RDS region-specific program lock-down. 169 * 170 * Allows user to lock to the current region as they move into the 171 * other region. 172 */ 173 public static final int CONFIG_RDS_REG = 5; 174 /** Enables DAB-DAB hard- and implicit-linking (the same content). */ 175 public static final int CONFIG_DAB_DAB_LINKING = 6; 176 /** Enables DAB-FM hard- and implicit-linking (the same content). */ 177 public static final int CONFIG_DAB_FM_LINKING = 7; 178 /** Enables DAB-DAB soft-linking (related content). */ 179 public static final int CONFIG_DAB_DAB_SOFT_LINKING = 8; 180 /** Enables DAB-FM soft-linking (related content). */ 181 public static final int CONFIG_DAB_FM_SOFT_LINKING = 9; 182 183 /** @hide */ 184 @IntDef(prefix = { "CONFIG_" }, value = { 185 CONFIG_FORCE_MONO, 186 CONFIG_FORCE_ANALOG, 187 CONFIG_FORCE_DIGITAL, 188 CONFIG_RDS_AF, 189 CONFIG_RDS_REG, 190 CONFIG_DAB_DAB_LINKING, 191 CONFIG_DAB_FM_LINKING, 192 CONFIG_DAB_DAB_SOFT_LINKING, 193 CONFIG_DAB_FM_SOFT_LINKING, 194 }) 195 @Retention(RetentionPolicy.SOURCE) 196 public @interface ConfigFlag {} 197 198 /***************************************************************************** 199 * Lists properties, options and radio bands supported by a given broadcast radio module. 200 * Each module has a unique ID used to address it when calling RadioManager APIs. 201 * Module properties are returned by {@link #listModules(List <ModuleProperties>)} method. 202 ****************************************************************************/ 203 public static class ModuleProperties implements Parcelable { 204 205 private final int mId; 206 @NonNull private final String mServiceName; 207 private final int mClassId; 208 private final String mImplementor; 209 private final String mProduct; 210 private final String mVersion; 211 private final String mSerial; 212 private final int mNumTuners; 213 private final int mNumAudioSources; 214 private final boolean mIsInitializationRequired; 215 private final boolean mIsCaptureSupported; 216 private final BandDescriptor[] mBands; 217 private final boolean mIsBgScanSupported; 218 private final Set<Integer> mSupportedProgramTypes; 219 private final Set<Integer> mSupportedIdentifierTypes; 220 @Nullable private final Map<String, Integer> mDabFrequencyTable; 221 @NonNull private final Map<String, String> mVendorInfo; 222 223 /** @hide */ 224 public ModuleProperties(int id, String serviceName, int classId, String implementor, 225 String product, String version, String serial, int numTuners, int numAudioSources, 226 boolean isInitializationRequired, boolean isCaptureSupported, 227 BandDescriptor[] bands, boolean isBgScanSupported, 228 @ProgramSelector.ProgramType int[] supportedProgramTypes, 229 @ProgramSelector.IdentifierType int[] supportedIdentifierTypes, 230 @Nullable Map<String, Integer> dabFrequencyTable, 231 Map<String, String> vendorInfo) { 232 mId = id; 233 mServiceName = TextUtils.isEmpty(serviceName) ? "default" : serviceName; 234 mClassId = classId; 235 mImplementor = implementor; 236 mProduct = product; 237 mVersion = version; 238 mSerial = serial; 239 mNumTuners = numTuners; 240 mNumAudioSources = numAudioSources; 241 mIsInitializationRequired = isInitializationRequired; 242 mIsCaptureSupported = isCaptureSupported; 243 mBands = bands; 244 mIsBgScanSupported = isBgScanSupported; 245 mSupportedProgramTypes = arrayToSet(supportedProgramTypes); 246 mSupportedIdentifierTypes = arrayToSet(supportedIdentifierTypes); 247 if (dabFrequencyTable != null) { 248 for (Map.Entry<String, Integer> entry : dabFrequencyTable.entrySet()) { 249 Objects.requireNonNull(entry.getKey()); 250 Objects.requireNonNull(entry.getValue()); 251 } 252 } 253 mDabFrequencyTable = dabFrequencyTable; 254 mVendorInfo = (vendorInfo == null) ? new HashMap<>() : vendorInfo; 255 } 256 257 private static Set<Integer> arrayToSet(int[] arr) { 258 return Arrays.stream(arr).boxed().collect(Collectors.toSet()); 259 } 260 261 private static int[] setToArray(Set<Integer> set) { 262 return set.stream().mapToInt(Integer::intValue).toArray(); 263 } 264 265 /** Unique module identifier provided by the native service. 266 * For use with {@link #openTuner(int, BandConfig, boolean, Callback, Handler)}. 267 * @return the radio module unique identifier. 268 */ 269 public int getId() { 270 return mId; 271 } 272 273 /** 274 * Module service (driver) name as registered with HIDL. 275 * @return the module service name. 276 */ 277 public @NonNull String getServiceName() { 278 return mServiceName; 279 } 280 281 /** Module class identifier: {@link #CLASS_AM_FM}, {@link #CLASS_SAT}, {@link #CLASS_DT} 282 * @return the radio module class identifier. 283 */ 284 public int getClassId() { 285 return mClassId; 286 } 287 288 /** Human readable broadcast radio module implementor 289 * @return the name of the radio module implementator. 290 */ 291 public String getImplementor() { 292 return mImplementor; 293 } 294 295 /** Human readable broadcast radio module product name 296 * @return the radio module product name. 297 */ 298 public String getProduct() { 299 return mProduct; 300 } 301 302 /** Human readable broadcast radio module version number 303 * @return the radio module version. 304 */ 305 public String getVersion() { 306 return mVersion; 307 } 308 309 /** Radio module serial number. 310 * Can be used for subscription services. 311 * @return the radio module serial number. 312 */ 313 public String getSerial() { 314 return mSerial; 315 } 316 317 /** Number of tuners available. 318 * This is the number of tuners that can be open simultaneously. 319 * @return the number of tuners supported. 320 */ 321 public int getNumTuners() { 322 return mNumTuners; 323 } 324 325 /** Number tuner audio sources available. Must be less or equal to getNumTuners(). 326 * When more than one tuner is supported, one is usually for playback and has one 327 * associated audio source and the other is for pre scanning and building a 328 * program list. 329 * @return the number of audio sources available. 330 */ 331 public int getNumAudioSources() { 332 return mNumAudioSources; 333 } 334 335 /** 336 * Checks, if BandConfig initialization (after {@link RadioManager#openTuner}) 337 * is required to be done before other operations or not. 338 * 339 * If it is, the client has to wait for {@link RadioTuner.Callback#onConfigurationChanged} 340 * callback before executing any other operations. Otherwise, such operation will fail 341 * returning {@link RadioManager#STATUS_INVALID_OPERATION} error code. 342 */ 343 public boolean isInitializationRequired() { 344 return mIsInitializationRequired; 345 } 346 347 /** {@code true} if audio capture is possible from radio tuner output. 348 * This indicates if routing to audio devices not connected to the same HAL as the FM radio 349 * is possible (e.g. to USB) or DAR (Digital Audio Recorder) feature can be implemented. 350 * @return {@code true} if audio capture is possible, {@code false} otherwise. 351 */ 352 public boolean isCaptureSupported() { 353 return mIsCaptureSupported; 354 } 355 356 /** 357 * {@code true} if the module supports background scanning. At the given time it may not 358 * be available though, see {@link RadioTuner#startBackgroundScan()}. 359 * 360 * @return {@code true} if background scanning is supported (not necessary available 361 * at a given time), {@code false} otherwise. 362 */ 363 public boolean isBackgroundScanningSupported() { 364 return mIsBgScanSupported; 365 } 366 367 /** 368 * Checks, if a given program type is supported by this tuner. 369 * 370 * If a program type is supported by radio module, it means it can tune 371 * to ProgramSelector of a given type. 372 * 373 * @return {@code true} if a given program type is supported. 374 */ 375 public boolean isProgramTypeSupported(@ProgramSelector.ProgramType int type) { 376 return mSupportedProgramTypes.contains(type); 377 } 378 379 /** 380 * Checks, if a given program identifier is supported by this tuner. 381 * 382 * If an identifier is supported by radio module, it means it can use it for 383 * tuning to ProgramSelector with either primary or secondary Identifier of 384 * a given type. 385 * 386 * @return {@code true} if a given program type is supported. 387 */ 388 public boolean isProgramIdentifierSupported(@ProgramSelector.IdentifierType int type) { 389 return mSupportedIdentifierTypes.contains(type); 390 } 391 392 /** 393 * A frequency table for Digital Audio Broadcasting (DAB). 394 * 395 * The key is a channel name, i.e. 5A, 7B. 396 * 397 * The value is a frequency, in kHz. 398 * 399 * @return a frequency table, or {@code null} if the module doesn't support DAB 400 */ 401 public @Nullable Map<String, Integer> getDabFrequencyTable() { 402 return mDabFrequencyTable; 403 } 404 405 /** 406 * A map of vendor-specific opaque strings, passed from HAL without changes. 407 * Format of these strings can vary across vendors. 408 * 409 * It may be used for extra features, that's not supported by a platform, 410 * for example: preset-slots=6; ultra-hd-capable=false. 411 * 412 * Keys must be prefixed with unique vendor Java-style namespace, 413 * eg. 'com.somecompany.parameter1'. 414 */ 415 public @NonNull Map<String, String> getVendorInfo() { 416 return mVendorInfo; 417 } 418 419 /** List of descriptors for all bands supported by this module. 420 * @return an array of {@link BandDescriptor}. 421 */ 422 public BandDescriptor[] getBands() { 423 return mBands; 424 } 425 426 private ModuleProperties(Parcel in) { 427 mId = in.readInt(); 428 String serviceName = in.readString(); 429 mServiceName = TextUtils.isEmpty(serviceName) ? "default" : serviceName; 430 mClassId = in.readInt(); 431 mImplementor = in.readString(); 432 mProduct = in.readString(); 433 mVersion = in.readString(); 434 mSerial = in.readString(); 435 mNumTuners = in.readInt(); 436 mNumAudioSources = in.readInt(); 437 mIsInitializationRequired = in.readInt() == 1; 438 mIsCaptureSupported = in.readInt() == 1; 439 Parcelable[] tmp = in.readParcelableArray(BandDescriptor.class.getClassLoader()); 440 mBands = new BandDescriptor[tmp.length]; 441 for (int i = 0; i < tmp.length; i++) { 442 mBands[i] = (BandDescriptor) tmp[i]; 443 } 444 mIsBgScanSupported = in.readInt() == 1; 445 mSupportedProgramTypes = arrayToSet(in.createIntArray()); 446 mSupportedIdentifierTypes = arrayToSet(in.createIntArray()); 447 mDabFrequencyTable = Utils.readStringIntMap(in); 448 mVendorInfo = Utils.readStringMap(in); 449 } 450 451 public static final Parcelable.Creator<ModuleProperties> CREATOR 452 = new Parcelable.Creator<ModuleProperties>() { 453 public ModuleProperties createFromParcel(Parcel in) { 454 return new ModuleProperties(in); 455 } 456 457 public ModuleProperties[] newArray(int size) { 458 return new ModuleProperties[size]; 459 } 460 }; 461 462 @Override 463 public void writeToParcel(Parcel dest, int flags) { 464 dest.writeInt(mId); 465 dest.writeString(mServiceName); 466 dest.writeInt(mClassId); 467 dest.writeString(mImplementor); 468 dest.writeString(mProduct); 469 dest.writeString(mVersion); 470 dest.writeString(mSerial); 471 dest.writeInt(mNumTuners); 472 dest.writeInt(mNumAudioSources); 473 dest.writeInt(mIsInitializationRequired ? 1 : 0); 474 dest.writeInt(mIsCaptureSupported ? 1 : 0); 475 dest.writeParcelableArray(mBands, flags); 476 dest.writeInt(mIsBgScanSupported ? 1 : 0); 477 dest.writeIntArray(setToArray(mSupportedProgramTypes)); 478 dest.writeIntArray(setToArray(mSupportedIdentifierTypes)); 479 Utils.writeStringIntMap(dest, mDabFrequencyTable); 480 Utils.writeStringMap(dest, mVendorInfo); 481 } 482 483 @Override 484 public int describeContents() { 485 return 0; 486 } 487 488 @Override 489 public String toString() { 490 return "ModuleProperties [mId=" + mId 491 + ", mServiceName=" + mServiceName + ", mClassId=" + mClassId 492 + ", mImplementor=" + mImplementor + ", mProduct=" + mProduct 493 + ", mVersion=" + mVersion + ", mSerial=" + mSerial 494 + ", mNumTuners=" + mNumTuners 495 + ", mNumAudioSources=" + mNumAudioSources 496 + ", mIsInitializationRequired=" + mIsInitializationRequired 497 + ", mIsCaptureSupported=" + mIsCaptureSupported 498 + ", mIsBgScanSupported=" + mIsBgScanSupported 499 + ", mBands=" + Arrays.toString(mBands) + "]"; 500 } 501 502 @Override 503 public int hashCode() { 504 return Objects.hash(mId, mServiceName, mClassId, mImplementor, mProduct, mVersion, 505 mSerial, mNumTuners, mNumAudioSources, mIsInitializationRequired, 506 mIsCaptureSupported, mBands, mIsBgScanSupported, mDabFrequencyTable, mVendorInfo); 507 } 508 509 @Override 510 public boolean equals(Object obj) { 511 if (this == obj) return true; 512 if (!(obj instanceof ModuleProperties)) return false; 513 ModuleProperties other = (ModuleProperties) obj; 514 515 if (mId != other.getId()) return false; 516 if (!TextUtils.equals(mServiceName, other.mServiceName)) return false; 517 if (mClassId != other.mClassId) return false; 518 if (!Objects.equals(mImplementor, other.mImplementor)) return false; 519 if (!Objects.equals(mProduct, other.mProduct)) return false; 520 if (!Objects.equals(mVersion, other.mVersion)) return false; 521 if (!Objects.equals(mSerial, other.mSerial)) return false; 522 if (mNumTuners != other.mNumTuners) return false; 523 if (mNumAudioSources != other.mNumAudioSources) return false; 524 if (mIsInitializationRequired != other.mIsInitializationRequired) return false; 525 if (mIsCaptureSupported != other.mIsCaptureSupported) return false; 526 if (!Objects.equals(mBands, other.mBands)) return false; 527 if (mIsBgScanSupported != other.mIsBgScanSupported) return false; 528 if (!Objects.equals(mDabFrequencyTable, other.mDabFrequencyTable)) return false; 529 if (!Objects.equals(mVendorInfo, other.mVendorInfo)) return false; 530 return true; 531 } 532 } 533 534 /** Radio band descriptor: an element in ModuleProperties bands array. 535 * It is either an instance of {@link FmBandDescriptor} or {@link AmBandDescriptor} */ 536 public static class BandDescriptor implements Parcelable { 537 538 private final int mRegion; 539 private final int mType; 540 private final int mLowerLimit; 541 private final int mUpperLimit; 542 private final int mSpacing; 543 544 BandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing) { 545 if (type != BAND_AM && type != BAND_FM && type != BAND_FM_HD && type != BAND_AM_HD) { 546 throw new IllegalArgumentException("Unsupported band: " + type); 547 } 548 mRegion = region; 549 mType = type; 550 mLowerLimit = lowerLimit; 551 mUpperLimit = upperLimit; 552 mSpacing = spacing; 553 } 554 555 /** Region this band applies to. E.g. {@link #REGION_ITU_1} 556 * @return the region this band is associated to. 557 */ 558 public int getRegion() { 559 return mRegion; 560 } 561 /** Band type, e.g {@link #BAND_FM}. Defines the subclass this descriptor can be cast to: 562 * <ul> 563 * <li>{@link #BAND_FM} or {@link #BAND_FM_HD} cast to {@link FmBandDescriptor}, </li> 564 * <li>{@link #BAND_AM} cast to {@link AmBandDescriptor}, </li> 565 * </ul> 566 * @return the band type. 567 */ 568 public int getType() { 569 return mType; 570 } 571 572 /** 573 * Checks if the band is either AM or AM_HD. 574 * 575 * @return {@code true}, if band is AM or AM_HD. 576 */ 577 public boolean isAmBand() { 578 return mType == BAND_AM || mType == BAND_AM_HD; 579 } 580 581 /** 582 * Checks if the band is either FM or FM_HD. 583 * 584 * @return {@code true}, if band is FM or FM_HD. 585 */ 586 public boolean isFmBand() { 587 return mType == BAND_FM || mType == BAND_FM_HD; 588 } 589 590 /** Lower band limit expressed in units according to band type. 591 * Currently all defined band types express channels as frequency in kHz 592 * @return the lower band limit. 593 */ 594 public int getLowerLimit() { 595 return mLowerLimit; 596 } 597 /** Upper band limit expressed in units according to band type. 598 * Currently all defined band types express channels as frequency in kHz 599 * @return the upper band limit. 600 */ 601 public int getUpperLimit() { 602 return mUpperLimit; 603 } 604 /** Channel spacing in units according to band type. 605 * Currently all defined band types express channels as frequency in kHz 606 * @return the channel spacing. 607 */ 608 public int getSpacing() { 609 return mSpacing; 610 } 611 612 private BandDescriptor(Parcel in) { 613 mRegion = in.readInt(); 614 mType = in.readInt(); 615 mLowerLimit = in.readInt(); 616 mUpperLimit = in.readInt(); 617 mSpacing = in.readInt(); 618 } 619 620 private static int lookupTypeFromParcel(Parcel in) { 621 int pos = in.dataPosition(); 622 in.readInt(); // skip region 623 int type = in.readInt(); 624 in.setDataPosition(pos); 625 return type; 626 } 627 628 public static final Parcelable.Creator<BandDescriptor> CREATOR 629 = new Parcelable.Creator<BandDescriptor>() { 630 public BandDescriptor createFromParcel(Parcel in) { 631 int type = lookupTypeFromParcel(in); 632 switch (type) { 633 case BAND_FM: 634 case BAND_FM_HD: 635 return new FmBandDescriptor(in); 636 case BAND_AM: 637 case BAND_AM_HD: 638 return new AmBandDescriptor(in); 639 default: 640 throw new IllegalArgumentException("Unsupported band: " + type); 641 } 642 } 643 644 public BandDescriptor[] newArray(int size) { 645 return new BandDescriptor[size]; 646 } 647 }; 648 649 @Override 650 public void writeToParcel(Parcel dest, int flags) { 651 dest.writeInt(mRegion); 652 dest.writeInt(mType); 653 dest.writeInt(mLowerLimit); 654 dest.writeInt(mUpperLimit); 655 dest.writeInt(mSpacing); 656 } 657 658 @Override 659 public int describeContents() { 660 return 0; 661 } 662 663 @Override 664 public String toString() { 665 return "BandDescriptor [mRegion=" + mRegion + ", mType=" + mType + ", mLowerLimit=" 666 + mLowerLimit + ", mUpperLimit=" + mUpperLimit + ", mSpacing=" + mSpacing + "]"; 667 } 668 669 @Override 670 public int hashCode() { 671 final int prime = 31; 672 int result = 1; 673 result = prime * result + mRegion; 674 result = prime * result + mType; 675 result = prime * result + mLowerLimit; 676 result = prime * result + mUpperLimit; 677 result = prime * result + mSpacing; 678 return result; 679 } 680 681 @Override 682 public boolean equals(Object obj) { 683 if (this == obj) 684 return true; 685 if (!(obj instanceof BandDescriptor)) 686 return false; 687 BandDescriptor other = (BandDescriptor) obj; 688 if (mRegion != other.getRegion()) 689 return false; 690 if (mType != other.getType()) 691 return false; 692 if (mLowerLimit != other.getLowerLimit()) 693 return false; 694 if (mUpperLimit != other.getUpperLimit()) 695 return false; 696 if (mSpacing != other.getSpacing()) 697 return false; 698 return true; 699 } 700 } 701 702 /** FM band descriptor 703 * @see #BAND_FM 704 * @see #BAND_FM_HD */ 705 public static class FmBandDescriptor extends BandDescriptor { 706 private final boolean mStereo; 707 private final boolean mRds; 708 private final boolean mTa; 709 private final boolean mAf; 710 private final boolean mEa; 711 712 /** @hide */ 713 public FmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing, 714 boolean stereo, boolean rds, boolean ta, boolean af, boolean ea) { 715 super(region, type, lowerLimit, upperLimit, spacing); 716 mStereo = stereo; 717 mRds = rds; 718 mTa = ta; 719 mAf = af; 720 mEa = ea; 721 } 722 723 /** Stereo is supported 724 * @return {@code true} if stereo is supported, {@code false} otherwise. 725 */ 726 public boolean isStereoSupported() { 727 return mStereo; 728 } 729 /** RDS or RBDS(if region is ITU2) is supported 730 * @return {@code true} if RDS or RBDS is supported, {@code false} otherwise. 731 */ 732 public boolean isRdsSupported() { 733 return mRds; 734 } 735 /** Traffic announcement is supported 736 * @return {@code true} if TA is supported, {@code false} otherwise. 737 */ 738 public boolean isTaSupported() { 739 return mTa; 740 } 741 /** Alternate Frequency Switching is supported 742 * @return {@code true} if AF switching is supported, {@code false} otherwise. 743 */ 744 public boolean isAfSupported() { 745 return mAf; 746 } 747 748 /** Emergency Announcement is supported 749 * @return {@code true} if Emergency annoucement is supported, {@code false} otherwise. 750 */ 751 public boolean isEaSupported() { 752 return mEa; 753 } 754 755 /* Parcelable implementation */ 756 private FmBandDescriptor(Parcel in) { 757 super(in); 758 mStereo = in.readByte() == 1; 759 mRds = in.readByte() == 1; 760 mTa = in.readByte() == 1; 761 mAf = in.readByte() == 1; 762 mEa = in.readByte() == 1; 763 } 764 765 public static final Parcelable.Creator<FmBandDescriptor> CREATOR 766 = new Parcelable.Creator<FmBandDescriptor>() { 767 public FmBandDescriptor createFromParcel(Parcel in) { 768 return new FmBandDescriptor(in); 769 } 770 771 public FmBandDescriptor[] newArray(int size) { 772 return new FmBandDescriptor[size]; 773 } 774 }; 775 776 @Override 777 public void writeToParcel(Parcel dest, int flags) { 778 super.writeToParcel(dest, flags); 779 dest.writeByte((byte) (mStereo ? 1 : 0)); 780 dest.writeByte((byte) (mRds ? 1 : 0)); 781 dest.writeByte((byte) (mTa ? 1 : 0)); 782 dest.writeByte((byte) (mAf ? 1 : 0)); 783 dest.writeByte((byte) (mEa ? 1 : 0)); 784 } 785 786 @Override 787 public int describeContents() { 788 return 0; 789 } 790 791 @Override 792 public String toString() { 793 return "FmBandDescriptor [ "+ super.toString() + " mStereo=" + mStereo 794 + ", mRds=" + mRds + ", mTa=" + mTa + ", mAf=" + mAf + 795 ", mEa =" + mEa + "]"; 796 } 797 798 @Override 799 public int hashCode() { 800 final int prime = 31; 801 int result = super.hashCode(); 802 result = prime * result + (mStereo ? 1 : 0); 803 result = prime * result + (mRds ? 1 : 0); 804 result = prime * result + (mTa ? 1 : 0); 805 result = prime * result + (mAf ? 1 : 0); 806 result = prime * result + (mEa ? 1 : 0); 807 return result; 808 } 809 810 @Override 811 public boolean equals(Object obj) { 812 if (this == obj) 813 return true; 814 if (!super.equals(obj)) 815 return false; 816 if (!(obj instanceof FmBandDescriptor)) 817 return false; 818 FmBandDescriptor other = (FmBandDescriptor) obj; 819 if (mStereo != other.isStereoSupported()) 820 return false; 821 if (mRds != other.isRdsSupported()) 822 return false; 823 if (mTa != other.isTaSupported()) 824 return false; 825 if (mAf != other.isAfSupported()) 826 return false; 827 if (mEa != other.isEaSupported()) 828 return false; 829 return true; 830 } 831 } 832 833 /** AM band descriptor. 834 * @see #BAND_AM */ 835 public static class AmBandDescriptor extends BandDescriptor { 836 837 private final boolean mStereo; 838 839 /** @hide */ 840 public AmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing, 841 boolean stereo) { 842 super(region, type, lowerLimit, upperLimit, spacing); 843 mStereo = stereo; 844 } 845 846 /** Stereo is supported 847 * @return {@code true} if stereo is supported, {@code false} otherwise. 848 */ 849 public boolean isStereoSupported() { 850 return mStereo; 851 } 852 853 private AmBandDescriptor(Parcel in) { 854 super(in); 855 mStereo = in.readByte() == 1; 856 } 857 858 public static final Parcelable.Creator<AmBandDescriptor> CREATOR 859 = new Parcelable.Creator<AmBandDescriptor>() { 860 public AmBandDescriptor createFromParcel(Parcel in) { 861 return new AmBandDescriptor(in); 862 } 863 864 public AmBandDescriptor[] newArray(int size) { 865 return new AmBandDescriptor[size]; 866 } 867 }; 868 869 @Override 870 public void writeToParcel(Parcel dest, int flags) { 871 super.writeToParcel(dest, flags); 872 dest.writeByte((byte) (mStereo ? 1 : 0)); 873 } 874 875 @Override 876 public int describeContents() { 877 return 0; 878 } 879 880 @Override 881 public String toString() { 882 return "AmBandDescriptor [ "+ super.toString() + " mStereo=" + mStereo + "]"; 883 } 884 885 @Override 886 public int hashCode() { 887 final int prime = 31; 888 int result = super.hashCode(); 889 result = prime * result + (mStereo ? 1 : 0); 890 return result; 891 } 892 893 @Override 894 public boolean equals(Object obj) { 895 if (this == obj) 896 return true; 897 if (!super.equals(obj)) 898 return false; 899 if (!(obj instanceof AmBandDescriptor)) 900 return false; 901 AmBandDescriptor other = (AmBandDescriptor) obj; 902 if (mStereo != other.isStereoSupported()) 903 return false; 904 return true; 905 } 906 } 907 908 909 /** Radio band configuration. */ 910 public static class BandConfig implements Parcelable { 911 912 @NonNull final BandDescriptor mDescriptor; 913 914 BandConfig(BandDescriptor descriptor) { 915 mDescriptor = Objects.requireNonNull(descriptor); 916 } 917 918 BandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing) { 919 mDescriptor = new BandDescriptor(region, type, lowerLimit, upperLimit, spacing); 920 } 921 922 private BandConfig(Parcel in) { 923 mDescriptor = new BandDescriptor(in); 924 } 925 926 BandDescriptor getDescriptor() { 927 return mDescriptor; 928 } 929 930 /** Region this band applies to. E.g. {@link #REGION_ITU_1} 931 * @return the region associated with this band. 932 */ 933 public int getRegion() { 934 return mDescriptor.getRegion(); 935 } 936 /** Band type, e.g {@link #BAND_FM}. Defines the subclass this descriptor can be cast to: 937 * <ul> 938 * <li>{@link #BAND_FM} or {@link #BAND_FM_HD} cast to {@link FmBandDescriptor}, </li> 939 * <li>{@link #BAND_AM} cast to {@link AmBandDescriptor}, </li> 940 * </ul> 941 * @return the band type. 942 */ 943 public int getType() { 944 return mDescriptor.getType(); 945 } 946 /** Lower band limit expressed in units according to band type. 947 * Currently all defined band types express channels as frequency in kHz 948 * @return the lower band limit. 949 */ 950 public int getLowerLimit() { 951 return mDescriptor.getLowerLimit(); 952 } 953 /** Upper band limit expressed in units according to band type. 954 * Currently all defined band types express channels as frequency in kHz 955 * @return the upper band limit. 956 */ 957 public int getUpperLimit() { 958 return mDescriptor.getUpperLimit(); 959 } 960 /** Channel spacing in units according to band type. 961 * Currently all defined band types express channels as frequency in kHz 962 * @return the channel spacing. 963 */ 964 public int getSpacing() { 965 return mDescriptor.getSpacing(); 966 } 967 968 969 public static final Parcelable.Creator<BandConfig> CREATOR 970 = new Parcelable.Creator<BandConfig>() { 971 public BandConfig createFromParcel(Parcel in) { 972 int type = BandDescriptor.lookupTypeFromParcel(in); 973 switch (type) { 974 case BAND_FM: 975 case BAND_FM_HD: 976 return new FmBandConfig(in); 977 case BAND_AM: 978 case BAND_AM_HD: 979 return new AmBandConfig(in); 980 default: 981 throw new IllegalArgumentException("Unsupported band: " + type); 982 } 983 } 984 985 public BandConfig[] newArray(int size) { 986 return new BandConfig[size]; 987 } 988 }; 989 990 @Override 991 public void writeToParcel(Parcel dest, int flags) { 992 mDescriptor.writeToParcel(dest, flags); 993 } 994 995 @Override 996 public int describeContents() { 997 return 0; 998 } 999 1000 @Override 1001 public String toString() { 1002 return "BandConfig [ " + mDescriptor.toString() + "]"; 1003 } 1004 1005 @Override 1006 public int hashCode() { 1007 final int prime = 31; 1008 int result = 1; 1009 result = prime * result + mDescriptor.hashCode(); 1010 return result; 1011 } 1012 1013 @Override 1014 public boolean equals(Object obj) { 1015 if (this == obj) 1016 return true; 1017 if (!(obj instanceof BandConfig)) 1018 return false; 1019 BandConfig other = (BandConfig) obj; 1020 BandDescriptor otherDesc = other.getDescriptor(); 1021 if ((mDescriptor == null) != (otherDesc == null)) return false; 1022 if (mDescriptor != null && !mDescriptor.equals(otherDesc)) return false; 1023 return true; 1024 } 1025 } 1026 1027 /** FM band configuration. 1028 * @see #BAND_FM 1029 * @see #BAND_FM_HD */ 1030 public static class FmBandConfig extends BandConfig { 1031 private final boolean mStereo; 1032 private final boolean mRds; 1033 private final boolean mTa; 1034 private final boolean mAf; 1035 private final boolean mEa; 1036 1037 /** @hide */ 1038 public FmBandConfig(FmBandDescriptor descriptor) { 1039 super((BandDescriptor)descriptor); 1040 mStereo = descriptor.isStereoSupported(); 1041 mRds = descriptor.isRdsSupported(); 1042 mTa = descriptor.isTaSupported(); 1043 mAf = descriptor.isAfSupported(); 1044 mEa = descriptor.isEaSupported(); 1045 } 1046 1047 FmBandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing, 1048 boolean stereo, boolean rds, boolean ta, boolean af, boolean ea) { 1049 super(region, type, lowerLimit, upperLimit, spacing); 1050 mStereo = stereo; 1051 mRds = rds; 1052 mTa = ta; 1053 mAf = af; 1054 mEa = ea; 1055 } 1056 1057 /** Get stereo enable state 1058 * @return the enable state. 1059 */ 1060 public boolean getStereo() { 1061 return mStereo; 1062 } 1063 1064 /** Get RDS or RBDS(if region is ITU2) enable state 1065 * @return the enable state. 1066 */ 1067 public boolean getRds() { 1068 return mRds; 1069 } 1070 1071 /** Get Traffic announcement enable state 1072 * @return the enable state. 1073 */ 1074 public boolean getTa() { 1075 return mTa; 1076 } 1077 1078 /** Get Alternate Frequency Switching enable state 1079 * @return the enable state. 1080 */ 1081 public boolean getAf() { 1082 return mAf; 1083 } 1084 1085 /** 1086 * Get Emergency announcement enable state 1087 * @return the enable state. 1088 */ 1089 public boolean getEa() { 1090 return mEa; 1091 } 1092 1093 private FmBandConfig(Parcel in) { 1094 super(in); 1095 mStereo = in.readByte() == 1; 1096 mRds = in.readByte() == 1; 1097 mTa = in.readByte() == 1; 1098 mAf = in.readByte() == 1; 1099 mEa = in.readByte() == 1; 1100 } 1101 1102 public static final Parcelable.Creator<FmBandConfig> CREATOR 1103 = new Parcelable.Creator<FmBandConfig>() { 1104 public FmBandConfig createFromParcel(Parcel in) { 1105 return new FmBandConfig(in); 1106 } 1107 1108 public FmBandConfig[] newArray(int size) { 1109 return new FmBandConfig[size]; 1110 } 1111 }; 1112 1113 @Override 1114 public void writeToParcel(Parcel dest, int flags) { 1115 super.writeToParcel(dest, flags); 1116 dest.writeByte((byte) (mStereo ? 1 : 0)); 1117 dest.writeByte((byte) (mRds ? 1 : 0)); 1118 dest.writeByte((byte) (mTa ? 1 : 0)); 1119 dest.writeByte((byte) (mAf ? 1 : 0)); 1120 dest.writeByte((byte) (mEa ? 1 : 0)); 1121 } 1122 1123 @Override 1124 public int describeContents() { 1125 return 0; 1126 } 1127 1128 @Override 1129 public String toString() { 1130 return "FmBandConfig [" + super.toString() 1131 + ", mStereo=" + mStereo + ", mRds=" + mRds + ", mTa=" + mTa 1132 + ", mAf=" + mAf + ", mEa =" + mEa + "]"; 1133 } 1134 1135 @Override 1136 public int hashCode() { 1137 final int prime = 31; 1138 int result = super.hashCode(); 1139 result = prime * result + (mStereo ? 1 : 0); 1140 result = prime * result + (mRds ? 1 : 0); 1141 result = prime * result + (mTa ? 1 : 0); 1142 result = prime * result + (mAf ? 1 : 0); 1143 result = prime * result + (mEa ? 1 : 0); 1144 return result; 1145 } 1146 1147 @Override 1148 public boolean equals(Object obj) { 1149 if (this == obj) 1150 return true; 1151 if (!super.equals(obj)) 1152 return false; 1153 if (!(obj instanceof FmBandConfig)) 1154 return false; 1155 FmBandConfig other = (FmBandConfig) obj; 1156 if (mStereo != other.mStereo) 1157 return false; 1158 if (mRds != other.mRds) 1159 return false; 1160 if (mTa != other.mTa) 1161 return false; 1162 if (mAf != other.mAf) 1163 return false; 1164 if (mEa != other.mEa) 1165 return false; 1166 return true; 1167 } 1168 1169 /** 1170 * Builder class for {@link FmBandConfig} objects. 1171 */ 1172 public static class Builder { 1173 private final BandDescriptor mDescriptor; 1174 private boolean mStereo; 1175 private boolean mRds; 1176 private boolean mTa; 1177 private boolean mAf; 1178 private boolean mEa; 1179 1180 /** 1181 * Constructs a new Builder with the defaults from an {@link FmBandDescriptor} . 1182 * @param descriptor the FmBandDescriptor defaults are read from . 1183 */ 1184 public Builder(FmBandDescriptor descriptor) { 1185 mDescriptor = new BandDescriptor(descriptor.getRegion(), descriptor.getType(), 1186 descriptor.getLowerLimit(), descriptor.getUpperLimit(), 1187 descriptor.getSpacing()); 1188 mStereo = descriptor.isStereoSupported(); 1189 mRds = descriptor.isRdsSupported(); 1190 mTa = descriptor.isTaSupported(); 1191 mAf = descriptor.isAfSupported(); 1192 mEa = descriptor.isEaSupported(); 1193 } 1194 1195 /** 1196 * Constructs a new Builder from a given {@link FmBandConfig} 1197 * @param config the FmBandConfig object whose data will be reused in the new Builder. 1198 */ 1199 public Builder(FmBandConfig config) { 1200 mDescriptor = new BandDescriptor(config.getRegion(), config.getType(), 1201 config.getLowerLimit(), config.getUpperLimit(), config.getSpacing()); 1202 mStereo = config.getStereo(); 1203 mRds = config.getRds(); 1204 mTa = config.getTa(); 1205 mAf = config.getAf(); 1206 mEa = config.getEa(); 1207 } 1208 1209 /** 1210 * Combines all of the parameters that have been set and return a new 1211 * {@link FmBandConfig} object. 1212 * @return a new {@link FmBandConfig} object 1213 */ 1214 public FmBandConfig build() { 1215 FmBandConfig config = new FmBandConfig(mDescriptor.getRegion(), 1216 mDescriptor.getType(), mDescriptor.getLowerLimit(), 1217 mDescriptor.getUpperLimit(), mDescriptor.getSpacing(), 1218 mStereo, mRds, mTa, mAf, mEa); 1219 return config; 1220 } 1221 1222 /** Set stereo enable state 1223 * @param state The new enable state. 1224 * @return the same Builder instance. 1225 */ 1226 public Builder setStereo(boolean state) { 1227 mStereo = state; 1228 return this; 1229 } 1230 1231 /** Set RDS or RBDS(if region is ITU2) enable state 1232 * @param state The new enable state. 1233 * @return the same Builder instance. 1234 */ 1235 public Builder setRds(boolean state) { 1236 mRds = state; 1237 return this; 1238 } 1239 1240 /** Set Traffic announcement enable state 1241 * @param state The new enable state. 1242 * @return the same Builder instance. 1243 */ 1244 public Builder setTa(boolean state) { 1245 mTa = state; 1246 return this; 1247 } 1248 1249 /** Set Alternate Frequency Switching enable state 1250 * @param state The new enable state. 1251 * @return the same Builder instance. 1252 */ 1253 public Builder setAf(boolean state) { 1254 mAf = state; 1255 return this; 1256 } 1257 1258 /** Set Emergency Announcement enable state 1259 * @param state The new enable state. 1260 * @return the same Builder instance. 1261 */ 1262 public Builder setEa(boolean state) { 1263 mEa = state; 1264 return this; 1265 } 1266 }; 1267 } 1268 1269 /** AM band configuration. 1270 * @see #BAND_AM */ 1271 public static class AmBandConfig extends BandConfig { 1272 private final boolean mStereo; 1273 1274 /** @hide */ 1275 public AmBandConfig(AmBandDescriptor descriptor) { 1276 super((BandDescriptor)descriptor); 1277 mStereo = descriptor.isStereoSupported(); 1278 } 1279 1280 AmBandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing, 1281 boolean stereo) { 1282 super(region, type, lowerLimit, upperLimit, spacing); 1283 mStereo = stereo; 1284 } 1285 1286 /** Get stereo enable state 1287 * @return the enable state. 1288 */ 1289 public boolean getStereo() { 1290 return mStereo; 1291 } 1292 1293 private AmBandConfig(Parcel in) { 1294 super(in); 1295 mStereo = in.readByte() == 1; 1296 } 1297 1298 public static final Parcelable.Creator<AmBandConfig> CREATOR 1299 = new Parcelable.Creator<AmBandConfig>() { 1300 public AmBandConfig createFromParcel(Parcel in) { 1301 return new AmBandConfig(in); 1302 } 1303 1304 public AmBandConfig[] newArray(int size) { 1305 return new AmBandConfig[size]; 1306 } 1307 }; 1308 1309 @Override 1310 public void writeToParcel(Parcel dest, int flags) { 1311 super.writeToParcel(dest, flags); 1312 dest.writeByte((byte) (mStereo ? 1 : 0)); 1313 } 1314 1315 @Override 1316 public int describeContents() { 1317 return 0; 1318 } 1319 1320 @Override 1321 public String toString() { 1322 return "AmBandConfig [" + super.toString() 1323 + ", mStereo=" + mStereo + "]"; 1324 } 1325 1326 @Override 1327 public int hashCode() { 1328 final int prime = 31; 1329 int result = super.hashCode(); 1330 result = prime * result + (mStereo ? 1 : 0); 1331 return result; 1332 } 1333 1334 @Override 1335 public boolean equals(Object obj) { 1336 if (this == obj) 1337 return true; 1338 if (!super.equals(obj)) 1339 return false; 1340 if (!(obj instanceof AmBandConfig)) 1341 return false; 1342 AmBandConfig other = (AmBandConfig) obj; 1343 if (mStereo != other.getStereo()) 1344 return false; 1345 return true; 1346 } 1347 1348 /** 1349 * Builder class for {@link AmBandConfig} objects. 1350 */ 1351 public static class Builder { 1352 private final BandDescriptor mDescriptor; 1353 private boolean mStereo; 1354 1355 /** 1356 * Constructs a new Builder with the defaults from an {@link AmBandDescriptor} . 1357 * @param descriptor the FmBandDescriptor defaults are read from . 1358 */ 1359 public Builder(AmBandDescriptor descriptor) { 1360 mDescriptor = new BandDescriptor(descriptor.getRegion(), descriptor.getType(), 1361 descriptor.getLowerLimit(), descriptor.getUpperLimit(), 1362 descriptor.getSpacing()); 1363 mStereo = descriptor.isStereoSupported(); 1364 } 1365 1366 /** 1367 * Constructs a new Builder from a given {@link AmBandConfig} 1368 * @param config the FmBandConfig object whose data will be reused in the new Builder. 1369 */ 1370 public Builder(AmBandConfig config) { 1371 mDescriptor = new BandDescriptor(config.getRegion(), config.getType(), 1372 config.getLowerLimit(), config.getUpperLimit(), config.getSpacing()); 1373 mStereo = config.getStereo(); 1374 } 1375 1376 /** 1377 * Combines all of the parameters that have been set and return a new 1378 * {@link AmBandConfig} object. 1379 * @return a new {@link AmBandConfig} object 1380 */ 1381 public AmBandConfig build() { 1382 AmBandConfig config = new AmBandConfig(mDescriptor.getRegion(), 1383 mDescriptor.getType(), mDescriptor.getLowerLimit(), 1384 mDescriptor.getUpperLimit(), mDescriptor.getSpacing(), 1385 mStereo); 1386 return config; 1387 } 1388 1389 /** Set stereo enable state 1390 * @param state The new enable state. 1391 * @return the same Builder instance. 1392 */ 1393 public Builder setStereo(boolean state) { 1394 mStereo = state; 1395 return this; 1396 } 1397 }; 1398 } 1399 1400 /** Radio program information. */ 1401 public static class ProgramInfo implements Parcelable { 1402 1403 // sourced from hardware/interfaces/broadcastradio/2.0/types.hal 1404 private static final int FLAG_LIVE = 1 << 0; 1405 private static final int FLAG_MUTED = 1 << 1; 1406 private static final int FLAG_TRAFFIC_PROGRAM = 1 << 2; 1407 private static final int FLAG_TRAFFIC_ANNOUNCEMENT = 1 << 3; 1408 private static final int FLAG_TUNED = 1 << 4; 1409 private static final int FLAG_STEREO = 1 << 5; 1410 1411 @NonNull private final ProgramSelector mSelector; 1412 @Nullable private final ProgramSelector.Identifier mLogicallyTunedTo; 1413 @Nullable private final ProgramSelector.Identifier mPhysicallyTunedTo; 1414 @NonNull private final Collection<ProgramSelector.Identifier> mRelatedContent; 1415 private final int mInfoFlags; 1416 private final int mSignalQuality; 1417 @Nullable private final RadioMetadata mMetadata; 1418 @NonNull private final Map<String, String> mVendorInfo; 1419 1420 /** @hide */ 1421 public ProgramInfo(@NonNull ProgramSelector selector, 1422 @Nullable ProgramSelector.Identifier logicallyTunedTo, 1423 @Nullable ProgramSelector.Identifier physicallyTunedTo, 1424 @Nullable Collection<ProgramSelector.Identifier> relatedContent, 1425 int infoFlags, int signalQuality, @Nullable RadioMetadata metadata, 1426 @Nullable Map<String, String> vendorInfo) { 1427 mSelector = Objects.requireNonNull(selector); 1428 mLogicallyTunedTo = logicallyTunedTo; 1429 mPhysicallyTunedTo = physicallyTunedTo; 1430 if (relatedContent == null) { 1431 mRelatedContent = Collections.emptyList(); 1432 } else { 1433 Preconditions.checkCollectionElementsNotNull(relatedContent, "relatedContent"); 1434 mRelatedContent = relatedContent; 1435 } 1436 mInfoFlags = infoFlags; 1437 mSignalQuality = signalQuality; 1438 mMetadata = metadata; 1439 mVendorInfo = (vendorInfo == null) ? new HashMap<>() : vendorInfo; 1440 } 1441 1442 /** 1443 * Program selector, necessary for tuning to a program. 1444 * 1445 * @return the program selector. 1446 */ 1447 public @NonNull ProgramSelector getSelector() { 1448 return mSelector; 1449 } 1450 1451 /** 1452 * Identifier currently used for program selection. 1453 * 1454 * This identifier can be used to determine which technology is 1455 * currently being used for reception. 1456 * 1457 * Some program selectors contain tuning information for different radio 1458 * technologies (i.e. FM RDS and DAB). For example, user may tune using 1459 * a ProgramSelector with RDS_PI primary identifier, but the tuner hardware 1460 * may choose to use DAB technology to make actual tuning. This identifier 1461 * must reflect that. 1462 */ 1463 public @Nullable ProgramSelector.Identifier getLogicallyTunedTo() { 1464 return mLogicallyTunedTo; 1465 } 1466 1467 /** 1468 * Identifier currently used by hardware to physically tune to a channel. 1469 * 1470 * Some radio technologies broadcast the same program on multiple channels, 1471 * i.e. with RDS AF the same program may be broadcasted on multiple 1472 * alternative frequencies; the same DAB program may be broadcast on 1473 * multiple ensembles. This identifier points to the channel to which the 1474 * radio hardware is physically tuned to. 1475 */ 1476 public @Nullable ProgramSelector.Identifier getPhysicallyTunedTo() { 1477 return mPhysicallyTunedTo; 1478 } 1479 1480 /** 1481 * Primary identifiers of related contents. 1482 * 1483 * Some radio technologies provide pointers to other programs that carry 1484 * related content (i.e. DAB soft-links). This field is a list of pointers 1485 * to other programs on the program list. 1486 * 1487 * Please note, that these identifiers does not have to exist on the program 1488 * list - i.e. DAB tuner may provide information on FM RDS alternatives 1489 * despite not supporting FM RDS. If the system has multiple tuners, another 1490 * one may have it on its list. 1491 */ 1492 public @Nullable Collection<ProgramSelector.Identifier> getRelatedContent() { 1493 return mRelatedContent; 1494 } 1495 1496 /** Main channel expressed in units according to band type. 1497 * Currently all defined band types express channels as frequency in kHz 1498 * @return the program channel 1499 * @deprecated Use {@link getSelector()} instead. 1500 */ 1501 @Deprecated 1502 public int getChannel() { 1503 try { 1504 return (int) mSelector.getFirstId(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY); 1505 } catch (IllegalArgumentException ex) { 1506 Log.w(TAG, "Not an AM/FM program"); 1507 return 0; 1508 } 1509 } 1510 1511 /** Sub channel ID. E.g 1 for HD radio HD1 1512 * @return the program sub channel 1513 * @deprecated Use {@link getSelector()} instead. 1514 */ 1515 @Deprecated 1516 public int getSubChannel() { 1517 try { 1518 return (int) mSelector.getFirstId( 1519 ProgramSelector.IDENTIFIER_TYPE_HD_SUBCHANNEL) + 1; 1520 } catch (IllegalArgumentException ex) { 1521 // this is a normal behavior for analog AM/FM selector 1522 return 0; 1523 } 1524 } 1525 1526 /** {@code true} if the tuner is currently tuned on a valid station 1527 * @return {@code true} if currently tuned, {@code false} otherwise. 1528 */ 1529 public boolean isTuned() { 1530 return (mInfoFlags & FLAG_TUNED) != 0; 1531 } 1532 1533 /** {@code true} if the received program is stereo 1534 * @return {@code true} if stereo, {@code false} otherwise. 1535 */ 1536 public boolean isStereo() { 1537 return (mInfoFlags & FLAG_STEREO) != 0; 1538 } 1539 1540 /** {@code true} if the received program is digital (e.g HD radio) 1541 * @return {@code true} if digital, {@code false} otherwise. 1542 * @deprecated Use {@link getLogicallyTunedTo()} instead. 1543 */ 1544 @Deprecated 1545 public boolean isDigital() { 1546 ProgramSelector.Identifier id = mLogicallyTunedTo; 1547 if (id == null) id = mSelector.getPrimaryId(); 1548 1549 int type = id.getType(); 1550 return (type != ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY 1551 && type != ProgramSelector.IDENTIFIER_TYPE_RDS_PI); 1552 } 1553 1554 /** 1555 * {@code true} if the program is currently playing live stream. 1556 * This may result in a slightly altered reception parameters, 1557 * usually targetted at reduced latency. 1558 */ 1559 public boolean isLive() { 1560 return (mInfoFlags & FLAG_LIVE) != 0; 1561 } 1562 1563 /** 1564 * {@code true} if radio stream is not playing, ie. due to bad reception 1565 * conditions or buffering. In this state volume knob MAY be disabled to 1566 * prevent user increasing volume too much. 1567 * It does NOT mean the user has muted audio. 1568 */ 1569 public boolean isMuted() { 1570 return (mInfoFlags & FLAG_MUTED) != 0; 1571 } 1572 1573 /** 1574 * {@code true} if radio station transmits traffic information 1575 * regularily. 1576 */ 1577 public boolean isTrafficProgram() { 1578 return (mInfoFlags & FLAG_TRAFFIC_PROGRAM) != 0; 1579 } 1580 1581 /** 1582 * {@code true} if radio station transmits traffic information 1583 * at the very moment. 1584 */ 1585 public boolean isTrafficAnnouncementActive() { 1586 return (mInfoFlags & FLAG_TRAFFIC_ANNOUNCEMENT) != 0; 1587 } 1588 1589 /** 1590 * Signal quality (as opposed to the name) indication from 0 (no signal) 1591 * to 100 (excellent) 1592 * @return the signal quality indication. 1593 */ 1594 public int getSignalStrength() { 1595 return mSignalQuality; 1596 } 1597 1598 /** Metadata currently received from this station. 1599 * null if no metadata have been received 1600 * @return current meta data received from this program. 1601 */ 1602 public RadioMetadata getMetadata() { 1603 return mMetadata; 1604 } 1605 1606 /** 1607 * A map of vendor-specific opaque strings, passed from HAL without changes. 1608 * Format of these strings can vary across vendors. 1609 * 1610 * It may be used for extra features, that's not supported by a platform, 1611 * for example: paid-service=true; bitrate=320kbps. 1612 * 1613 * Keys must be prefixed with unique vendor Java-style namespace, 1614 * eg. 'com.somecompany.parameter1'. 1615 */ 1616 public @NonNull Map<String, String> getVendorInfo() { 1617 return mVendorInfo; 1618 } 1619 1620 private ProgramInfo(Parcel in) { 1621 mSelector = Objects.requireNonNull(in.readTypedObject(ProgramSelector.CREATOR)); 1622 mLogicallyTunedTo = in.readTypedObject(ProgramSelector.Identifier.CREATOR); 1623 mPhysicallyTunedTo = in.readTypedObject(ProgramSelector.Identifier.CREATOR); 1624 mRelatedContent = in.createTypedArrayList(ProgramSelector.Identifier.CREATOR); 1625 mInfoFlags = in.readInt(); 1626 mSignalQuality = in.readInt(); 1627 mMetadata = in.readTypedObject(RadioMetadata.CREATOR); 1628 mVendorInfo = Utils.readStringMap(in); 1629 } 1630 1631 public static final Parcelable.Creator<ProgramInfo> CREATOR 1632 = new Parcelable.Creator<ProgramInfo>() { 1633 public ProgramInfo createFromParcel(Parcel in) { 1634 return new ProgramInfo(in); 1635 } 1636 1637 public ProgramInfo[] newArray(int size) { 1638 return new ProgramInfo[size]; 1639 } 1640 }; 1641 1642 @Override 1643 public void writeToParcel(Parcel dest, int flags) { 1644 dest.writeTypedObject(mSelector, flags); 1645 dest.writeTypedObject(mLogicallyTunedTo, flags); 1646 dest.writeTypedObject(mPhysicallyTunedTo, flags); 1647 Utils.writeTypedCollection(dest, mRelatedContent); 1648 dest.writeInt(mInfoFlags); 1649 dest.writeInt(mSignalQuality); 1650 dest.writeTypedObject(mMetadata, flags); 1651 Utils.writeStringMap(dest, mVendorInfo); 1652 } 1653 1654 @Override 1655 public int describeContents() { 1656 return 0; 1657 } 1658 1659 @Override 1660 public String toString() { 1661 return "ProgramInfo" 1662 + " [selector=" + mSelector 1663 + ", logicallyTunedTo=" + Objects.toString(mLogicallyTunedTo) 1664 + ", physicallyTunedTo=" + Objects.toString(mPhysicallyTunedTo) 1665 + ", relatedContent=" + mRelatedContent.size() 1666 + ", infoFlags=" + mInfoFlags 1667 + ", mSignalQuality=" + mSignalQuality 1668 + ", mMetadata=" + Objects.toString(mMetadata) 1669 + "]"; 1670 } 1671 1672 @Override 1673 public int hashCode() { 1674 return Objects.hash(mSelector, mLogicallyTunedTo, mPhysicallyTunedTo, 1675 mRelatedContent, mInfoFlags, mSignalQuality, mMetadata, mVendorInfo); 1676 } 1677 1678 @Override 1679 public boolean equals(Object obj) { 1680 if (this == obj) return true; 1681 if (!(obj instanceof ProgramInfo)) return false; 1682 ProgramInfo other = (ProgramInfo) obj; 1683 1684 if (!Objects.equals(mSelector, other.mSelector)) return false; 1685 if (!Objects.equals(mLogicallyTunedTo, other.mLogicallyTunedTo)) return false; 1686 if (!Objects.equals(mPhysicallyTunedTo, other.mPhysicallyTunedTo)) return false; 1687 if (!Objects.equals(mRelatedContent, other.mRelatedContent)) return false; 1688 if (mInfoFlags != other.mInfoFlags) return false; 1689 if (mSignalQuality != other.mSignalQuality) return false; 1690 if (!Objects.equals(mMetadata, other.mMetadata)) return false; 1691 if (!Objects.equals(mVendorInfo, other.mVendorInfo)) return false; 1692 1693 return true; 1694 } 1695 } 1696 1697 1698 /** 1699 * Returns a list of descriptors for all broadcast radio modules present on the device. 1700 * @param modules An List of {@link ModuleProperties} where the list will be returned. 1701 * @return 1702 * <ul> 1703 * <li>{@link #STATUS_OK} in case of success, </li> 1704 * <li>{@link #STATUS_ERROR} in case of unspecified error, </li> 1705 * <li>{@link #STATUS_NO_INIT} if the native service cannot be reached, </li> 1706 * <li>{@link #STATUS_BAD_VALUE} if modules is null, </li> 1707 * <li>{@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails, </li> 1708 * </ul> 1709 */ 1710 @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) 1711 public int listModules(List<ModuleProperties> modules) { 1712 if (modules == null) { 1713 Log.e(TAG, "the output list must not be empty"); 1714 return STATUS_BAD_VALUE; 1715 } 1716 1717 Log.d(TAG, "Listing available tuners..."); 1718 List<ModuleProperties> returnedList; 1719 try { 1720 returnedList = mService.listModules(); 1721 } catch (RemoteException e) { 1722 Log.e(TAG, "Failed listing available tuners", e); 1723 return STATUS_DEAD_OBJECT; 1724 } 1725 1726 if (returnedList == null) { 1727 Log.e(TAG, "Returned list was a null"); 1728 return STATUS_ERROR; 1729 } 1730 1731 modules.addAll(returnedList); 1732 return STATUS_OK; 1733 } 1734 1735 private native int nativeListModules(List<ModuleProperties> modules); 1736 1737 /** 1738 * Open an interface to control a tuner on a given broadcast radio module. 1739 * Optionally selects and applies the configuration passed as "config" argument. 1740 * @param moduleId radio module identifier {@link ModuleProperties#getId()}. Mandatory. 1741 * @param config desired band and configuration to apply when enabling the hardware module. 1742 * optional, can be null. 1743 * @param withAudio {@code true} to request a tuner with an audio source. 1744 * This tuner is intended for live listening or recording or a radio program. 1745 * If {@code false}, the tuner can only be used to retrieve program informations. 1746 * @param callback {@link RadioTuner.Callback} interface. Mandatory. 1747 * @param handler the Handler on which the callbacks will be received. 1748 * Can be null if default handler is OK. 1749 * @return a valid {@link RadioTuner} interface in case of success or null in case of error. 1750 */ 1751 @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) 1752 public RadioTuner openTuner(int moduleId, BandConfig config, boolean withAudio, 1753 RadioTuner.Callback callback, Handler handler) { 1754 if (callback == null) { 1755 throw new IllegalArgumentException("callback must not be empty"); 1756 } 1757 1758 Log.d(TAG, "Opening tuner " + moduleId + "..."); 1759 1760 ITuner tuner; 1761 TunerCallbackAdapter halCallback = new TunerCallbackAdapter(callback, handler); 1762 try { 1763 tuner = mService.openTuner(moduleId, config, withAudio, halCallback); 1764 } catch (RemoteException | IllegalArgumentException ex) { 1765 Log.e(TAG, "Failed to open tuner", ex); 1766 return null; 1767 } 1768 if (tuner == null) { 1769 Log.e(TAG, "Failed to open tuner"); 1770 return null; 1771 } 1772 return new TunerAdapter(tuner, halCallback, 1773 config != null ? config.getType() : BAND_INVALID); 1774 } 1775 1776 private final Map<Announcement.OnListUpdatedListener, ICloseHandle> mAnnouncementListeners = 1777 new HashMap<>(); 1778 1779 /** 1780 * Adds new announcement listener. 1781 * 1782 * @param enabledAnnouncementTypes a set of announcement types to listen to 1783 * @param listener announcement listener 1784 */ 1785 @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) 1786 public void addAnnouncementListener(@NonNull Set<Integer> enabledAnnouncementTypes, 1787 @NonNull Announcement.OnListUpdatedListener listener) { 1788 addAnnouncementListener(cmd -> cmd.run(), enabledAnnouncementTypes, listener); 1789 } 1790 1791 /** 1792 * Adds new announcement listener with executor. 1793 * 1794 * @param executor the executor 1795 * @param enabledAnnouncementTypes a set of announcement types to listen to 1796 * @param listener announcement listener 1797 */ 1798 @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) 1799 public void addAnnouncementListener(@NonNull @CallbackExecutor Executor executor, 1800 @NonNull Set<Integer> enabledAnnouncementTypes, 1801 @NonNull Announcement.OnListUpdatedListener listener) { 1802 Objects.requireNonNull(executor); 1803 Objects.requireNonNull(listener); 1804 int[] types = enabledAnnouncementTypes.stream().mapToInt(Integer::intValue).toArray(); 1805 IAnnouncementListener listenerIface = new IAnnouncementListener.Stub() { 1806 public void onListUpdated(List<Announcement> activeAnnouncements) { 1807 executor.execute(() -> listener.onListUpdated(activeAnnouncements)); 1808 } 1809 }; 1810 synchronized (mAnnouncementListeners) { 1811 ICloseHandle closeHandle = null; 1812 try { 1813 closeHandle = mService.addAnnouncementListener(types, listenerIface); 1814 } catch (RemoteException ex) { 1815 ex.rethrowFromSystemServer(); 1816 } 1817 Objects.requireNonNull(closeHandle); 1818 ICloseHandle oldCloseHandle = mAnnouncementListeners.put(listener, closeHandle); 1819 if (oldCloseHandle != null) Utils.close(oldCloseHandle); 1820 } 1821 } 1822 1823 /** 1824 * Removes previously registered announcement listener. 1825 * 1826 * @param listener announcement listener, previously registered with 1827 * {@link addAnnouncementListener} 1828 */ 1829 @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) 1830 public void removeAnnouncementListener(@NonNull Announcement.OnListUpdatedListener listener) { 1831 Objects.requireNonNull(listener); 1832 synchronized (mAnnouncementListeners) { 1833 ICloseHandle closeHandle = mAnnouncementListeners.remove(listener); 1834 if (closeHandle != null) Utils.close(closeHandle); 1835 } 1836 } 1837 1838 @NonNull private final Context mContext; 1839 @NonNull private final IRadioService mService; 1840 1841 /** 1842 * @hide 1843 */ 1844 public RadioManager(@NonNull Context context) throws ServiceNotFoundException { 1845 mContext = context; 1846 mService = IRadioService.Stub.asInterface( 1847 ServiceManager.getServiceOrThrow(Context.RADIO_SERVICE)); 1848 } 1849 } 1850