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