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 */ ModuleProperties(int id, String serviceName, int classId, String implementor, String product, String version, String serial, int numTuners, int numAudioSources, boolean isInitializationRequired, boolean isCaptureSupported, BandDescriptor[] bands, boolean isBgScanSupported, @ProgramSelector.ProgramType int[] supportedProgramTypes, @ProgramSelector.IdentifierType int[] supportedIdentifierTypes, @Nullable Map<String, Integer> dabFrequencyTable, Map<String, String> vendorInfo)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 arrayToSet(int[] arr)257 private static Set<Integer> arrayToSet(int[] arr) { 258 return Arrays.stream(arr).boxed().collect(Collectors.toSet()); 259 } 260 setToArray(Set<Integer> set)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 */ getId()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 */ getServiceName()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 */ getClassId()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 */ getImplementor()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 */ getProduct()298 public String getProduct() { 299 return mProduct; 300 } 301 302 /** Human readable broadcast radio module version number 303 * @return the radio module version. 304 */ getVersion()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 */ getSerial()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 */ getNumTuners()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 */ getNumAudioSources()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 */ isInitializationRequired()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 */ isCaptureSupported()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 */ isBackgroundScanningSupported()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 */ isProgramTypeSupported(@rogramSelector.ProgramType int type)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 */ isProgramIdentifierSupported(@rogramSelector.IdentifierType int type)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 */ getDabFrequencyTable()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 */ getVendorInfo()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 */ getBands()422 public BandDescriptor[] getBands() { 423 return mBands; 424 } 425 ModuleProperties(Parcel in)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 @android.annotation.NonNull 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 writeToParcel(Parcel dest, int flags)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 describeContents()484 public int describeContents() { 485 return 0; 486 } 487 488 @NonNull 489 @Override toString()490 public String toString() { 491 return "ModuleProperties [mId=" + mId 492 + ", mServiceName=" + mServiceName + ", mClassId=" + mClassId 493 + ", mImplementor=" + mImplementor + ", mProduct=" + mProduct 494 + ", mVersion=" + mVersion + ", mSerial=" + mSerial 495 + ", mNumTuners=" + mNumTuners 496 + ", mNumAudioSources=" + mNumAudioSources 497 + ", mIsInitializationRequired=" + mIsInitializationRequired 498 + ", mIsCaptureSupported=" + mIsCaptureSupported 499 + ", mIsBgScanSupported=" + mIsBgScanSupported 500 + ", mBands=" + Arrays.toString(mBands) + "]"; 501 } 502 503 @Override hashCode()504 public int hashCode() { 505 return Objects.hash(mId, mServiceName, mClassId, mImplementor, mProduct, mVersion, 506 mSerial, mNumTuners, mNumAudioSources, mIsInitializationRequired, 507 mIsCaptureSupported, mBands, mIsBgScanSupported, mDabFrequencyTable, mVendorInfo); 508 } 509 510 @Override equals(@ullable Object obj)511 public boolean equals(@Nullable Object obj) { 512 if (this == obj) return true; 513 if (!(obj instanceof ModuleProperties)) return false; 514 ModuleProperties other = (ModuleProperties) obj; 515 516 if (mId != other.getId()) return false; 517 if (!TextUtils.equals(mServiceName, other.mServiceName)) return false; 518 if (mClassId != other.mClassId) return false; 519 if (!Objects.equals(mImplementor, other.mImplementor)) return false; 520 if (!Objects.equals(mProduct, other.mProduct)) return false; 521 if (!Objects.equals(mVersion, other.mVersion)) return false; 522 if (!Objects.equals(mSerial, other.mSerial)) return false; 523 if (mNumTuners != other.mNumTuners) return false; 524 if (mNumAudioSources != other.mNumAudioSources) return false; 525 if (mIsInitializationRequired != other.mIsInitializationRequired) return false; 526 if (mIsCaptureSupported != other.mIsCaptureSupported) return false; 527 if (!Objects.equals(mBands, other.mBands)) return false; 528 if (mIsBgScanSupported != other.mIsBgScanSupported) return false; 529 if (!Objects.equals(mDabFrequencyTable, other.mDabFrequencyTable)) return false; 530 if (!Objects.equals(mVendorInfo, other.mVendorInfo)) return false; 531 return true; 532 } 533 } 534 535 /** Radio band descriptor: an element in ModuleProperties bands array. 536 * It is either an instance of {@link FmBandDescriptor} or {@link AmBandDescriptor} */ 537 public static class BandDescriptor implements Parcelable { 538 539 private final int mRegion; 540 private final int mType; 541 private final int mLowerLimit; 542 private final int mUpperLimit; 543 private final int mSpacing; 544 BandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing)545 BandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing) { 546 if (type != BAND_AM && type != BAND_FM && type != BAND_FM_HD && type != BAND_AM_HD) { 547 throw new IllegalArgumentException("Unsupported band: " + type); 548 } 549 mRegion = region; 550 mType = type; 551 mLowerLimit = lowerLimit; 552 mUpperLimit = upperLimit; 553 mSpacing = spacing; 554 } 555 556 /** Region this band applies to. E.g. {@link #REGION_ITU_1} 557 * @return the region this band is associated to. 558 */ getRegion()559 public int getRegion() { 560 return mRegion; 561 } 562 /** Band type, e.g {@link #BAND_FM}. Defines the subclass this descriptor can be cast to: 563 * <ul> 564 * <li>{@link #BAND_FM} or {@link #BAND_FM_HD} cast to {@link FmBandDescriptor}, </li> 565 * <li>{@link #BAND_AM} cast to {@link AmBandDescriptor}, </li> 566 * </ul> 567 * @return the band type. 568 */ getType()569 public int getType() { 570 return mType; 571 } 572 573 /** 574 * Checks if the band is either AM or AM_HD. 575 * 576 * @return {@code true}, if band is AM or AM_HD. 577 */ isAmBand()578 public boolean isAmBand() { 579 return mType == BAND_AM || mType == BAND_AM_HD; 580 } 581 582 /** 583 * Checks if the band is either FM or FM_HD. 584 * 585 * @return {@code true}, if band is FM or FM_HD. 586 */ isFmBand()587 public boolean isFmBand() { 588 return mType == BAND_FM || mType == BAND_FM_HD; 589 } 590 591 /** Lower band limit expressed in units according to band type. 592 * Currently all defined band types express channels as frequency in kHz 593 * @return the lower band limit. 594 */ getLowerLimit()595 public int getLowerLimit() { 596 return mLowerLimit; 597 } 598 /** Upper band limit expressed in units according to band type. 599 * Currently all defined band types express channels as frequency in kHz 600 * @return the upper band limit. 601 */ getUpperLimit()602 public int getUpperLimit() { 603 return mUpperLimit; 604 } 605 /** Channel spacing in units according to band type. 606 * Currently all defined band types express channels as frequency in kHz 607 * @return the channel spacing. 608 */ getSpacing()609 public int getSpacing() { 610 return mSpacing; 611 } 612 BandDescriptor(Parcel in)613 private BandDescriptor(Parcel in) { 614 mRegion = in.readInt(); 615 mType = in.readInt(); 616 mLowerLimit = in.readInt(); 617 mUpperLimit = in.readInt(); 618 mSpacing = in.readInt(); 619 } 620 lookupTypeFromParcel(Parcel in)621 private static int lookupTypeFromParcel(Parcel in) { 622 int pos = in.dataPosition(); 623 in.readInt(); // skip region 624 int type = in.readInt(); 625 in.setDataPosition(pos); 626 return type; 627 } 628 629 public static final @android.annotation.NonNull Parcelable.Creator<BandDescriptor> CREATOR 630 = new Parcelable.Creator<BandDescriptor>() { 631 public BandDescriptor createFromParcel(Parcel in) { 632 int type = lookupTypeFromParcel(in); 633 switch (type) { 634 case BAND_FM: 635 case BAND_FM_HD: 636 return new FmBandDescriptor(in); 637 case BAND_AM: 638 case BAND_AM_HD: 639 return new AmBandDescriptor(in); 640 default: 641 throw new IllegalArgumentException("Unsupported band: " + type); 642 } 643 } 644 645 public BandDescriptor[] newArray(int size) { 646 return new BandDescriptor[size]; 647 } 648 }; 649 650 @Override writeToParcel(Parcel dest, int flags)651 public void writeToParcel(Parcel dest, int flags) { 652 dest.writeInt(mRegion); 653 dest.writeInt(mType); 654 dest.writeInt(mLowerLimit); 655 dest.writeInt(mUpperLimit); 656 dest.writeInt(mSpacing); 657 } 658 659 @Override describeContents()660 public int describeContents() { 661 return 0; 662 } 663 664 @NonNull 665 @Override toString()666 public String toString() { 667 return "BandDescriptor [mRegion=" + mRegion + ", mType=" + mType + ", mLowerLimit=" 668 + mLowerLimit + ", mUpperLimit=" + mUpperLimit + ", mSpacing=" + mSpacing + "]"; 669 } 670 671 @Override hashCode()672 public int hashCode() { 673 final int prime = 31; 674 int result = 1; 675 result = prime * result + mRegion; 676 result = prime * result + mType; 677 result = prime * result + mLowerLimit; 678 result = prime * result + mUpperLimit; 679 result = prime * result + mSpacing; 680 return result; 681 } 682 683 @Override equals(@ullable Object obj)684 public boolean equals(@Nullable Object obj) { 685 if (this == obj) 686 return true; 687 if (!(obj instanceof BandDescriptor)) 688 return false; 689 BandDescriptor other = (BandDescriptor) obj; 690 if (mRegion != other.getRegion()) 691 return false; 692 if (mType != other.getType()) 693 return false; 694 if (mLowerLimit != other.getLowerLimit()) 695 return false; 696 if (mUpperLimit != other.getUpperLimit()) 697 return false; 698 if (mSpacing != other.getSpacing()) 699 return false; 700 return true; 701 } 702 } 703 704 /** FM band descriptor 705 * @see #BAND_FM 706 * @see #BAND_FM_HD */ 707 public static class FmBandDescriptor extends BandDescriptor { 708 private final boolean mStereo; 709 private final boolean mRds; 710 private final boolean mTa; 711 private final boolean mAf; 712 private final boolean mEa; 713 714 /** @hide */ FmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing, boolean stereo, boolean rds, boolean ta, boolean af, boolean ea)715 public FmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing, 716 boolean stereo, boolean rds, boolean ta, boolean af, boolean ea) { 717 super(region, type, lowerLimit, upperLimit, spacing); 718 mStereo = stereo; 719 mRds = rds; 720 mTa = ta; 721 mAf = af; 722 mEa = ea; 723 } 724 725 /** Stereo is supported 726 * @return {@code true} if stereo is supported, {@code false} otherwise. 727 */ isStereoSupported()728 public boolean isStereoSupported() { 729 return mStereo; 730 } 731 /** RDS or RBDS(if region is ITU2) is supported 732 * @return {@code true} if RDS or RBDS is supported, {@code false} otherwise. 733 */ isRdsSupported()734 public boolean isRdsSupported() { 735 return mRds; 736 } 737 /** Traffic announcement is supported 738 * @return {@code true} if TA is supported, {@code false} otherwise. 739 */ isTaSupported()740 public boolean isTaSupported() { 741 return mTa; 742 } 743 /** Alternate Frequency Switching is supported 744 * @return {@code true} if AF switching is supported, {@code false} otherwise. 745 */ isAfSupported()746 public boolean isAfSupported() { 747 return mAf; 748 } 749 750 /** Emergency Announcement is supported 751 * @return {@code true} if Emergency annoucement is supported, {@code false} otherwise. 752 */ isEaSupported()753 public boolean isEaSupported() { 754 return mEa; 755 } 756 757 /* Parcelable implementation */ FmBandDescriptor(Parcel in)758 private FmBandDescriptor(Parcel in) { 759 super(in); 760 mStereo = in.readByte() == 1; 761 mRds = in.readByte() == 1; 762 mTa = in.readByte() == 1; 763 mAf = in.readByte() == 1; 764 mEa = in.readByte() == 1; 765 } 766 767 public static final @android.annotation.NonNull Parcelable.Creator<FmBandDescriptor> CREATOR 768 = new Parcelable.Creator<FmBandDescriptor>() { 769 public FmBandDescriptor createFromParcel(Parcel in) { 770 return new FmBandDescriptor(in); 771 } 772 773 public FmBandDescriptor[] newArray(int size) { 774 return new FmBandDescriptor[size]; 775 } 776 }; 777 778 @Override writeToParcel(Parcel dest, int flags)779 public void writeToParcel(Parcel dest, int flags) { 780 super.writeToParcel(dest, flags); 781 dest.writeByte((byte) (mStereo ? 1 : 0)); 782 dest.writeByte((byte) (mRds ? 1 : 0)); 783 dest.writeByte((byte) (mTa ? 1 : 0)); 784 dest.writeByte((byte) (mAf ? 1 : 0)); 785 dest.writeByte((byte) (mEa ? 1 : 0)); 786 } 787 788 @Override describeContents()789 public int describeContents() { 790 return 0; 791 } 792 793 @NonNull 794 @Override toString()795 public String toString() { 796 return "FmBandDescriptor [ "+ super.toString() + " mStereo=" + mStereo 797 + ", mRds=" + mRds + ", mTa=" + mTa + ", mAf=" + mAf + 798 ", mEa =" + mEa + "]"; 799 } 800 801 @Override hashCode()802 public int hashCode() { 803 final int prime = 31; 804 int result = super.hashCode(); 805 result = prime * result + (mStereo ? 1 : 0); 806 result = prime * result + (mRds ? 1 : 0); 807 result = prime * result + (mTa ? 1 : 0); 808 result = prime * result + (mAf ? 1 : 0); 809 result = prime * result + (mEa ? 1 : 0); 810 return result; 811 } 812 813 @Override equals(@ullable Object obj)814 public boolean equals(@Nullable Object obj) { 815 if (this == obj) 816 return true; 817 if (!super.equals(obj)) 818 return false; 819 if (!(obj instanceof FmBandDescriptor)) 820 return false; 821 FmBandDescriptor other = (FmBandDescriptor) obj; 822 if (mStereo != other.isStereoSupported()) 823 return false; 824 if (mRds != other.isRdsSupported()) 825 return false; 826 if (mTa != other.isTaSupported()) 827 return false; 828 if (mAf != other.isAfSupported()) 829 return false; 830 if (mEa != other.isEaSupported()) 831 return false; 832 return true; 833 } 834 } 835 836 /** AM band descriptor. 837 * @see #BAND_AM */ 838 public static class AmBandDescriptor extends BandDescriptor { 839 840 private final boolean mStereo; 841 842 /** @hide */ AmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing, boolean stereo)843 public AmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing, 844 boolean stereo) { 845 super(region, type, lowerLimit, upperLimit, spacing); 846 mStereo = stereo; 847 } 848 849 /** Stereo is supported 850 * @return {@code true} if stereo is supported, {@code false} otherwise. 851 */ isStereoSupported()852 public boolean isStereoSupported() { 853 return mStereo; 854 } 855 AmBandDescriptor(Parcel in)856 private AmBandDescriptor(Parcel in) { 857 super(in); 858 mStereo = in.readByte() == 1; 859 } 860 861 public static final @android.annotation.NonNull Parcelable.Creator<AmBandDescriptor> CREATOR 862 = new Parcelable.Creator<AmBandDescriptor>() { 863 public AmBandDescriptor createFromParcel(Parcel in) { 864 return new AmBandDescriptor(in); 865 } 866 867 public AmBandDescriptor[] newArray(int size) { 868 return new AmBandDescriptor[size]; 869 } 870 }; 871 872 @Override writeToParcel(Parcel dest, int flags)873 public void writeToParcel(Parcel dest, int flags) { 874 super.writeToParcel(dest, flags); 875 dest.writeByte((byte) (mStereo ? 1 : 0)); 876 } 877 878 @Override describeContents()879 public int describeContents() { 880 return 0; 881 } 882 883 @NonNull 884 @Override toString()885 public String toString() { 886 return "AmBandDescriptor [ "+ super.toString() + " mStereo=" + mStereo + "]"; 887 } 888 889 @Override hashCode()890 public int hashCode() { 891 final int prime = 31; 892 int result = super.hashCode(); 893 result = prime * result + (mStereo ? 1 : 0); 894 return result; 895 } 896 897 @Override equals(@ullable Object obj)898 public boolean equals(@Nullable Object obj) { 899 if (this == obj) 900 return true; 901 if (!super.equals(obj)) 902 return false; 903 if (!(obj instanceof AmBandDescriptor)) 904 return false; 905 AmBandDescriptor other = (AmBandDescriptor) obj; 906 if (mStereo != other.isStereoSupported()) 907 return false; 908 return true; 909 } 910 } 911 912 913 /** Radio band configuration. */ 914 public static class BandConfig implements Parcelable { 915 916 @NonNull final BandDescriptor mDescriptor; 917 BandConfig(BandDescriptor descriptor)918 BandConfig(BandDescriptor descriptor) { 919 mDescriptor = Objects.requireNonNull(descriptor); 920 } 921 BandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing)922 BandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing) { 923 mDescriptor = new BandDescriptor(region, type, lowerLimit, upperLimit, spacing); 924 } 925 BandConfig(Parcel in)926 private BandConfig(Parcel in) { 927 mDescriptor = new BandDescriptor(in); 928 } 929 getDescriptor()930 BandDescriptor getDescriptor() { 931 return mDescriptor; 932 } 933 934 /** Region this band applies to. E.g. {@link #REGION_ITU_1} 935 * @return the region associated with this band. 936 */ getRegion()937 public int getRegion() { 938 return mDescriptor.getRegion(); 939 } 940 /** Band type, e.g {@link #BAND_FM}. Defines the subclass this descriptor can be cast to: 941 * <ul> 942 * <li>{@link #BAND_FM} or {@link #BAND_FM_HD} cast to {@link FmBandDescriptor}, </li> 943 * <li>{@link #BAND_AM} cast to {@link AmBandDescriptor}, </li> 944 * </ul> 945 * @return the band type. 946 */ getType()947 public int getType() { 948 return mDescriptor.getType(); 949 } 950 /** Lower band limit expressed in units according to band type. 951 * Currently all defined band types express channels as frequency in kHz 952 * @return the lower band limit. 953 */ getLowerLimit()954 public int getLowerLimit() { 955 return mDescriptor.getLowerLimit(); 956 } 957 /** Upper band limit expressed in units according to band type. 958 * Currently all defined band types express channels as frequency in kHz 959 * @return the upper band limit. 960 */ getUpperLimit()961 public int getUpperLimit() { 962 return mDescriptor.getUpperLimit(); 963 } 964 /** Channel spacing in units according to band type. 965 * Currently all defined band types express channels as frequency in kHz 966 * @return the channel spacing. 967 */ getSpacing()968 public int getSpacing() { 969 return mDescriptor.getSpacing(); 970 } 971 972 973 public static final @android.annotation.NonNull Parcelable.Creator<BandConfig> CREATOR 974 = new Parcelable.Creator<BandConfig>() { 975 public BandConfig createFromParcel(Parcel in) { 976 int type = BandDescriptor.lookupTypeFromParcel(in); 977 switch (type) { 978 case BAND_FM: 979 case BAND_FM_HD: 980 return new FmBandConfig(in); 981 case BAND_AM: 982 case BAND_AM_HD: 983 return new AmBandConfig(in); 984 default: 985 throw new IllegalArgumentException("Unsupported band: " + type); 986 } 987 } 988 989 public BandConfig[] newArray(int size) { 990 return new BandConfig[size]; 991 } 992 }; 993 994 @Override writeToParcel(Parcel dest, int flags)995 public void writeToParcel(Parcel dest, int flags) { 996 mDescriptor.writeToParcel(dest, flags); 997 } 998 999 @Override describeContents()1000 public int describeContents() { 1001 return 0; 1002 } 1003 1004 @NonNull 1005 @Override toString()1006 public String toString() { 1007 return "BandConfig [ " + mDescriptor.toString() + "]"; 1008 } 1009 1010 @Override hashCode()1011 public int hashCode() { 1012 final int prime = 31; 1013 int result = 1; 1014 result = prime * result + mDescriptor.hashCode(); 1015 return result; 1016 } 1017 1018 @Override equals(@ullable Object obj)1019 public boolean equals(@Nullable Object obj) { 1020 if (this == obj) 1021 return true; 1022 if (!(obj instanceof BandConfig)) 1023 return false; 1024 BandConfig other = (BandConfig) obj; 1025 BandDescriptor otherDesc = other.getDescriptor(); 1026 if ((mDescriptor == null) != (otherDesc == null)) return false; 1027 if (mDescriptor != null && !mDescriptor.equals(otherDesc)) return false; 1028 return true; 1029 } 1030 } 1031 1032 /** FM band configuration. 1033 * @see #BAND_FM 1034 * @see #BAND_FM_HD */ 1035 public static class FmBandConfig extends BandConfig { 1036 private final boolean mStereo; 1037 private final boolean mRds; 1038 private final boolean mTa; 1039 private final boolean mAf; 1040 private final boolean mEa; 1041 1042 /** @hide */ FmBandConfig(FmBandDescriptor descriptor)1043 public FmBandConfig(FmBandDescriptor descriptor) { 1044 super((BandDescriptor)descriptor); 1045 mStereo = descriptor.isStereoSupported(); 1046 mRds = descriptor.isRdsSupported(); 1047 mTa = descriptor.isTaSupported(); 1048 mAf = descriptor.isAfSupported(); 1049 mEa = descriptor.isEaSupported(); 1050 } 1051 FmBandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing, boolean stereo, boolean rds, boolean ta, boolean af, boolean ea)1052 FmBandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing, 1053 boolean stereo, boolean rds, boolean ta, boolean af, boolean ea) { 1054 super(region, type, lowerLimit, upperLimit, spacing); 1055 mStereo = stereo; 1056 mRds = rds; 1057 mTa = ta; 1058 mAf = af; 1059 mEa = ea; 1060 } 1061 1062 /** Get stereo enable state 1063 * @return the enable state. 1064 */ getStereo()1065 public boolean getStereo() { 1066 return mStereo; 1067 } 1068 1069 /** Get RDS or RBDS(if region is ITU2) enable state 1070 * @return the enable state. 1071 */ getRds()1072 public boolean getRds() { 1073 return mRds; 1074 } 1075 1076 /** Get Traffic announcement enable state 1077 * @return the enable state. 1078 */ getTa()1079 public boolean getTa() { 1080 return mTa; 1081 } 1082 1083 /** Get Alternate Frequency Switching enable state 1084 * @return the enable state. 1085 */ getAf()1086 public boolean getAf() { 1087 return mAf; 1088 } 1089 1090 /** 1091 * Get Emergency announcement enable state 1092 * @return the enable state. 1093 */ getEa()1094 public boolean getEa() { 1095 return mEa; 1096 } 1097 FmBandConfig(Parcel in)1098 private FmBandConfig(Parcel in) { 1099 super(in); 1100 mStereo = in.readByte() == 1; 1101 mRds = in.readByte() == 1; 1102 mTa = in.readByte() == 1; 1103 mAf = in.readByte() == 1; 1104 mEa = in.readByte() == 1; 1105 } 1106 1107 public static final @android.annotation.NonNull Parcelable.Creator<FmBandConfig> CREATOR 1108 = new Parcelable.Creator<FmBandConfig>() { 1109 public FmBandConfig createFromParcel(Parcel in) { 1110 return new FmBandConfig(in); 1111 } 1112 1113 public FmBandConfig[] newArray(int size) { 1114 return new FmBandConfig[size]; 1115 } 1116 }; 1117 1118 @Override writeToParcel(Parcel dest, int flags)1119 public void writeToParcel(Parcel dest, int flags) { 1120 super.writeToParcel(dest, flags); 1121 dest.writeByte((byte) (mStereo ? 1 : 0)); 1122 dest.writeByte((byte) (mRds ? 1 : 0)); 1123 dest.writeByte((byte) (mTa ? 1 : 0)); 1124 dest.writeByte((byte) (mAf ? 1 : 0)); 1125 dest.writeByte((byte) (mEa ? 1 : 0)); 1126 } 1127 1128 @Override describeContents()1129 public int describeContents() { 1130 return 0; 1131 } 1132 1133 @NonNull 1134 @Override toString()1135 public String toString() { 1136 return "FmBandConfig [" + super.toString() 1137 + ", mStereo=" + mStereo + ", mRds=" + mRds + ", mTa=" + mTa 1138 + ", mAf=" + mAf + ", mEa =" + mEa + "]"; 1139 } 1140 1141 @Override hashCode()1142 public int hashCode() { 1143 final int prime = 31; 1144 int result = super.hashCode(); 1145 result = prime * result + (mStereo ? 1 : 0); 1146 result = prime * result + (mRds ? 1 : 0); 1147 result = prime * result + (mTa ? 1 : 0); 1148 result = prime * result + (mAf ? 1 : 0); 1149 result = prime * result + (mEa ? 1 : 0); 1150 return result; 1151 } 1152 1153 @Override equals(@ullable Object obj)1154 public boolean equals(@Nullable Object obj) { 1155 if (this == obj) 1156 return true; 1157 if (!super.equals(obj)) 1158 return false; 1159 if (!(obj instanceof FmBandConfig)) 1160 return false; 1161 FmBandConfig other = (FmBandConfig) obj; 1162 if (mStereo != other.mStereo) 1163 return false; 1164 if (mRds != other.mRds) 1165 return false; 1166 if (mTa != other.mTa) 1167 return false; 1168 if (mAf != other.mAf) 1169 return false; 1170 if (mEa != other.mEa) 1171 return false; 1172 return true; 1173 } 1174 1175 /** 1176 * Builder class for {@link FmBandConfig} objects. 1177 */ 1178 public static class Builder { 1179 private final BandDescriptor mDescriptor; 1180 private boolean mStereo; 1181 private boolean mRds; 1182 private boolean mTa; 1183 private boolean mAf; 1184 private boolean mEa; 1185 1186 /** 1187 * Constructs a new Builder with the defaults from an {@link FmBandDescriptor} . 1188 * @param descriptor the FmBandDescriptor defaults are read from . 1189 */ Builder(FmBandDescriptor descriptor)1190 public Builder(FmBandDescriptor descriptor) { 1191 mDescriptor = new BandDescriptor(descriptor.getRegion(), descriptor.getType(), 1192 descriptor.getLowerLimit(), descriptor.getUpperLimit(), 1193 descriptor.getSpacing()); 1194 mStereo = descriptor.isStereoSupported(); 1195 mRds = descriptor.isRdsSupported(); 1196 mTa = descriptor.isTaSupported(); 1197 mAf = descriptor.isAfSupported(); 1198 mEa = descriptor.isEaSupported(); 1199 } 1200 1201 /** 1202 * Constructs a new Builder from a given {@link FmBandConfig} 1203 * @param config the FmBandConfig object whose data will be reused in the new Builder. 1204 */ Builder(FmBandConfig config)1205 public Builder(FmBandConfig config) { 1206 mDescriptor = new BandDescriptor(config.getRegion(), config.getType(), 1207 config.getLowerLimit(), config.getUpperLimit(), config.getSpacing()); 1208 mStereo = config.getStereo(); 1209 mRds = config.getRds(); 1210 mTa = config.getTa(); 1211 mAf = config.getAf(); 1212 mEa = config.getEa(); 1213 } 1214 1215 /** 1216 * Combines all of the parameters that have been set and return a new 1217 * {@link FmBandConfig} object. 1218 * @return a new {@link FmBandConfig} object 1219 */ build()1220 public FmBandConfig build() { 1221 FmBandConfig config = new FmBandConfig(mDescriptor.getRegion(), 1222 mDescriptor.getType(), mDescriptor.getLowerLimit(), 1223 mDescriptor.getUpperLimit(), mDescriptor.getSpacing(), 1224 mStereo, mRds, mTa, mAf, mEa); 1225 return config; 1226 } 1227 1228 /** Set stereo enable state 1229 * @param state The new enable state. 1230 * @return the same Builder instance. 1231 */ setStereo(boolean state)1232 public Builder setStereo(boolean state) { 1233 mStereo = state; 1234 return this; 1235 } 1236 1237 /** Set RDS or RBDS(if region is ITU2) enable state 1238 * @param state The new enable state. 1239 * @return the same Builder instance. 1240 */ setRds(boolean state)1241 public Builder setRds(boolean state) { 1242 mRds = state; 1243 return this; 1244 } 1245 1246 /** Set Traffic announcement enable state 1247 * @param state The new enable state. 1248 * @return the same Builder instance. 1249 */ setTa(boolean state)1250 public Builder setTa(boolean state) { 1251 mTa = state; 1252 return this; 1253 } 1254 1255 /** Set Alternate Frequency Switching enable state 1256 * @param state The new enable state. 1257 * @return the same Builder instance. 1258 */ setAf(boolean state)1259 public Builder setAf(boolean state) { 1260 mAf = state; 1261 return this; 1262 } 1263 1264 /** Set Emergency Announcement enable state 1265 * @param state The new enable state. 1266 * @return the same Builder instance. 1267 */ setEa(boolean state)1268 public Builder setEa(boolean state) { 1269 mEa = state; 1270 return this; 1271 } 1272 }; 1273 } 1274 1275 /** AM band configuration. 1276 * @see #BAND_AM */ 1277 public static class AmBandConfig extends BandConfig { 1278 private final boolean mStereo; 1279 1280 /** @hide */ AmBandConfig(AmBandDescriptor descriptor)1281 public AmBandConfig(AmBandDescriptor descriptor) { 1282 super((BandDescriptor)descriptor); 1283 mStereo = descriptor.isStereoSupported(); 1284 } 1285 AmBandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing, boolean stereo)1286 AmBandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing, 1287 boolean stereo) { 1288 super(region, type, lowerLimit, upperLimit, spacing); 1289 mStereo = stereo; 1290 } 1291 1292 /** Get stereo enable state 1293 * @return the enable state. 1294 */ getStereo()1295 public boolean getStereo() { 1296 return mStereo; 1297 } 1298 AmBandConfig(Parcel in)1299 private AmBandConfig(Parcel in) { 1300 super(in); 1301 mStereo = in.readByte() == 1; 1302 } 1303 1304 public static final @android.annotation.NonNull Parcelable.Creator<AmBandConfig> CREATOR 1305 = new Parcelable.Creator<AmBandConfig>() { 1306 public AmBandConfig createFromParcel(Parcel in) { 1307 return new AmBandConfig(in); 1308 } 1309 1310 public AmBandConfig[] newArray(int size) { 1311 return new AmBandConfig[size]; 1312 } 1313 }; 1314 1315 @Override writeToParcel(Parcel dest, int flags)1316 public void writeToParcel(Parcel dest, int flags) { 1317 super.writeToParcel(dest, flags); 1318 dest.writeByte((byte) (mStereo ? 1 : 0)); 1319 } 1320 1321 @Override describeContents()1322 public int describeContents() { 1323 return 0; 1324 } 1325 1326 @NonNull 1327 @Override toString()1328 public String toString() { 1329 return "AmBandConfig [" + super.toString() 1330 + ", mStereo=" + mStereo + "]"; 1331 } 1332 1333 @Override hashCode()1334 public int hashCode() { 1335 final int prime = 31; 1336 int result = super.hashCode(); 1337 result = prime * result + (mStereo ? 1 : 0); 1338 return result; 1339 } 1340 1341 @Override equals(@ullable Object obj)1342 public boolean equals(@Nullable Object obj) { 1343 if (this == obj) 1344 return true; 1345 if (!super.equals(obj)) 1346 return false; 1347 if (!(obj instanceof AmBandConfig)) 1348 return false; 1349 AmBandConfig other = (AmBandConfig) obj; 1350 if (mStereo != other.getStereo()) 1351 return false; 1352 return true; 1353 } 1354 1355 /** 1356 * Builder class for {@link AmBandConfig} objects. 1357 */ 1358 public static class Builder { 1359 private final BandDescriptor mDescriptor; 1360 private boolean mStereo; 1361 1362 /** 1363 * Constructs a new Builder with the defaults from an {@link AmBandDescriptor} . 1364 * @param descriptor the FmBandDescriptor defaults are read from . 1365 */ Builder(AmBandDescriptor descriptor)1366 public Builder(AmBandDescriptor descriptor) { 1367 mDescriptor = new BandDescriptor(descriptor.getRegion(), descriptor.getType(), 1368 descriptor.getLowerLimit(), descriptor.getUpperLimit(), 1369 descriptor.getSpacing()); 1370 mStereo = descriptor.isStereoSupported(); 1371 } 1372 1373 /** 1374 * Constructs a new Builder from a given {@link AmBandConfig} 1375 * @param config the FmBandConfig object whose data will be reused in the new Builder. 1376 */ Builder(AmBandConfig config)1377 public Builder(AmBandConfig config) { 1378 mDescriptor = new BandDescriptor(config.getRegion(), config.getType(), 1379 config.getLowerLimit(), config.getUpperLimit(), config.getSpacing()); 1380 mStereo = config.getStereo(); 1381 } 1382 1383 /** 1384 * Combines all of the parameters that have been set and return a new 1385 * {@link AmBandConfig} object. 1386 * @return a new {@link AmBandConfig} object 1387 */ build()1388 public AmBandConfig build() { 1389 AmBandConfig config = new AmBandConfig(mDescriptor.getRegion(), 1390 mDescriptor.getType(), mDescriptor.getLowerLimit(), 1391 mDescriptor.getUpperLimit(), mDescriptor.getSpacing(), 1392 mStereo); 1393 return config; 1394 } 1395 1396 /** Set stereo enable state 1397 * @param state The new enable state. 1398 * @return the same Builder instance. 1399 */ setStereo(boolean state)1400 public Builder setStereo(boolean state) { 1401 mStereo = state; 1402 return this; 1403 } 1404 }; 1405 } 1406 1407 /** Radio program information. */ 1408 public static class ProgramInfo implements Parcelable { 1409 1410 // sourced from hardware/interfaces/broadcastradio/2.0/types.hal 1411 private static final int FLAG_LIVE = 1 << 0; 1412 private static final int FLAG_MUTED = 1 << 1; 1413 private static final int FLAG_TRAFFIC_PROGRAM = 1 << 2; 1414 private static final int FLAG_TRAFFIC_ANNOUNCEMENT = 1 << 3; 1415 private static final int FLAG_TUNED = 1 << 4; 1416 private static final int FLAG_STEREO = 1 << 5; 1417 1418 @NonNull private final ProgramSelector mSelector; 1419 @Nullable private final ProgramSelector.Identifier mLogicallyTunedTo; 1420 @Nullable private final ProgramSelector.Identifier mPhysicallyTunedTo; 1421 @NonNull private final Collection<ProgramSelector.Identifier> mRelatedContent; 1422 private final int mInfoFlags; 1423 private final int mSignalQuality; 1424 @Nullable private final RadioMetadata mMetadata; 1425 @NonNull private final Map<String, String> mVendorInfo; 1426 1427 /** @hide */ ProgramInfo(@onNull ProgramSelector selector, @Nullable ProgramSelector.Identifier logicallyTunedTo, @Nullable ProgramSelector.Identifier physicallyTunedTo, @Nullable Collection<ProgramSelector.Identifier> relatedContent, int infoFlags, int signalQuality, @Nullable RadioMetadata metadata, @Nullable Map<String, String> vendorInfo)1428 public ProgramInfo(@NonNull ProgramSelector selector, 1429 @Nullable ProgramSelector.Identifier logicallyTunedTo, 1430 @Nullable ProgramSelector.Identifier physicallyTunedTo, 1431 @Nullable Collection<ProgramSelector.Identifier> relatedContent, 1432 int infoFlags, int signalQuality, @Nullable RadioMetadata metadata, 1433 @Nullable Map<String, String> vendorInfo) { 1434 mSelector = Objects.requireNonNull(selector); 1435 mLogicallyTunedTo = logicallyTunedTo; 1436 mPhysicallyTunedTo = physicallyTunedTo; 1437 if (relatedContent == null) { 1438 mRelatedContent = Collections.emptyList(); 1439 } else { 1440 Preconditions.checkCollectionElementsNotNull(relatedContent, "relatedContent"); 1441 mRelatedContent = relatedContent; 1442 } 1443 mInfoFlags = infoFlags; 1444 mSignalQuality = signalQuality; 1445 mMetadata = metadata; 1446 mVendorInfo = (vendorInfo == null) ? new HashMap<>() : vendorInfo; 1447 } 1448 1449 /** 1450 * Program selector, necessary for tuning to a program. 1451 * 1452 * @return the program selector. 1453 */ getSelector()1454 public @NonNull ProgramSelector getSelector() { 1455 return mSelector; 1456 } 1457 1458 /** 1459 * Identifier currently used for program selection. 1460 * 1461 * This identifier can be used to determine which technology is 1462 * currently being used for reception. 1463 * 1464 * Some program selectors contain tuning information for different radio 1465 * technologies (i.e. FM RDS and DAB). For example, user may tune using 1466 * a ProgramSelector with RDS_PI primary identifier, but the tuner hardware 1467 * may choose to use DAB technology to make actual tuning. This identifier 1468 * must reflect that. 1469 */ getLogicallyTunedTo()1470 public @Nullable ProgramSelector.Identifier getLogicallyTunedTo() { 1471 return mLogicallyTunedTo; 1472 } 1473 1474 /** 1475 * Identifier currently used by hardware to physically tune to a channel. 1476 * 1477 * Some radio technologies broadcast the same program on multiple channels, 1478 * i.e. with RDS AF the same program may be broadcasted on multiple 1479 * alternative frequencies; the same DAB program may be broadcast on 1480 * multiple ensembles. This identifier points to the channel to which the 1481 * radio hardware is physically tuned to. 1482 */ getPhysicallyTunedTo()1483 public @Nullable ProgramSelector.Identifier getPhysicallyTunedTo() { 1484 return mPhysicallyTunedTo; 1485 } 1486 1487 /** 1488 * Primary identifiers of related contents. 1489 * 1490 * Some radio technologies provide pointers to other programs that carry 1491 * related content (i.e. DAB soft-links). This field is a list of pointers 1492 * to other programs on the program list. 1493 * 1494 * Please note, that these identifiers does not have to exist on the program 1495 * list - i.e. DAB tuner may provide information on FM RDS alternatives 1496 * despite not supporting FM RDS. If the system has multiple tuners, another 1497 * one may have it on its list. 1498 */ getRelatedContent()1499 public @Nullable Collection<ProgramSelector.Identifier> getRelatedContent() { 1500 return mRelatedContent; 1501 } 1502 1503 /** Main channel expressed in units according to band type. 1504 * Currently all defined band types express channels as frequency in kHz 1505 * @return the program channel 1506 * @deprecated Use {@link getSelector()} instead. 1507 */ 1508 @Deprecated getChannel()1509 public int getChannel() { 1510 try { 1511 return (int) mSelector.getFirstId(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY); 1512 } catch (IllegalArgumentException ex) { 1513 Log.w(TAG, "Not an AM/FM program"); 1514 return 0; 1515 } 1516 } 1517 1518 /** Sub channel ID. E.g 1 for HD radio HD1 1519 * @return the program sub channel 1520 * @deprecated Use {@link getSelector()} instead. 1521 */ 1522 @Deprecated getSubChannel()1523 public int getSubChannel() { 1524 try { 1525 return (int) mSelector.getFirstId( 1526 ProgramSelector.IDENTIFIER_TYPE_HD_SUBCHANNEL) + 1; 1527 } catch (IllegalArgumentException ex) { 1528 // this is a normal behavior for analog AM/FM selector 1529 return 0; 1530 } 1531 } 1532 1533 /** {@code true} if the tuner is currently tuned on a valid station 1534 * @return {@code true} if currently tuned, {@code false} otherwise. 1535 */ isTuned()1536 public boolean isTuned() { 1537 return (mInfoFlags & FLAG_TUNED) != 0; 1538 } 1539 1540 /** {@code true} if the received program is stereo 1541 * @return {@code true} if stereo, {@code false} otherwise. 1542 */ isStereo()1543 public boolean isStereo() { 1544 return (mInfoFlags & FLAG_STEREO) != 0; 1545 } 1546 1547 /** {@code true} if the received program is digital (e.g HD radio) 1548 * @return {@code true} if digital, {@code false} otherwise. 1549 * @deprecated Use {@link getLogicallyTunedTo()} instead. 1550 */ 1551 @Deprecated isDigital()1552 public boolean isDigital() { 1553 ProgramSelector.Identifier id = mLogicallyTunedTo; 1554 if (id == null) id = mSelector.getPrimaryId(); 1555 1556 int type = id.getType(); 1557 return (type != ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY 1558 && type != ProgramSelector.IDENTIFIER_TYPE_RDS_PI); 1559 } 1560 1561 /** 1562 * {@code true} if the program is currently playing live stream. 1563 * This may result in a slightly altered reception parameters, 1564 * usually targetted at reduced latency. 1565 */ isLive()1566 public boolean isLive() { 1567 return (mInfoFlags & FLAG_LIVE) != 0; 1568 } 1569 1570 /** 1571 * {@code true} if radio stream is not playing, ie. due to bad reception 1572 * conditions or buffering. In this state volume knob MAY be disabled to 1573 * prevent user increasing volume too much. 1574 * It does NOT mean the user has muted audio. 1575 */ isMuted()1576 public boolean isMuted() { 1577 return (mInfoFlags & FLAG_MUTED) != 0; 1578 } 1579 1580 /** 1581 * {@code true} if radio station transmits traffic information 1582 * regularily. 1583 */ isTrafficProgram()1584 public boolean isTrafficProgram() { 1585 return (mInfoFlags & FLAG_TRAFFIC_PROGRAM) != 0; 1586 } 1587 1588 /** 1589 * {@code true} if radio station transmits traffic information 1590 * at the very moment. 1591 */ isTrafficAnnouncementActive()1592 public boolean isTrafficAnnouncementActive() { 1593 return (mInfoFlags & FLAG_TRAFFIC_ANNOUNCEMENT) != 0; 1594 } 1595 1596 /** 1597 * Signal quality (as opposed to the name) indication from 0 (no signal) 1598 * to 100 (excellent) 1599 * @return the signal quality indication. 1600 */ getSignalStrength()1601 public int getSignalStrength() { 1602 return mSignalQuality; 1603 } 1604 1605 /** Metadata currently received from this station. 1606 * null if no metadata have been received 1607 * @return current meta data received from this program. 1608 */ getMetadata()1609 public RadioMetadata getMetadata() { 1610 return mMetadata; 1611 } 1612 1613 /** 1614 * A map of vendor-specific opaque strings, passed from HAL without changes. 1615 * Format of these strings can vary across vendors. 1616 * 1617 * It may be used for extra features, that's not supported by a platform, 1618 * for example: paid-service=true; bitrate=320kbps. 1619 * 1620 * Keys must be prefixed with unique vendor Java-style namespace, 1621 * eg. 'com.somecompany.parameter1'. 1622 */ getVendorInfo()1623 public @NonNull Map<String, String> getVendorInfo() { 1624 return mVendorInfo; 1625 } 1626 ProgramInfo(Parcel in)1627 private ProgramInfo(Parcel in) { 1628 mSelector = Objects.requireNonNull(in.readTypedObject(ProgramSelector.CREATOR)); 1629 mLogicallyTunedTo = in.readTypedObject(ProgramSelector.Identifier.CREATOR); 1630 mPhysicallyTunedTo = in.readTypedObject(ProgramSelector.Identifier.CREATOR); 1631 mRelatedContent = in.createTypedArrayList(ProgramSelector.Identifier.CREATOR); 1632 mInfoFlags = in.readInt(); 1633 mSignalQuality = in.readInt(); 1634 mMetadata = in.readTypedObject(RadioMetadata.CREATOR); 1635 mVendorInfo = Utils.readStringMap(in); 1636 } 1637 1638 public static final @android.annotation.NonNull Parcelable.Creator<ProgramInfo> CREATOR 1639 = new Parcelable.Creator<ProgramInfo>() { 1640 public ProgramInfo createFromParcel(Parcel in) { 1641 return new ProgramInfo(in); 1642 } 1643 1644 public ProgramInfo[] newArray(int size) { 1645 return new ProgramInfo[size]; 1646 } 1647 }; 1648 1649 @Override writeToParcel(Parcel dest, int flags)1650 public void writeToParcel(Parcel dest, int flags) { 1651 dest.writeTypedObject(mSelector, flags); 1652 dest.writeTypedObject(mLogicallyTunedTo, flags); 1653 dest.writeTypedObject(mPhysicallyTunedTo, flags); 1654 Utils.writeTypedCollection(dest, mRelatedContent); 1655 dest.writeInt(mInfoFlags); 1656 dest.writeInt(mSignalQuality); 1657 dest.writeTypedObject(mMetadata, flags); 1658 Utils.writeStringMap(dest, mVendorInfo); 1659 } 1660 1661 @Override describeContents()1662 public int describeContents() { 1663 return 0; 1664 } 1665 1666 @NonNull 1667 @Override toString()1668 public String toString() { 1669 return "ProgramInfo" 1670 + " [selector=" + mSelector 1671 + ", logicallyTunedTo=" + Objects.toString(mLogicallyTunedTo) 1672 + ", physicallyTunedTo=" + Objects.toString(mPhysicallyTunedTo) 1673 + ", relatedContent=" + mRelatedContent.size() 1674 + ", infoFlags=" + mInfoFlags 1675 + ", mSignalQuality=" + mSignalQuality 1676 + ", mMetadata=" + Objects.toString(mMetadata) 1677 + "]"; 1678 } 1679 1680 @Override hashCode()1681 public int hashCode() { 1682 return Objects.hash(mSelector, mLogicallyTunedTo, mPhysicallyTunedTo, 1683 mRelatedContent, mInfoFlags, mSignalQuality, mMetadata, mVendorInfo); 1684 } 1685 1686 @Override equals(@ullable Object obj)1687 public boolean equals(@Nullable Object obj) { 1688 if (this == obj) return true; 1689 if (!(obj instanceof ProgramInfo)) return false; 1690 ProgramInfo other = (ProgramInfo) obj; 1691 1692 if (!Objects.equals(mSelector, other.mSelector)) return false; 1693 if (!Objects.equals(mLogicallyTunedTo, other.mLogicallyTunedTo)) return false; 1694 if (!Objects.equals(mPhysicallyTunedTo, other.mPhysicallyTunedTo)) return false; 1695 if (!Objects.equals(mRelatedContent, other.mRelatedContent)) return false; 1696 if (mInfoFlags != other.mInfoFlags) return false; 1697 if (mSignalQuality != other.mSignalQuality) return false; 1698 if (!Objects.equals(mMetadata, other.mMetadata)) return false; 1699 if (!Objects.equals(mVendorInfo, other.mVendorInfo)) return false; 1700 1701 return true; 1702 } 1703 } 1704 1705 1706 /** 1707 * Returns a list of descriptors for all broadcast radio modules present on the device. 1708 * @param modules An List of {@link ModuleProperties} where the list will be returned. 1709 * @return 1710 * <ul> 1711 * <li>{@link #STATUS_OK} in case of success, </li> 1712 * <li>{@link #STATUS_ERROR} in case of unspecified error, </li> 1713 * <li>{@link #STATUS_NO_INIT} if the native service cannot be reached, </li> 1714 * <li>{@link #STATUS_BAD_VALUE} if modules is null, </li> 1715 * <li>{@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails, </li> 1716 * </ul> 1717 */ 1718 @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) listModules(List<ModuleProperties> modules)1719 public int listModules(List<ModuleProperties> modules) { 1720 if (modules == null) { 1721 Log.e(TAG, "the output list must not be empty"); 1722 return STATUS_BAD_VALUE; 1723 } 1724 1725 Log.d(TAG, "Listing available tuners..."); 1726 List<ModuleProperties> returnedList; 1727 try { 1728 returnedList = mService.listModules(); 1729 } catch (RemoteException e) { 1730 Log.e(TAG, "Failed listing available tuners", e); 1731 return STATUS_DEAD_OBJECT; 1732 } 1733 1734 if (returnedList == null) { 1735 Log.e(TAG, "Returned list was a null"); 1736 return STATUS_ERROR; 1737 } 1738 1739 modules.addAll(returnedList); 1740 return STATUS_OK; 1741 } 1742 nativeListModules(List<ModuleProperties> modules)1743 private native int nativeListModules(List<ModuleProperties> modules); 1744 1745 /** 1746 * Open an interface to control a tuner on a given broadcast radio module. 1747 * Optionally selects and applies the configuration passed as "config" argument. 1748 * @param moduleId radio module identifier {@link ModuleProperties#getId()}. Mandatory. 1749 * @param config desired band and configuration to apply when enabling the hardware module. 1750 * optional, can be null. 1751 * @param withAudio {@code true} to request a tuner with an audio source. 1752 * This tuner is intended for live listening or recording or a radio program. 1753 * If {@code false}, the tuner can only be used to retrieve program informations. 1754 * @param callback {@link RadioTuner.Callback} interface. Mandatory. 1755 * @param handler the Handler on which the callbacks will be received. 1756 * Can be null if default handler is OK. 1757 * @return a valid {@link RadioTuner} interface in case of success or null in case of error. 1758 */ 1759 @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) openTuner(int moduleId, BandConfig config, boolean withAudio, RadioTuner.Callback callback, Handler handler)1760 public RadioTuner openTuner(int moduleId, BandConfig config, boolean withAudio, 1761 RadioTuner.Callback callback, Handler handler) { 1762 if (callback == null) { 1763 throw new IllegalArgumentException("callback must not be empty"); 1764 } 1765 1766 Log.d(TAG, "Opening tuner " + moduleId + "..."); 1767 1768 ITuner tuner; 1769 TunerCallbackAdapter halCallback = new TunerCallbackAdapter(callback, handler); 1770 try { 1771 tuner = mService.openTuner(moduleId, config, withAudio, halCallback); 1772 } catch (RemoteException | IllegalArgumentException | IllegalStateException ex) { 1773 Log.e(TAG, "Failed to open tuner", ex); 1774 return null; 1775 } 1776 if (tuner == null) { 1777 Log.e(TAG, "Failed to open tuner"); 1778 return null; 1779 } 1780 return new TunerAdapter(tuner, halCallback, 1781 config != null ? config.getType() : BAND_INVALID); 1782 } 1783 1784 private final Map<Announcement.OnListUpdatedListener, ICloseHandle> mAnnouncementListeners = 1785 new HashMap<>(); 1786 1787 /** 1788 * Adds new announcement listener. 1789 * 1790 * @param enabledAnnouncementTypes a set of announcement types to listen to 1791 * @param listener announcement listener 1792 */ 1793 @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) addAnnouncementListener(@onNull Set<Integer> enabledAnnouncementTypes, @NonNull Announcement.OnListUpdatedListener listener)1794 public void addAnnouncementListener(@NonNull Set<Integer> enabledAnnouncementTypes, 1795 @NonNull Announcement.OnListUpdatedListener listener) { 1796 addAnnouncementListener(cmd -> cmd.run(), enabledAnnouncementTypes, listener); 1797 } 1798 1799 /** 1800 * Adds new announcement listener with executor. 1801 * 1802 * @param executor the executor 1803 * @param enabledAnnouncementTypes a set of announcement types to listen to 1804 * @param listener announcement listener 1805 */ 1806 @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) addAnnouncementListener(@onNull @allbackExecutor Executor executor, @NonNull Set<Integer> enabledAnnouncementTypes, @NonNull Announcement.OnListUpdatedListener listener)1807 public void addAnnouncementListener(@NonNull @CallbackExecutor Executor executor, 1808 @NonNull Set<Integer> enabledAnnouncementTypes, 1809 @NonNull Announcement.OnListUpdatedListener listener) { 1810 Objects.requireNonNull(executor); 1811 Objects.requireNonNull(listener); 1812 int[] types = enabledAnnouncementTypes.stream().mapToInt(Integer::intValue).toArray(); 1813 IAnnouncementListener listenerIface = new IAnnouncementListener.Stub() { 1814 public void onListUpdated(List<Announcement> activeAnnouncements) { 1815 executor.execute(() -> listener.onListUpdated(activeAnnouncements)); 1816 } 1817 }; 1818 synchronized (mAnnouncementListeners) { 1819 ICloseHandle closeHandle = null; 1820 try { 1821 closeHandle = mService.addAnnouncementListener(types, listenerIface); 1822 } catch (RemoteException ex) { 1823 ex.rethrowFromSystemServer(); 1824 } 1825 Objects.requireNonNull(closeHandle); 1826 ICloseHandle oldCloseHandle = mAnnouncementListeners.put(listener, closeHandle); 1827 if (oldCloseHandle != null) Utils.close(oldCloseHandle); 1828 } 1829 } 1830 1831 /** 1832 * Removes previously registered announcement listener. 1833 * 1834 * @param listener announcement listener, previously registered with 1835 * {@link addAnnouncementListener} 1836 */ 1837 @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) removeAnnouncementListener(@onNull Announcement.OnListUpdatedListener listener)1838 public void removeAnnouncementListener(@NonNull Announcement.OnListUpdatedListener listener) { 1839 Objects.requireNonNull(listener); 1840 synchronized (mAnnouncementListeners) { 1841 ICloseHandle closeHandle = mAnnouncementListeners.remove(listener); 1842 if (closeHandle != null) Utils.close(closeHandle); 1843 } 1844 } 1845 1846 @NonNull private final Context mContext; 1847 @NonNull private final IRadioService mService; 1848 1849 /** 1850 * @hide 1851 */ RadioManager(@onNull Context context)1852 public RadioManager(@NonNull Context context) throws ServiceNotFoundException { 1853 mContext = context; 1854 mService = IRadioService.Stub.asInterface( 1855 ServiceManager.getServiceOrThrow(Context.RADIO_SERVICE)); 1856 } 1857 } 1858