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