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