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 1551 /** @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)1552 public ProgramInfo(@NonNull ProgramSelector selector, 1553 @Nullable ProgramSelector.Identifier logicallyTunedTo, 1554 @Nullable ProgramSelector.Identifier physicallyTunedTo, 1555 @Nullable Collection<ProgramSelector.Identifier> relatedContent, 1556 int infoFlags, int signalQuality, @Nullable RadioMetadata metadata, 1557 @Nullable Map<String, String> vendorInfo) { 1558 mSelector = Objects.requireNonNull(selector); 1559 mLogicallyTunedTo = logicallyTunedTo; 1560 mPhysicallyTunedTo = physicallyTunedTo; 1561 if (relatedContent == null) { 1562 mRelatedContent = Collections.emptyList(); 1563 } else { 1564 Preconditions.checkCollectionElementsNotNull(relatedContent, "relatedContent"); 1565 mRelatedContent = relatedContent; 1566 } 1567 mInfoFlags = infoFlags; 1568 mSignalQuality = signalQuality; 1569 mMetadata = metadata; 1570 mVendorInfo = (vendorInfo == null) ? new HashMap<>() : vendorInfo; 1571 } 1572 1573 /** 1574 * Program selector, necessary for tuning to a program. 1575 * 1576 * @return the program selector. 1577 */ getSelector()1578 public @NonNull ProgramSelector getSelector() { 1579 return mSelector; 1580 } 1581 1582 /** 1583 * Identifier currently used for program selection. 1584 * 1585 * <p>This identifier can be used to determine which technology is 1586 * currently being used for reception. 1587 * 1588 * <p>Some program selectors contain tuning information for different radio 1589 * technologies (i.e. FM RDS and DAB). For example, user may tune using 1590 * a ProgramSelector with RDS_PI primary identifier, but the tuner hardware 1591 * may choose to use DAB technology to make actual tuning. This identifier 1592 * must reflect that. 1593 */ getLogicallyTunedTo()1594 public @Nullable ProgramSelector.Identifier getLogicallyTunedTo() { 1595 return mLogicallyTunedTo; 1596 } 1597 1598 /** 1599 * Identifier currently used by hardware to physically tune to a channel. 1600 * 1601 * <p>Some radio technologies broadcast the same program on multiple channels, 1602 * i.e. with RDS AF the same program may be broadcasted on multiple 1603 * alternative frequencies; the same DAB program may be broadcast on 1604 * multiple ensembles. This identifier points to the channel to which the 1605 * radio hardware is physically tuned to. 1606 */ getPhysicallyTunedTo()1607 public @Nullable ProgramSelector.Identifier getPhysicallyTunedTo() { 1608 return mPhysicallyTunedTo; 1609 } 1610 1611 /** 1612 * Primary identifiers of related contents. 1613 * 1614 * <p>Some radio technologies provide pointers to other programs that carry 1615 * related content (i.e. DAB soft-links). This field is a list of pointers 1616 * to other programs on the program list. 1617 * 1618 * <p>Please note, that these identifiers does not have to exist on the program 1619 * list - i.e. DAB tuner may provide information on FM RDS alternatives 1620 * despite not supporting FM RDS. If the system has multiple tuners, another 1621 * one may have it on its list. 1622 */ getRelatedContent()1623 public @Nullable Collection<ProgramSelector.Identifier> getRelatedContent() { 1624 return mRelatedContent; 1625 } 1626 1627 /** 1628 * Main channel expressed in units according to band type. 1629 * Currently all defined band types express channels as frequency in kHz 1630 * @return the program channel 1631 * @deprecated Use {@link ProgramInfo#getSelector} instead. 1632 */ 1633 @Deprecated getChannel()1634 public int getChannel() { 1635 try { 1636 return (int) mSelector.getFirstId(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY); 1637 } catch (IllegalArgumentException ex) { 1638 Log.w(TAG, "Not an AM/FM program"); 1639 return 0; 1640 } 1641 } 1642 1643 /** 1644 * Sub channel ID. E.g. 1 for HD radio HD1 1645 * @return the program sub channel 1646 * @deprecated Use {@link ProgramInfo#getSelector} instead. 1647 */ 1648 @Deprecated getSubChannel()1649 public int getSubChannel() { 1650 try { 1651 return (int) mSelector.getFirstId( 1652 ProgramSelector.IDENTIFIER_TYPE_HD_SUBCHANNEL) + 1; 1653 } catch (IllegalArgumentException ex) { 1654 // this is a normal behavior for analog AM/FM selector 1655 return 0; 1656 } 1657 } 1658 1659 /** {@code true} if the tuner is currently tuned on a valid station 1660 * @return {@code true} if currently tuned, {@code false} otherwise. 1661 */ isTuned()1662 public boolean isTuned() { 1663 return (mInfoFlags & FLAG_TUNED) != 0; 1664 } 1665 1666 /** 1667 * {@code true} if the received program is stereo 1668 * @return {@code true} if stereo, {@code false} otherwise. 1669 */ isStereo()1670 public boolean isStereo() { 1671 return (mInfoFlags & FLAG_STEREO) != 0; 1672 } 1673 1674 /** 1675 * {@code true} if the received program is digital (e.g. HD radio) 1676 * @return {@code true} if digital, {@code false} otherwise. 1677 * @deprecated Use {@link ProgramInfo#getLogicallyTunedTo()} instead. 1678 */ 1679 @Deprecated isDigital()1680 public boolean isDigital() { 1681 ProgramSelector.Identifier id = mLogicallyTunedTo; 1682 if (id == null) id = mSelector.getPrimaryId(); 1683 1684 int type = id.getType(); 1685 return (type != ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY 1686 && type != ProgramSelector.IDENTIFIER_TYPE_RDS_PI); 1687 } 1688 1689 /** 1690 * {@code true} if the program is currently playing live stream. 1691 * 1692 * <p>This may result in a slightly altered reception parameters, 1693 * usually targeted at reduced latency. 1694 */ isLive()1695 public boolean isLive() { 1696 return (mInfoFlags & FLAG_LIVE) != 0; 1697 } 1698 1699 /** 1700 * {@code true} if radio stream is not playing, i.e. due to bad reception 1701 * conditions or buffering. In this state volume knob MAY be disabled to 1702 * prevent user increasing volume too much. 1703 * 1704 * <p>It does NOT mean the user has muted audio. 1705 */ isMuted()1706 public boolean isMuted() { 1707 return (mInfoFlags & FLAG_MUTED) != 0; 1708 } 1709 1710 /** 1711 * {@code true} if radio station transmits traffic information 1712 * regularily. 1713 */ isTrafficProgram()1714 public boolean isTrafficProgram() { 1715 return (mInfoFlags & FLAG_TRAFFIC_PROGRAM) != 0; 1716 } 1717 1718 /** 1719 * {@code true} if radio station transmits traffic information 1720 * at the very moment. 1721 */ isTrafficAnnouncementActive()1722 public boolean isTrafficAnnouncementActive() { 1723 return (mInfoFlags & FLAG_TRAFFIC_ANNOUNCEMENT) != 0; 1724 } 1725 1726 /** 1727 * @return {@code true} if the signal has been acquired. 1728 */ 1729 @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED) isSignalAcquired()1730 public boolean isSignalAcquired() { 1731 return (mInfoFlags & FLAG_SIGNAL_ACQUIRED) != 0; 1732 } 1733 /** 1734 * @return {@code true} if HD Station Information Service (SIS) information is available. 1735 */ 1736 @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED) isHdSisAvailable()1737 public boolean isHdSisAvailable() { 1738 return (mInfoFlags & FLAG_HD_SIS_ACQUIRED) != 0; 1739 } 1740 /** 1741 * @return {@code true} if HD audio is available. 1742 */ 1743 @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED) isHdAudioAvailable()1744 public boolean isHdAudioAvailable() { 1745 return (mInfoFlags & FLAG_HD_AUDIO_ACQUIRED) != 0; 1746 } 1747 1748 /** 1749 * Signal quality (as opposed to the name) indication from 0 (no signal) 1750 * to 100 (excellent) 1751 * @return the signal quality indication. 1752 */ getSignalStrength()1753 public int getSignalStrength() { 1754 return mSignalQuality; 1755 } 1756 1757 /** Metadata currently received from this station. 1758 * 1759 * @return current meta data received from this program, {@code null} if no metadata have 1760 * been received 1761 */ getMetadata()1762 public RadioMetadata getMetadata() { 1763 return mMetadata; 1764 } 1765 1766 /** 1767 * A map of vendor-specific opaque strings, passed from HAL without changes. 1768 * Format of these strings can vary across vendors. 1769 * 1770 * <p>It may be used for extra features, that's not supported by a platform, 1771 * for example: paid-service=true; bitrate=320kbps. 1772 * 1773 * <p>Keys must be prefixed with unique vendor Java-style namespace, 1774 * e.g. 'com.somecompany.parameter1'. 1775 */ getVendorInfo()1776 public @NonNull Map<String, String> getVendorInfo() { 1777 return mVendorInfo; 1778 } 1779 ProgramInfo(Parcel in)1780 private ProgramInfo(Parcel in) { 1781 mSelector = Objects.requireNonNull(in.readTypedObject(ProgramSelector.CREATOR)); 1782 mLogicallyTunedTo = in.readTypedObject(ProgramSelector.Identifier.CREATOR); 1783 mPhysicallyTunedTo = in.readTypedObject(ProgramSelector.Identifier.CREATOR); 1784 mRelatedContent = in.createTypedArrayList(ProgramSelector.Identifier.CREATOR); 1785 mInfoFlags = in.readInt(); 1786 mSignalQuality = in.readInt(); 1787 mMetadata = in.readTypedObject(RadioMetadata.CREATOR); 1788 mVendorInfo = Utils.readStringMap(in); 1789 } 1790 1791 public static final @android.annotation.NonNull Parcelable.Creator<ProgramInfo> CREATOR 1792 = new Parcelable.Creator<ProgramInfo>() { 1793 public ProgramInfo createFromParcel(Parcel in) { 1794 return new ProgramInfo(in); 1795 } 1796 1797 public ProgramInfo[] newArray(int size) { 1798 return new ProgramInfo[size]; 1799 } 1800 }; 1801 1802 @Override writeToParcel(Parcel dest, int flags)1803 public void writeToParcel(Parcel dest, int flags) { 1804 dest.writeTypedObject(mSelector, flags); 1805 dest.writeTypedObject(mLogicallyTunedTo, flags); 1806 dest.writeTypedObject(mPhysicallyTunedTo, flags); 1807 Utils.writeTypedCollection(dest, mRelatedContent); 1808 dest.writeInt(mInfoFlags); 1809 dest.writeInt(mSignalQuality); 1810 dest.writeTypedObject(mMetadata, flags); 1811 Utils.writeStringMap(dest, mVendorInfo); 1812 } 1813 1814 @Override describeContents()1815 public int describeContents() { 1816 return 0; 1817 } 1818 1819 @NonNull 1820 @Override toString()1821 public String toString() { 1822 return "ProgramInfo" 1823 + " [selector=" + mSelector 1824 + ", logicallyTunedTo=" + Objects.toString(mLogicallyTunedTo) 1825 + ", physicallyTunedTo=" + Objects.toString(mPhysicallyTunedTo) 1826 + ", relatedContent=" + mRelatedContent.size() 1827 + ", infoFlags=" + mInfoFlags 1828 + ", mSignalQuality=" + mSignalQuality 1829 + ", mMetadata=" + Objects.toString(mMetadata) 1830 + "]"; 1831 } 1832 1833 @Override hashCode()1834 public int hashCode() { 1835 return Objects.hash(mSelector, mLogicallyTunedTo, mPhysicallyTunedTo, 1836 mRelatedContent, mInfoFlags, mSignalQuality, mMetadata, mVendorInfo); 1837 } 1838 1839 @Override equals(@ullable Object obj)1840 public boolean equals(@Nullable Object obj) { 1841 if (this == obj) return true; 1842 if (!(obj instanceof ProgramInfo)) return false; 1843 ProgramInfo other = (ProgramInfo) obj; 1844 1845 if (!mSelector.strictEquals(other.mSelector)) return false; 1846 if (!Objects.equals(mLogicallyTunedTo, other.mLogicallyTunedTo)) return false; 1847 if (!Objects.equals(mPhysicallyTunedTo, other.mPhysicallyTunedTo)) return false; 1848 if (!Objects.equals(mRelatedContent, other.mRelatedContent)) return false; 1849 if (mInfoFlags != other.mInfoFlags) return false; 1850 if (mSignalQuality != other.mSignalQuality) return false; 1851 if (!Objects.equals(mMetadata, other.mMetadata)) return false; 1852 if (!Objects.equals(mVendorInfo, other.mVendorInfo)) return false; 1853 1854 return true; 1855 } 1856 } 1857 1858 1859 /** 1860 * Returns a list of descriptors for all broadcast radio modules present on the device. 1861 * @param modules An List of {@link ModuleProperties} where the list will be returned. 1862 * @return 1863 * <ul> 1864 * <li>{@link #STATUS_OK} in case of success, </li> 1865 * <li>{@link #STATUS_ERROR} in case of unspecified error, </li> 1866 * <li>{@link #STATUS_NO_INIT} if the native service cannot be reached, </li> 1867 * <li>{@link #STATUS_BAD_VALUE} if modules is null, </li> 1868 * <li>{@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails, </li> 1869 * </ul> 1870 */ 1871 @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) 1872 @RadioStatusType listModules(List<ModuleProperties> modules)1873 public int listModules(List<ModuleProperties> modules) { 1874 if (modules == null) { 1875 Log.e(TAG, "the output list must not be empty"); 1876 return STATUS_BAD_VALUE; 1877 } 1878 1879 Log.d(TAG, "Listing available tuners..."); 1880 List<ModuleProperties> returnedList; 1881 try { 1882 returnedList = mService.listModules(); 1883 } catch (RemoteException e) { 1884 Log.e(TAG, "Failed listing available tuners", e); 1885 return STATUS_DEAD_OBJECT; 1886 } 1887 1888 if (returnedList == null) { 1889 Log.e(TAG, "Returned list was a null"); 1890 return STATUS_ERROR; 1891 } 1892 1893 modules.addAll(returnedList); 1894 return STATUS_OK; 1895 } 1896 nativeListModules(List<ModuleProperties> modules)1897 private native int nativeListModules(List<ModuleProperties> modules); 1898 1899 /** 1900 * Open an interface to control a tuner on a given broadcast radio module. 1901 * 1902 * <p>Optionally selects and applies the configuration passed as "config" argument. 1903 * @param moduleId radio module identifier {@link ModuleProperties#getId()}. Mandatory. 1904 * @param config desired band and configuration to apply when enabling the hardware module. 1905 * optional, can be null. 1906 * @param withAudio {@code true} to request a tuner with an audio source. 1907 * This tuner is intended for live listening or recording or a radio program. 1908 * If {@code false}, the tuner can only be used to retrieve program information. 1909 * @param callback {@link RadioTuner.Callback} interface. Mandatory. 1910 * @param handler the Handler on which the callbacks will be received. 1911 * Can be null if default handler is OK. 1912 * @return a valid {@link RadioTuner} interface in case of success or null in case of error. 1913 */ 1914 @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) openTuner(int moduleId, BandConfig config, boolean withAudio, RadioTuner.Callback callback, Handler handler)1915 public RadioTuner openTuner(int moduleId, BandConfig config, boolean withAudio, 1916 RadioTuner.Callback callback, Handler handler) { 1917 if (callback == null) { 1918 throw new IllegalArgumentException("callback must not be empty"); 1919 } 1920 1921 Log.d(TAG, "Opening tuner " + moduleId + "..."); 1922 1923 ITuner tuner; 1924 TunerCallbackAdapter halCallback = new TunerCallbackAdapter(callback, handler); 1925 try { 1926 tuner = mService.openTuner(moduleId, config, withAudio, halCallback); 1927 } catch (RemoteException | IllegalArgumentException | IllegalStateException ex) { 1928 Log.e(TAG, "Failed to open tuner", ex); 1929 return null; 1930 } 1931 if (tuner == null) { 1932 Log.e(TAG, "Failed to open tuner"); 1933 return null; 1934 } 1935 return new TunerAdapter(tuner, halCallback, 1936 config != null ? config.getType() : BAND_INVALID); 1937 } 1938 1939 private final Map<Announcement.OnListUpdatedListener, ICloseHandle> mAnnouncementListeners = 1940 new HashMap<>(); 1941 1942 /** 1943 * Adds new announcement listener. 1944 * 1945 * @param enabledAnnouncementTypes a set of announcement types to listen to 1946 * @param listener announcement listener 1947 */ 1948 @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) addAnnouncementListener(@onNull Set<Integer> enabledAnnouncementTypes, @NonNull Announcement.OnListUpdatedListener listener)1949 public void addAnnouncementListener(@NonNull Set<Integer> enabledAnnouncementTypes, 1950 @NonNull Announcement.OnListUpdatedListener listener) { 1951 addAnnouncementListener(cmd -> cmd.run(), enabledAnnouncementTypes, listener); 1952 } 1953 1954 /** 1955 * Adds new announcement listener with executor. 1956 * 1957 * @param executor the executor 1958 * @param enabledAnnouncementTypes a set of announcement types to listen to 1959 * @param listener announcement listener 1960 */ 1961 @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) addAnnouncementListener(@onNull @allbackExecutor Executor executor, @NonNull Set<Integer> enabledAnnouncementTypes, @NonNull Announcement.OnListUpdatedListener listener)1962 public void addAnnouncementListener(@NonNull @CallbackExecutor Executor executor, 1963 @NonNull Set<Integer> enabledAnnouncementTypes, 1964 @NonNull Announcement.OnListUpdatedListener listener) { 1965 Objects.requireNonNull(executor); 1966 Objects.requireNonNull(listener); 1967 int[] types = enabledAnnouncementTypes.stream().mapToInt(Integer::intValue).toArray(); 1968 IAnnouncementListener listenerIface = new IAnnouncementListener.Stub() { 1969 public void onListUpdated(List<Announcement> activeAnnouncements) { 1970 executor.execute(() -> listener.onListUpdated(activeAnnouncements)); 1971 } 1972 }; 1973 synchronized (mAnnouncementListeners) { 1974 ICloseHandle closeHandle = null; 1975 try { 1976 closeHandle = mService.addAnnouncementListener(types, listenerIface); 1977 } catch (RemoteException ex) { 1978 ex.rethrowFromSystemServer(); 1979 } 1980 Objects.requireNonNull(closeHandle); 1981 ICloseHandle oldCloseHandle = mAnnouncementListeners.put(listener, closeHandle); 1982 if (oldCloseHandle != null) Utils.close(oldCloseHandle); 1983 } 1984 } 1985 1986 /** 1987 * Removes previously registered announcement listener. 1988 * 1989 * @param listener announcement listener, previously registered with 1990 * {@link #addAnnouncementListener(Executor, Set, Announcement.OnListUpdatedListener)} 1991 * or {@link #addAnnouncementListener(Set, Announcement.OnListUpdatedListener)} 1992 */ 1993 @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) removeAnnouncementListener(@onNull Announcement.OnListUpdatedListener listener)1994 public void removeAnnouncementListener(@NonNull Announcement.OnListUpdatedListener listener) { 1995 Objects.requireNonNull(listener); 1996 synchronized (mAnnouncementListeners) { 1997 ICloseHandle closeHandle = mAnnouncementListeners.remove(listener); 1998 if (closeHandle != null) Utils.close(closeHandle); 1999 } 2000 } 2001 2002 @NonNull private final Context mContext; 2003 @NonNull private final IRadioService mService; 2004 2005 /** 2006 * @hide 2007 */ RadioManager(Context context)2008 public RadioManager(Context context) throws ServiceNotFoundException { 2009 this(context, IRadioService.Stub.asInterface(ServiceManager.getServiceOrThrow( 2010 Context.RADIO_SERVICE))); 2011 } 2012 2013 /** 2014 * @hide 2015 */ 2016 @VisibleForTesting RadioManager(Context context, IRadioService service)2017 public RadioManager(Context context, IRadioService service) { 2018 mContext = context; 2019 mService = service; 2020 } 2021 } 2022