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 17package android.hardware.radio; 18 19import android.Manifest; 20import android.annotation.IntDef; 21import android.annotation.NonNull; 22import android.annotation.RequiresPermission; 23import android.annotation.SystemApi; 24import android.annotation.SystemService; 25import android.content.Context; 26import android.os.Handler; 27import android.os.Parcel; 28import android.os.Parcelable; 29import android.os.RemoteException; 30import android.os.ServiceManager; 31import android.os.ServiceManager.ServiceNotFoundException; 32import android.text.TextUtils; 33import android.util.Log; 34 35import java.lang.annotation.Retention; 36import java.lang.annotation.RetentionPolicy; 37import java.util.Arrays; 38import java.util.HashMap; 39import java.util.List; 40import java.util.Map; 41import java.util.Set; 42import java.util.stream.Collectors; 43 44/** 45 * The RadioManager class allows to control a broadcast radio tuner present on the device. 46 * It provides data structures and methods to query for available radio modules, list their 47 * properties and open an interface to control tuning operations and receive callbacks when 48 * asynchronous operations complete or events occur. 49 * @hide 50 */ 51@SystemApi 52@SystemService(Context.RADIO_SERVICE) 53public class RadioManager { 54 private static final String TAG = "BroadcastRadio.manager"; 55 56 /** Method return status: successful operation */ 57 public static final int STATUS_OK = 0; 58 /** Method return status: unspecified error */ 59 public static final int STATUS_ERROR = Integer.MIN_VALUE; 60 /** Method return status: permission denied */ 61 public static final int STATUS_PERMISSION_DENIED = -1; 62 /** Method return status: initialization failure */ 63 public static final int STATUS_NO_INIT = -19; 64 /** Method return status: invalid argument provided */ 65 public static final int STATUS_BAD_VALUE = -22; 66 /** Method return status: cannot reach service */ 67 public static final int STATUS_DEAD_OBJECT = -32; 68 /** Method return status: invalid or out of sequence operation */ 69 public static final int STATUS_INVALID_OPERATION = -38; 70 /** Method return status: time out before operation completion */ 71 public static final int STATUS_TIMED_OUT = -110; 72 73 74 // keep in sync with radio_class_t in /system/core/incluse/system/radio.h 75 /** Radio module class supporting FM (including HD radio) and AM */ 76 public static final int CLASS_AM_FM = 0; 77 /** Radio module class supporting satellite radio */ 78 public static final int CLASS_SAT = 1; 79 /** Radio module class supporting Digital terrestrial radio */ 80 public static final int CLASS_DT = 2; 81 82 public static final int BAND_INVALID = -1; 83 /** AM radio band (LW/MW/SW). 84 * @see BandDescriptor */ 85 public static final int BAND_AM = 0; 86 /** FM radio band. 87 * @see BandDescriptor */ 88 public static final int BAND_FM = 1; 89 /** FM HD radio or DRM band. 90 * @see BandDescriptor */ 91 public static final int BAND_FM_HD = 2; 92 /** AM HD radio or DRM band. 93 * @see BandDescriptor */ 94 public static final int BAND_AM_HD = 3; 95 @IntDef(prefix = { "BAND_" }, value = { 96 BAND_INVALID, 97 BAND_AM, 98 BAND_FM, 99 BAND_AM_HD, 100 BAND_FM_HD, 101 }) 102 @Retention(RetentionPolicy.SOURCE) 103 public @interface Band {} 104 105 // keep in sync with radio_region_t in /system/core/incluse/system/radio.h 106 /** Africa, Europe. 107 * @see BandDescriptor */ 108 public static final int REGION_ITU_1 = 0; 109 /** Americas. 110 * @see BandDescriptor */ 111 public static final int REGION_ITU_2 = 1; 112 /** Russia. 113 * @see BandDescriptor */ 114 public static final int REGION_OIRT = 2; 115 /** Japan. 116 * @see BandDescriptor */ 117 public static final int REGION_JAPAN = 3; 118 /** Korea. 119 * @see BandDescriptor */ 120 public static final int REGION_KOREA = 4; 121 122 private static void writeStringMap(@NonNull Parcel dest, @NonNull Map<String, String> map) { 123 dest.writeInt(map.size()); 124 for (Map.Entry<String, String> entry : map.entrySet()) { 125 dest.writeString(entry.getKey()); 126 dest.writeString(entry.getValue()); 127 } 128 } 129 130 private static @NonNull Map<String, String> readStringMap(@NonNull Parcel in) { 131 int size = in.readInt(); 132 Map<String, String> map = new HashMap<>(); 133 while (size-- > 0) { 134 String key = in.readString(); 135 String value = in.readString(); 136 map.put(key, value); 137 } 138 return map; 139 } 140 141 /***************************************************************************** 142 * Lists properties, options and radio bands supported by a given broadcast radio module. 143 * Each module has a unique ID used to address it when calling RadioManager APIs. 144 * Module properties are returned by {@link #listModules(List <ModuleProperties>)} method. 145 ****************************************************************************/ 146 public static class ModuleProperties implements Parcelable { 147 148 private final int mId; 149 @NonNull private final String mServiceName; 150 private final int mClassId; 151 private final String mImplementor; 152 private final String mProduct; 153 private final String mVersion; 154 private final String mSerial; 155 private final int mNumTuners; 156 private final int mNumAudioSources; 157 private final boolean mIsCaptureSupported; 158 private final BandDescriptor[] mBands; 159 private final boolean mIsBgScanSupported; 160 private final Set<Integer> mSupportedProgramTypes; 161 private final Set<Integer> mSupportedIdentifierTypes; 162 @NonNull private final Map<String, String> mVendorInfo; 163 164 ModuleProperties(int id, String serviceName, int classId, String implementor, 165 String product, String version, String serial, int numTuners, int numAudioSources, 166 boolean isCaptureSupported, BandDescriptor[] bands, boolean isBgScanSupported, 167 @ProgramSelector.ProgramType int[] supportedProgramTypes, 168 @ProgramSelector.IdentifierType int[] supportedIdentifierTypes, 169 Map<String, String> vendorInfo) { 170 mId = id; 171 mServiceName = TextUtils.isEmpty(serviceName) ? "default" : serviceName; 172 mClassId = classId; 173 mImplementor = implementor; 174 mProduct = product; 175 mVersion = version; 176 mSerial = serial; 177 mNumTuners = numTuners; 178 mNumAudioSources = numAudioSources; 179 mIsCaptureSupported = isCaptureSupported; 180 mBands = bands; 181 mIsBgScanSupported = isBgScanSupported; 182 mSupportedProgramTypes = arrayToSet(supportedProgramTypes); 183 mSupportedIdentifierTypes = arrayToSet(supportedIdentifierTypes); 184 mVendorInfo = (vendorInfo == null) ? new HashMap<>() : vendorInfo; 185 } 186 187 private static Set<Integer> arrayToSet(int[] arr) { 188 return Arrays.stream(arr).boxed().collect(Collectors.toSet()); 189 } 190 191 private static int[] setToArray(Set<Integer> set) { 192 return set.stream().mapToInt(Integer::intValue).toArray(); 193 } 194 195 /** Unique module identifier provided by the native service. 196 * For use with {@link #openTuner(int, BandConfig, boolean, Callback, Handler)}. 197 * @return the radio module unique identifier. 198 */ 199 public int getId() { 200 return mId; 201 } 202 203 /** 204 * Module service (driver) name as registered with HIDL. 205 * @return the module service name. 206 */ 207 public @NonNull String getServiceName() { 208 return mServiceName; 209 } 210 211 /** Module class identifier: {@link #CLASS_AM_FM}, {@link #CLASS_SAT}, {@link #CLASS_DT} 212 * @return the radio module class identifier. 213 */ 214 public int getClassId() { 215 return mClassId; 216 } 217 218 /** Human readable broadcast radio module implementor 219 * @return the name of the radio module implementator. 220 */ 221 public String getImplementor() { 222 return mImplementor; 223 } 224 225 /** Human readable broadcast radio module product name 226 * @return the radio module product name. 227 */ 228 public String getProduct() { 229 return mProduct; 230 } 231 232 /** Human readable broadcast radio module version number 233 * @return the radio module version. 234 */ 235 public String getVersion() { 236 return mVersion; 237 } 238 239 /** Radio module serial number. 240 * Can be used for subscription services. 241 * @return the radio module serial number. 242 */ 243 public String getSerial() { 244 return mSerial; 245 } 246 247 /** Number of tuners available. 248 * This is the number of tuners that can be open simultaneously. 249 * @return the number of tuners supported. 250 */ 251 public int getNumTuners() { 252 return mNumTuners; 253 } 254 255 /** Number tuner audio sources available. Must be less or equal to getNumTuners(). 256 * When more than one tuner is supported, one is usually for playback and has one 257 * associated audio source and the other is for pre scanning and building a 258 * program list. 259 * @return the number of audio sources available. 260 */ 261 public int getNumAudioSources() { 262 return mNumAudioSources; 263 } 264 265 /** {@code true} if audio capture is possible from radio tuner output. 266 * This indicates if routing to audio devices not connected to the same HAL as the FM radio 267 * is possible (e.g. to USB) or DAR (Digital Audio Recorder) feature can be implemented. 268 * @return {@code true} if audio capture is possible, {@code false} otherwise. 269 */ 270 public boolean isCaptureSupported() { 271 return mIsCaptureSupported; 272 } 273 274 /** 275 * {@code true} if the module supports background scanning. At the given time it may not 276 * be available though, see {@link RadioTuner#startBackgroundScan()}. 277 * 278 * @return {@code true} if background scanning is supported (not necessary available 279 * at a given time), {@code false} otherwise. 280 */ 281 public boolean isBackgroundScanningSupported() { 282 return mIsBgScanSupported; 283 } 284 285 /** 286 * Checks, if a given program type is supported by this tuner. 287 * 288 * If a program type is supported by radio module, it means it can tune 289 * to ProgramSelector of a given type. 290 * 291 * @return {@code true} if a given program type is supported. 292 */ 293 public boolean isProgramTypeSupported(@ProgramSelector.ProgramType int type) { 294 return mSupportedProgramTypes.contains(type); 295 } 296 297 /** 298 * Checks, if a given program identifier is supported by this tuner. 299 * 300 * If an identifier is supported by radio module, it means it can use it for 301 * tuning to ProgramSelector with either primary or secondary Identifier of 302 * a given type. 303 * 304 * @return {@code true} if a given program type is supported. 305 */ 306 public boolean isProgramIdentifierSupported(@ProgramSelector.IdentifierType int type) { 307 return mSupportedIdentifierTypes.contains(type); 308 } 309 310 /** 311 * A map of vendor-specific opaque strings, passed from HAL without changes. 312 * Format of these strings can vary across vendors. 313 * 314 * It may be used for extra features, that's not supported by a platform, 315 * for example: preset-slots=6; ultra-hd-capable=false. 316 * 317 * Keys must be prefixed with unique vendor Java-style namespace, 318 * eg. 'com.somecompany.parameter1'. 319 */ 320 public @NonNull Map<String, String> getVendorInfo() { 321 return mVendorInfo; 322 } 323 324 /** List of descriptors for all bands supported by this module. 325 * @return an array of {@link BandDescriptor}. 326 */ 327 public BandDescriptor[] getBands() { 328 return mBands; 329 } 330 331 private ModuleProperties(Parcel in) { 332 mId = in.readInt(); 333 String serviceName = in.readString(); 334 mServiceName = TextUtils.isEmpty(serviceName) ? "default" : serviceName; 335 mClassId = in.readInt(); 336 mImplementor = in.readString(); 337 mProduct = in.readString(); 338 mVersion = in.readString(); 339 mSerial = in.readString(); 340 mNumTuners = in.readInt(); 341 mNumAudioSources = in.readInt(); 342 mIsCaptureSupported = in.readInt() == 1; 343 Parcelable[] tmp = in.readParcelableArray(BandDescriptor.class.getClassLoader()); 344 mBands = new BandDescriptor[tmp.length]; 345 for (int i = 0; i < tmp.length; i++) { 346 mBands[i] = (BandDescriptor) tmp[i]; 347 } 348 mIsBgScanSupported = in.readInt() == 1; 349 mSupportedProgramTypes = arrayToSet(in.createIntArray()); 350 mSupportedIdentifierTypes = arrayToSet(in.createIntArray()); 351 mVendorInfo = readStringMap(in); 352 } 353 354 public static final Parcelable.Creator<ModuleProperties> CREATOR 355 = new Parcelable.Creator<ModuleProperties>() { 356 public ModuleProperties createFromParcel(Parcel in) { 357 return new ModuleProperties(in); 358 } 359 360 public ModuleProperties[] newArray(int size) { 361 return new ModuleProperties[size]; 362 } 363 }; 364 365 @Override 366 public void writeToParcel(Parcel dest, int flags) { 367 dest.writeInt(mId); 368 dest.writeString(mServiceName); 369 dest.writeInt(mClassId); 370 dest.writeString(mImplementor); 371 dest.writeString(mProduct); 372 dest.writeString(mVersion); 373 dest.writeString(mSerial); 374 dest.writeInt(mNumTuners); 375 dest.writeInt(mNumAudioSources); 376 dest.writeInt(mIsCaptureSupported ? 1 : 0); 377 dest.writeParcelableArray(mBands, flags); 378 dest.writeInt(mIsBgScanSupported ? 1 : 0); 379 dest.writeIntArray(setToArray(mSupportedProgramTypes)); 380 dest.writeIntArray(setToArray(mSupportedIdentifierTypes)); 381 writeStringMap(dest, mVendorInfo); 382 } 383 384 @Override 385 public int describeContents() { 386 return 0; 387 } 388 389 @Override 390 public String toString() { 391 return "ModuleProperties [mId=" + mId 392 + ", mServiceName=" + mServiceName + ", mClassId=" + mClassId 393 + ", mImplementor=" + mImplementor + ", mProduct=" + mProduct 394 + ", mVersion=" + mVersion + ", mSerial=" + mSerial 395 + ", mNumTuners=" + mNumTuners 396 + ", mNumAudioSources=" + mNumAudioSources 397 + ", mIsCaptureSupported=" + mIsCaptureSupported 398 + ", mIsBgScanSupported=" + mIsBgScanSupported 399 + ", mBands=" + Arrays.toString(mBands) + "]"; 400 } 401 402 @Override 403 public int hashCode() { 404 final int prime = 31; 405 int result = 1; 406 result = prime * result + mId; 407 result = prime * result + mServiceName.hashCode(); 408 result = prime * result + mClassId; 409 result = prime * result + ((mImplementor == null) ? 0 : mImplementor.hashCode()); 410 result = prime * result + ((mProduct == null) ? 0 : mProduct.hashCode()); 411 result = prime * result + ((mVersion == null) ? 0 : mVersion.hashCode()); 412 result = prime * result + ((mSerial == null) ? 0 : mSerial.hashCode()); 413 result = prime * result + mNumTuners; 414 result = prime * result + mNumAudioSources; 415 result = prime * result + (mIsCaptureSupported ? 1 : 0); 416 result = prime * result + Arrays.hashCode(mBands); 417 result = prime * result + (mIsBgScanSupported ? 1 : 0); 418 result = prime * result + mVendorInfo.hashCode(); 419 return result; 420 } 421 422 @Override 423 public boolean equals(Object obj) { 424 if (this == obj) 425 return true; 426 if (!(obj instanceof ModuleProperties)) 427 return false; 428 ModuleProperties other = (ModuleProperties) obj; 429 if (mId != other.getId()) 430 return false; 431 if (!TextUtils.equals(mServiceName, other.mServiceName)) return false; 432 if (mClassId != other.getClassId()) 433 return false; 434 if (mImplementor == null) { 435 if (other.getImplementor() != null) 436 return false; 437 } else if (!mImplementor.equals(other.getImplementor())) 438 return false; 439 if (mProduct == null) { 440 if (other.getProduct() != null) 441 return false; 442 } else if (!mProduct.equals(other.getProduct())) 443 return false; 444 if (mVersion == null) { 445 if (other.getVersion() != null) 446 return false; 447 } else if (!mVersion.equals(other.getVersion())) 448 return false; 449 if (mSerial == null) { 450 if (other.getSerial() != null) 451 return false; 452 } else if (!mSerial.equals(other.getSerial())) 453 return false; 454 if (mNumTuners != other.getNumTuners()) 455 return false; 456 if (mNumAudioSources != other.getNumAudioSources()) 457 return false; 458 if (mIsCaptureSupported != other.isCaptureSupported()) 459 return false; 460 if (!Arrays.equals(mBands, other.getBands())) 461 return false; 462 if (mIsBgScanSupported != other.isBackgroundScanningSupported()) 463 return false; 464 if (!mVendorInfo.equals(other.mVendorInfo)) return false; 465 return true; 466 } 467 } 468 469 /** Radio band descriptor: an element in ModuleProperties bands array. 470 * It is either an instance of {@link FmBandDescriptor} or {@link AmBandDescriptor} */ 471 public static class BandDescriptor implements Parcelable { 472 473 private final int mRegion; 474 private final int mType; 475 private final int mLowerLimit; 476 private final int mUpperLimit; 477 private final int mSpacing; 478 479 BandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing) { 480 if (type != BAND_AM && type != BAND_FM && type != BAND_FM_HD && type != BAND_AM_HD) { 481 throw new IllegalArgumentException("Unsupported band: " + type); 482 } 483 mRegion = region; 484 mType = type; 485 mLowerLimit = lowerLimit; 486 mUpperLimit = upperLimit; 487 mSpacing = spacing; 488 } 489 490 /** Region this band applies to. E.g. {@link #REGION_ITU_1} 491 * @return the region this band is associated to. 492 */ 493 public int getRegion() { 494 return mRegion; 495 } 496 /** Band type, e.g {@link #BAND_FM}. Defines the subclass this descriptor can be cast to: 497 * <ul> 498 * <li>{@link #BAND_FM} or {@link #BAND_FM_HD} cast to {@link FmBandDescriptor}, </li> 499 * <li>{@link #BAND_AM} cast to {@link AmBandDescriptor}, </li> 500 * </ul> 501 * @return the band type. 502 */ 503 public int getType() { 504 return mType; 505 } 506 507 /** 508 * Checks if the band is either AM or AM_HD. 509 * 510 * @return {@code true}, if band is AM or AM_HD. 511 */ 512 public boolean isAmBand() { 513 return mType == BAND_AM || mType == BAND_AM_HD; 514 } 515 516 /** 517 * Checks if the band is either FM or FM_HD. 518 * 519 * @return {@code true}, if band is FM or FM_HD. 520 */ 521 public boolean isFmBand() { 522 return mType == BAND_FM || mType == BAND_FM_HD; 523 } 524 525 /** Lower band limit expressed in units according to band type. 526 * Currently all defined band types express channels as frequency in kHz 527 * @return the lower band limit. 528 */ 529 public int getLowerLimit() { 530 return mLowerLimit; 531 } 532 /** Upper band limit expressed in units according to band type. 533 * Currently all defined band types express channels as frequency in kHz 534 * @return the upper band limit. 535 */ 536 public int getUpperLimit() { 537 return mUpperLimit; 538 } 539 /** Channel spacing in units according to band type. 540 * Currently all defined band types express channels as frequency in kHz 541 * @return the channel spacing. 542 */ 543 public int getSpacing() { 544 return mSpacing; 545 } 546 547 private BandDescriptor(Parcel in) { 548 mRegion = in.readInt(); 549 mType = in.readInt(); 550 mLowerLimit = in.readInt(); 551 mUpperLimit = in.readInt(); 552 mSpacing = in.readInt(); 553 } 554 555 private static int lookupTypeFromParcel(Parcel in) { 556 int pos = in.dataPosition(); 557 in.readInt(); // skip region 558 int type = in.readInt(); 559 in.setDataPosition(pos); 560 return type; 561 } 562 563 public static final Parcelable.Creator<BandDescriptor> CREATOR 564 = new Parcelable.Creator<BandDescriptor>() { 565 public BandDescriptor createFromParcel(Parcel in) { 566 int type = lookupTypeFromParcel(in); 567 switch (type) { 568 case BAND_FM: 569 case BAND_FM_HD: 570 return new FmBandDescriptor(in); 571 case BAND_AM: 572 case BAND_AM_HD: 573 return new AmBandDescriptor(in); 574 default: 575 throw new IllegalArgumentException("Unsupported band: " + type); 576 } 577 } 578 579 public BandDescriptor[] newArray(int size) { 580 return new BandDescriptor[size]; 581 } 582 }; 583 584 @Override 585 public void writeToParcel(Parcel dest, int flags) { 586 dest.writeInt(mRegion); 587 dest.writeInt(mType); 588 dest.writeInt(mLowerLimit); 589 dest.writeInt(mUpperLimit); 590 dest.writeInt(mSpacing); 591 } 592 593 @Override 594 public int describeContents() { 595 return 0; 596 } 597 598 @Override 599 public String toString() { 600 return "BandDescriptor [mRegion=" + mRegion + ", mType=" + mType + ", mLowerLimit=" 601 + mLowerLimit + ", mUpperLimit=" + mUpperLimit + ", mSpacing=" + mSpacing + "]"; 602 } 603 604 @Override 605 public int hashCode() { 606 final int prime = 31; 607 int result = 1; 608 result = prime * result + mRegion; 609 result = prime * result + mType; 610 result = prime * result + mLowerLimit; 611 result = prime * result + mUpperLimit; 612 result = prime * result + mSpacing; 613 return result; 614 } 615 616 @Override 617 public boolean equals(Object obj) { 618 if (this == obj) 619 return true; 620 if (!(obj instanceof BandDescriptor)) 621 return false; 622 BandDescriptor other = (BandDescriptor) obj; 623 if (mRegion != other.getRegion()) 624 return false; 625 if (mType != other.getType()) 626 return false; 627 if (mLowerLimit != other.getLowerLimit()) 628 return false; 629 if (mUpperLimit != other.getUpperLimit()) 630 return false; 631 if (mSpacing != other.getSpacing()) 632 return false; 633 return true; 634 } 635 } 636 637 /** FM band descriptor 638 * @see #BAND_FM 639 * @see #BAND_FM_HD */ 640 public static class FmBandDescriptor extends BandDescriptor { 641 private final boolean mStereo; 642 private final boolean mRds; 643 private final boolean mTa; 644 private final boolean mAf; 645 private final boolean mEa; 646 647 FmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing, 648 boolean stereo, boolean rds, boolean ta, boolean af, boolean ea) { 649 super(region, type, lowerLimit, upperLimit, spacing); 650 mStereo = stereo; 651 mRds = rds; 652 mTa = ta; 653 mAf = af; 654 mEa = ea; 655 } 656 657 /** Stereo is supported 658 * @return {@code true} if stereo is supported, {@code false} otherwise. 659 */ 660 public boolean isStereoSupported() { 661 return mStereo; 662 } 663 /** RDS or RBDS(if region is ITU2) is supported 664 * @return {@code true} if RDS or RBDS is supported, {@code false} otherwise. 665 */ 666 public boolean isRdsSupported() { 667 return mRds; 668 } 669 /** Traffic announcement is supported 670 * @return {@code true} if TA is supported, {@code false} otherwise. 671 */ 672 public boolean isTaSupported() { 673 return mTa; 674 } 675 /** Alternate Frequency Switching is supported 676 * @return {@code true} if AF switching is supported, {@code false} otherwise. 677 */ 678 public boolean isAfSupported() { 679 return mAf; 680 } 681 682 /** Emergency Announcement is supported 683 * @return {@code true} if Emergency annoucement is supported, {@code false} otherwise. 684 */ 685 public boolean isEaSupported() { 686 return mEa; 687 } 688 689 /* Parcelable implementation */ 690 private FmBandDescriptor(Parcel in) { 691 super(in); 692 mStereo = in.readByte() == 1; 693 mRds = in.readByte() == 1; 694 mTa = in.readByte() == 1; 695 mAf = in.readByte() == 1; 696 mEa = in.readByte() == 1; 697 } 698 699 public static final Parcelable.Creator<FmBandDescriptor> CREATOR 700 = new Parcelable.Creator<FmBandDescriptor>() { 701 public FmBandDescriptor createFromParcel(Parcel in) { 702 return new FmBandDescriptor(in); 703 } 704 705 public FmBandDescriptor[] newArray(int size) { 706 return new FmBandDescriptor[size]; 707 } 708 }; 709 710 @Override 711 public void writeToParcel(Parcel dest, int flags) { 712 super.writeToParcel(dest, flags); 713 dest.writeByte((byte) (mStereo ? 1 : 0)); 714 dest.writeByte((byte) (mRds ? 1 : 0)); 715 dest.writeByte((byte) (mTa ? 1 : 0)); 716 dest.writeByte((byte) (mAf ? 1 : 0)); 717 dest.writeByte((byte) (mEa ? 1 : 0)); 718 } 719 720 @Override 721 public int describeContents() { 722 return 0; 723 } 724 725 @Override 726 public String toString() { 727 return "FmBandDescriptor [ "+ super.toString() + " mStereo=" + mStereo 728 + ", mRds=" + mRds + ", mTa=" + mTa + ", mAf=" + mAf + 729 ", mEa =" + mEa + "]"; 730 } 731 732 @Override 733 public int hashCode() { 734 final int prime = 31; 735 int result = super.hashCode(); 736 result = prime * result + (mStereo ? 1 : 0); 737 result = prime * result + (mRds ? 1 : 0); 738 result = prime * result + (mTa ? 1 : 0); 739 result = prime * result + (mAf ? 1 : 0); 740 result = prime * result + (mEa ? 1 : 0); 741 return result; 742 } 743 744 @Override 745 public boolean equals(Object obj) { 746 if (this == obj) 747 return true; 748 if (!super.equals(obj)) 749 return false; 750 if (!(obj instanceof FmBandDescriptor)) 751 return false; 752 FmBandDescriptor other = (FmBandDescriptor) obj; 753 if (mStereo != other.isStereoSupported()) 754 return false; 755 if (mRds != other.isRdsSupported()) 756 return false; 757 if (mTa != other.isTaSupported()) 758 return false; 759 if (mAf != other.isAfSupported()) 760 return false; 761 if (mEa != other.isEaSupported()) 762 return false; 763 return true; 764 } 765 } 766 767 /** AM band descriptor. 768 * @see #BAND_AM */ 769 public static class AmBandDescriptor extends BandDescriptor { 770 771 private final boolean mStereo; 772 773 AmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing, 774 boolean stereo) { 775 super(region, type, lowerLimit, upperLimit, spacing); 776 mStereo = stereo; 777 } 778 779 /** Stereo is supported 780 * @return {@code true} if stereo is supported, {@code false} otherwise. 781 */ 782 public boolean isStereoSupported() { 783 return mStereo; 784 } 785 786 private AmBandDescriptor(Parcel in) { 787 super(in); 788 mStereo = in.readByte() == 1; 789 } 790 791 public static final Parcelable.Creator<AmBandDescriptor> CREATOR 792 = new Parcelable.Creator<AmBandDescriptor>() { 793 public AmBandDescriptor createFromParcel(Parcel in) { 794 return new AmBandDescriptor(in); 795 } 796 797 public AmBandDescriptor[] newArray(int size) { 798 return new AmBandDescriptor[size]; 799 } 800 }; 801 802 @Override 803 public void writeToParcel(Parcel dest, int flags) { 804 super.writeToParcel(dest, flags); 805 dest.writeByte((byte) (mStereo ? 1 : 0)); 806 } 807 808 @Override 809 public int describeContents() { 810 return 0; 811 } 812 813 @Override 814 public String toString() { 815 return "AmBandDescriptor [ "+ super.toString() + " mStereo=" + mStereo + "]"; 816 } 817 818 @Override 819 public int hashCode() { 820 final int prime = 31; 821 int result = super.hashCode(); 822 result = prime * result + (mStereo ? 1 : 0); 823 return result; 824 } 825 826 @Override 827 public boolean equals(Object obj) { 828 if (this == obj) 829 return true; 830 if (!super.equals(obj)) 831 return false; 832 if (!(obj instanceof AmBandDescriptor)) 833 return false; 834 AmBandDescriptor other = (AmBandDescriptor) obj; 835 if (mStereo != other.isStereoSupported()) 836 return false; 837 return true; 838 } 839 } 840 841 842 /** Radio band configuration. */ 843 public static class BandConfig implements Parcelable { 844 845 final BandDescriptor mDescriptor; 846 847 BandConfig(BandDescriptor descriptor) { 848 mDescriptor = descriptor; 849 } 850 851 BandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing) { 852 mDescriptor = new BandDescriptor(region, type, lowerLimit, upperLimit, spacing); 853 } 854 855 private BandConfig(Parcel in) { 856 mDescriptor = new BandDescriptor(in); 857 } 858 859 BandDescriptor getDescriptor() { 860 return mDescriptor; 861 } 862 863 /** Region this band applies to. E.g. {@link #REGION_ITU_1} 864 * @return the region associated with this band. 865 */ 866 public int getRegion() { 867 return mDescriptor.getRegion(); 868 } 869 /** Band type, e.g {@link #BAND_FM}. Defines the subclass this descriptor can be cast to: 870 * <ul> 871 * <li>{@link #BAND_FM} or {@link #BAND_FM_HD} cast to {@link FmBandDescriptor}, </li> 872 * <li>{@link #BAND_AM} cast to {@link AmBandDescriptor}, </li> 873 * </ul> 874 * @return the band type. 875 */ 876 public int getType() { 877 return mDescriptor.getType(); 878 } 879 /** Lower band limit expressed in units according to band type. 880 * Currently all defined band types express channels as frequency in kHz 881 * @return the lower band limit. 882 */ 883 public int getLowerLimit() { 884 return mDescriptor.getLowerLimit(); 885 } 886 /** Upper band limit expressed in units according to band type. 887 * Currently all defined band types express channels as frequency in kHz 888 * @return the upper band limit. 889 */ 890 public int getUpperLimit() { 891 return mDescriptor.getUpperLimit(); 892 } 893 /** Channel spacing in units according to band type. 894 * Currently all defined band types express channels as frequency in kHz 895 * @return the channel spacing. 896 */ 897 public int getSpacing() { 898 return mDescriptor.getSpacing(); 899 } 900 901 902 public static final Parcelable.Creator<BandConfig> CREATOR 903 = new Parcelable.Creator<BandConfig>() { 904 public BandConfig createFromParcel(Parcel in) { 905 int type = BandDescriptor.lookupTypeFromParcel(in); 906 switch (type) { 907 case BAND_FM: 908 case BAND_FM_HD: 909 return new FmBandConfig(in); 910 case BAND_AM: 911 case BAND_AM_HD: 912 return new AmBandConfig(in); 913 default: 914 throw new IllegalArgumentException("Unsupported band: " + type); 915 } 916 } 917 918 public BandConfig[] newArray(int size) { 919 return new BandConfig[size]; 920 } 921 }; 922 923 @Override 924 public void writeToParcel(Parcel dest, int flags) { 925 mDescriptor.writeToParcel(dest, flags); 926 } 927 928 @Override 929 public int describeContents() { 930 return 0; 931 } 932 933 @Override 934 public String toString() { 935 return "BandConfig [ " + mDescriptor.toString() + "]"; 936 } 937 938 @Override 939 public int hashCode() { 940 final int prime = 31; 941 int result = 1; 942 result = prime * result + mDescriptor.hashCode(); 943 return result; 944 } 945 946 @Override 947 public boolean equals(Object obj) { 948 if (this == obj) 949 return true; 950 if (!(obj instanceof BandConfig)) 951 return false; 952 BandConfig other = (BandConfig) obj; 953 BandDescriptor otherDesc = other.getDescriptor(); 954 if ((mDescriptor == null) != (otherDesc == null)) return false; 955 if (mDescriptor != null && !mDescriptor.equals(otherDesc)) return false; 956 return true; 957 } 958 } 959 960 /** FM band configuration. 961 * @see #BAND_FM 962 * @see #BAND_FM_HD */ 963 public static class FmBandConfig extends BandConfig { 964 private final boolean mStereo; 965 private final boolean mRds; 966 private final boolean mTa; 967 private final boolean mAf; 968 private final boolean mEa; 969 970 FmBandConfig(FmBandDescriptor descriptor) { 971 super((BandDescriptor)descriptor); 972 mStereo = descriptor.isStereoSupported(); 973 mRds = descriptor.isRdsSupported(); 974 mTa = descriptor.isTaSupported(); 975 mAf = descriptor.isAfSupported(); 976 mEa = descriptor.isEaSupported(); 977 } 978 979 FmBandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing, 980 boolean stereo, boolean rds, boolean ta, boolean af, boolean ea) { 981 super(region, type, lowerLimit, upperLimit, spacing); 982 mStereo = stereo; 983 mRds = rds; 984 mTa = ta; 985 mAf = af; 986 mEa = ea; 987 } 988 989 /** Get stereo enable state 990 * @return the enable state. 991 */ 992 public boolean getStereo() { 993 return mStereo; 994 } 995 996 /** Get RDS or RBDS(if region is ITU2) enable state 997 * @return the enable state. 998 */ 999 public boolean getRds() { 1000 return mRds; 1001 } 1002 1003 /** Get Traffic announcement enable state 1004 * @return the enable state. 1005 */ 1006 public boolean getTa() { 1007 return mTa; 1008 } 1009 1010 /** Get Alternate Frequency Switching enable state 1011 * @return the enable state. 1012 */ 1013 public boolean getAf() { 1014 return mAf; 1015 } 1016 1017 /** 1018 * Get Emergency announcement enable state 1019 * @return the enable state. 1020 */ 1021 public boolean getEa() { 1022 return mEa; 1023 } 1024 1025 private FmBandConfig(Parcel in) { 1026 super(in); 1027 mStereo = in.readByte() == 1; 1028 mRds = in.readByte() == 1; 1029 mTa = in.readByte() == 1; 1030 mAf = in.readByte() == 1; 1031 mEa = in.readByte() == 1; 1032 } 1033 1034 public static final Parcelable.Creator<FmBandConfig> CREATOR 1035 = new Parcelable.Creator<FmBandConfig>() { 1036 public FmBandConfig createFromParcel(Parcel in) { 1037 return new FmBandConfig(in); 1038 } 1039 1040 public FmBandConfig[] newArray(int size) { 1041 return new FmBandConfig[size]; 1042 } 1043 }; 1044 1045 @Override 1046 public void writeToParcel(Parcel dest, int flags) { 1047 super.writeToParcel(dest, flags); 1048 dest.writeByte((byte) (mStereo ? 1 : 0)); 1049 dest.writeByte((byte) (mRds ? 1 : 0)); 1050 dest.writeByte((byte) (mTa ? 1 : 0)); 1051 dest.writeByte((byte) (mAf ? 1 : 0)); 1052 dest.writeByte((byte) (mEa ? 1 : 0)); 1053 } 1054 1055 @Override 1056 public int describeContents() { 1057 return 0; 1058 } 1059 1060 @Override 1061 public String toString() { 1062 return "FmBandConfig [" + super.toString() 1063 + ", mStereo=" + mStereo + ", mRds=" + mRds + ", mTa=" + mTa 1064 + ", mAf=" + mAf + ", mEa =" + mEa + "]"; 1065 } 1066 1067 @Override 1068 public int hashCode() { 1069 final int prime = 31; 1070 int result = super.hashCode(); 1071 result = prime * result + (mStereo ? 1 : 0); 1072 result = prime * result + (mRds ? 1 : 0); 1073 result = prime * result + (mTa ? 1 : 0); 1074 result = prime * result + (mAf ? 1 : 0); 1075 result = prime * result + (mEa ? 1 : 0); 1076 return result; 1077 } 1078 1079 @Override 1080 public boolean equals(Object obj) { 1081 if (this == obj) 1082 return true; 1083 if (!super.equals(obj)) 1084 return false; 1085 if (!(obj instanceof FmBandConfig)) 1086 return false; 1087 FmBandConfig other = (FmBandConfig) obj; 1088 if (mStereo != other.mStereo) 1089 return false; 1090 if (mRds != other.mRds) 1091 return false; 1092 if (mTa != other.mTa) 1093 return false; 1094 if (mAf != other.mAf) 1095 return false; 1096 if (mEa != other.mEa) 1097 return false; 1098 return true; 1099 } 1100 1101 /** 1102 * Builder class for {@link FmBandConfig} objects. 1103 */ 1104 public static class Builder { 1105 private final BandDescriptor mDescriptor; 1106 private boolean mStereo; 1107 private boolean mRds; 1108 private boolean mTa; 1109 private boolean mAf; 1110 private boolean mEa; 1111 1112 /** 1113 * Constructs a new Builder with the defaults from an {@link FmBandDescriptor} . 1114 * @param descriptor the FmBandDescriptor defaults are read from . 1115 */ 1116 public Builder(FmBandDescriptor descriptor) { 1117 mDescriptor = new BandDescriptor(descriptor.getRegion(), descriptor.getType(), 1118 descriptor.getLowerLimit(), descriptor.getUpperLimit(), 1119 descriptor.getSpacing()); 1120 mStereo = descriptor.isStereoSupported(); 1121 mRds = descriptor.isRdsSupported(); 1122 mTa = descriptor.isTaSupported(); 1123 mAf = descriptor.isAfSupported(); 1124 mEa = descriptor.isEaSupported(); 1125 } 1126 1127 /** 1128 * Constructs a new Builder from a given {@link FmBandConfig} 1129 * @param config the FmBandConfig object whose data will be reused in the new Builder. 1130 */ 1131 public Builder(FmBandConfig config) { 1132 mDescriptor = new BandDescriptor(config.getRegion(), config.getType(), 1133 config.getLowerLimit(), config.getUpperLimit(), config.getSpacing()); 1134 mStereo = config.getStereo(); 1135 mRds = config.getRds(); 1136 mTa = config.getTa(); 1137 mAf = config.getAf(); 1138 mEa = config.getEa(); 1139 } 1140 1141 /** 1142 * Combines all of the parameters that have been set and return a new 1143 * {@link FmBandConfig} object. 1144 * @return a new {@link FmBandConfig} object 1145 */ 1146 public FmBandConfig build() { 1147 FmBandConfig config = new FmBandConfig(mDescriptor.getRegion(), 1148 mDescriptor.getType(), mDescriptor.getLowerLimit(), 1149 mDescriptor.getUpperLimit(), mDescriptor.getSpacing(), 1150 mStereo, mRds, mTa, mAf, mEa); 1151 return config; 1152 } 1153 1154 /** Set stereo enable state 1155 * @param state The new enable state. 1156 * @return the same Builder instance. 1157 */ 1158 public Builder setStereo(boolean state) { 1159 mStereo = state; 1160 return this; 1161 } 1162 1163 /** Set RDS or RBDS(if region is ITU2) enable state 1164 * @param state The new enable state. 1165 * @return the same Builder instance. 1166 */ 1167 public Builder setRds(boolean state) { 1168 mRds = state; 1169 return this; 1170 } 1171 1172 /** Set Traffic announcement enable state 1173 * @param state The new enable state. 1174 * @return the same Builder instance. 1175 */ 1176 public Builder setTa(boolean state) { 1177 mTa = state; 1178 return this; 1179 } 1180 1181 /** Set Alternate Frequency Switching enable state 1182 * @param state The new enable state. 1183 * @return the same Builder instance. 1184 */ 1185 public Builder setAf(boolean state) { 1186 mAf = state; 1187 return this; 1188 } 1189 1190 /** Set Emergency Announcement enable state 1191 * @param state The new enable state. 1192 * @return the same Builder instance. 1193 */ 1194 public Builder setEa(boolean state) { 1195 mEa = state; 1196 return this; 1197 } 1198 }; 1199 } 1200 1201 /** AM band configuration. 1202 * @see #BAND_AM */ 1203 public static class AmBandConfig extends BandConfig { 1204 private final boolean mStereo; 1205 1206 AmBandConfig(AmBandDescriptor descriptor) { 1207 super((BandDescriptor)descriptor); 1208 mStereo = descriptor.isStereoSupported(); 1209 } 1210 1211 AmBandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing, 1212 boolean stereo) { 1213 super(region, type, lowerLimit, upperLimit, spacing); 1214 mStereo = stereo; 1215 } 1216 1217 /** Get stereo enable state 1218 * @return the enable state. 1219 */ 1220 public boolean getStereo() { 1221 return mStereo; 1222 } 1223 1224 private AmBandConfig(Parcel in) { 1225 super(in); 1226 mStereo = in.readByte() == 1; 1227 } 1228 1229 public static final Parcelable.Creator<AmBandConfig> CREATOR 1230 = new Parcelable.Creator<AmBandConfig>() { 1231 public AmBandConfig createFromParcel(Parcel in) { 1232 return new AmBandConfig(in); 1233 } 1234 1235 public AmBandConfig[] newArray(int size) { 1236 return new AmBandConfig[size]; 1237 } 1238 }; 1239 1240 @Override 1241 public void writeToParcel(Parcel dest, int flags) { 1242 super.writeToParcel(dest, flags); 1243 dest.writeByte((byte) (mStereo ? 1 : 0)); 1244 } 1245 1246 @Override 1247 public int describeContents() { 1248 return 0; 1249 } 1250 1251 @Override 1252 public String toString() { 1253 return "AmBandConfig [" + super.toString() 1254 + ", mStereo=" + mStereo + "]"; 1255 } 1256 1257 @Override 1258 public int hashCode() { 1259 final int prime = 31; 1260 int result = super.hashCode(); 1261 result = prime * result + (mStereo ? 1 : 0); 1262 return result; 1263 } 1264 1265 @Override 1266 public boolean equals(Object obj) { 1267 if (this == obj) 1268 return true; 1269 if (!super.equals(obj)) 1270 return false; 1271 if (!(obj instanceof AmBandConfig)) 1272 return false; 1273 AmBandConfig other = (AmBandConfig) obj; 1274 if (mStereo != other.getStereo()) 1275 return false; 1276 return true; 1277 } 1278 1279 /** 1280 * Builder class for {@link AmBandConfig} objects. 1281 */ 1282 public static class Builder { 1283 private final BandDescriptor mDescriptor; 1284 private boolean mStereo; 1285 1286 /** 1287 * Constructs a new Builder with the defaults from an {@link AmBandDescriptor} . 1288 * @param descriptor the FmBandDescriptor defaults are read from . 1289 */ 1290 public Builder(AmBandDescriptor descriptor) { 1291 mDescriptor = new BandDescriptor(descriptor.getRegion(), descriptor.getType(), 1292 descriptor.getLowerLimit(), descriptor.getUpperLimit(), 1293 descriptor.getSpacing()); 1294 mStereo = descriptor.isStereoSupported(); 1295 } 1296 1297 /** 1298 * Constructs a new Builder from a given {@link AmBandConfig} 1299 * @param config the FmBandConfig object whose data will be reused in the new Builder. 1300 */ 1301 public Builder(AmBandConfig config) { 1302 mDescriptor = new BandDescriptor(config.getRegion(), config.getType(), 1303 config.getLowerLimit(), config.getUpperLimit(), config.getSpacing()); 1304 mStereo = config.getStereo(); 1305 } 1306 1307 /** 1308 * Combines all of the parameters that have been set and return a new 1309 * {@link AmBandConfig} object. 1310 * @return a new {@link AmBandConfig} object 1311 */ 1312 public AmBandConfig build() { 1313 AmBandConfig config = new AmBandConfig(mDescriptor.getRegion(), 1314 mDescriptor.getType(), mDescriptor.getLowerLimit(), 1315 mDescriptor.getUpperLimit(), mDescriptor.getSpacing(), 1316 mStereo); 1317 return config; 1318 } 1319 1320 /** Set stereo enable state 1321 * @param state The new enable state. 1322 * @return the same Builder instance. 1323 */ 1324 public Builder setStereo(boolean state) { 1325 mStereo = state; 1326 return this; 1327 } 1328 }; 1329 } 1330 1331 /** Radio program information returned by 1332 * {@link RadioTuner#getProgramInformation(RadioManager.ProgramInfo[])} */ 1333 public static class ProgramInfo implements Parcelable { 1334 1335 // sourced from hardware/interfaces/broadcastradio/1.1/types.hal 1336 private static final int FLAG_LIVE = 1 << 0; 1337 private static final int FLAG_MUTED = 1 << 1; 1338 private static final int FLAG_TRAFFIC_PROGRAM = 1 << 2; 1339 private static final int FLAG_TRAFFIC_ANNOUNCEMENT = 1 << 3; 1340 1341 @NonNull private final ProgramSelector mSelector; 1342 private final boolean mTuned; 1343 private final boolean mStereo; 1344 private final boolean mDigital; 1345 private final int mFlags; 1346 private final int mSignalStrength; 1347 private final RadioMetadata mMetadata; 1348 @NonNull private final Map<String, String> mVendorInfo; 1349 1350 ProgramInfo(@NonNull ProgramSelector selector, boolean tuned, boolean stereo, 1351 boolean digital, int signalStrength, RadioMetadata metadata, int flags, 1352 Map<String, String> vendorInfo) { 1353 mSelector = selector; 1354 mTuned = tuned; 1355 mStereo = stereo; 1356 mDigital = digital; 1357 mFlags = flags; 1358 mSignalStrength = signalStrength; 1359 mMetadata = metadata; 1360 mVendorInfo = (vendorInfo == null) ? new HashMap<>() : vendorInfo; 1361 } 1362 1363 /** 1364 * Program selector, necessary for tuning to a program. 1365 * 1366 * @return the program selector. 1367 */ 1368 public @NonNull ProgramSelector getSelector() { 1369 return mSelector; 1370 } 1371 1372 /** Main channel expressed in units according to band type. 1373 * Currently all defined band types express channels as frequency in kHz 1374 * @return the program channel 1375 * @deprecated Use {@link getSelector()} instead. 1376 */ 1377 @Deprecated 1378 public int getChannel() { 1379 try { 1380 return (int) mSelector.getFirstId(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY); 1381 } catch (IllegalArgumentException ex) { 1382 Log.w(TAG, "Not an AM/FM program"); 1383 return 0; 1384 } 1385 } 1386 1387 /** Sub channel ID. E.g 1 for HD radio HD1 1388 * @return the program sub channel 1389 * @deprecated Use {@link getSelector()} instead. 1390 */ 1391 @Deprecated 1392 public int getSubChannel() { 1393 try { 1394 return (int) mSelector.getFirstId( 1395 ProgramSelector.IDENTIFIER_TYPE_HD_SUBCHANNEL) + 1; 1396 } catch (IllegalArgumentException ex) { 1397 // this is a normal behavior for analog AM/FM selector 1398 return 0; 1399 } 1400 } 1401 1402 /** {@code true} if the tuner is currently tuned on a valid station 1403 * @return {@code true} if currently tuned, {@code false} otherwise. 1404 */ 1405 public boolean isTuned() { 1406 return mTuned; 1407 } 1408 /** {@code true} if the received program is stereo 1409 * @return {@code true} if stereo, {@code false} otherwise. 1410 */ 1411 public boolean isStereo() { 1412 return mStereo; 1413 } 1414 /** {@code true} if the received program is digital (e.g HD radio) 1415 * @return {@code true} if digital, {@code false} otherwise. 1416 */ 1417 public boolean isDigital() { 1418 return mDigital; 1419 } 1420 1421 /** 1422 * {@code true} if the program is currently playing live stream. 1423 * This may result in a slightly altered reception parameters, 1424 * usually targetted at reduced latency. 1425 */ 1426 public boolean isLive() { 1427 return (mFlags & FLAG_LIVE) != 0; 1428 } 1429 1430 /** 1431 * {@code true} if radio stream is not playing, ie. due to bad reception 1432 * conditions or buffering. In this state volume knob MAY be disabled to 1433 * prevent user increasing volume too much. 1434 * It does NOT mean the user has muted audio. 1435 */ 1436 public boolean isMuted() { 1437 return (mFlags & FLAG_MUTED) != 0; 1438 } 1439 1440 /** 1441 * {@code true} if radio station transmits traffic information 1442 * regularily. 1443 */ 1444 public boolean isTrafficProgram() { 1445 return (mFlags & FLAG_TRAFFIC_PROGRAM) != 0; 1446 } 1447 1448 /** 1449 * {@code true} if radio station transmits traffic information 1450 * at the very moment. 1451 */ 1452 public boolean isTrafficAnnouncementActive() { 1453 return (mFlags & FLAG_TRAFFIC_ANNOUNCEMENT) != 0; 1454 } 1455 1456 /** Signal strength indicator from 0 (no signal) to 100 (excellent) 1457 * @return the signal strength indication. 1458 */ 1459 public int getSignalStrength() { 1460 return mSignalStrength; 1461 } 1462 /** Metadata currently received from this station. 1463 * null if no metadata have been received 1464 * @return current meta data received from this program. 1465 */ 1466 public RadioMetadata getMetadata() { 1467 return mMetadata; 1468 } 1469 1470 /** 1471 * A map of vendor-specific opaque strings, passed from HAL without changes. 1472 * Format of these strings can vary across vendors. 1473 * 1474 * It may be used for extra features, that's not supported by a platform, 1475 * for example: paid-service=true; bitrate=320kbps. 1476 * 1477 * Keys must be prefixed with unique vendor Java-style namespace, 1478 * eg. 'com.somecompany.parameter1'. 1479 */ 1480 public @NonNull Map<String, String> getVendorInfo() { 1481 return mVendorInfo; 1482 } 1483 1484 private ProgramInfo(Parcel in) { 1485 mSelector = in.readParcelable(null); 1486 mTuned = in.readByte() == 1; 1487 mStereo = in.readByte() == 1; 1488 mDigital = in.readByte() == 1; 1489 mSignalStrength = in.readInt(); 1490 if (in.readByte() == 1) { 1491 mMetadata = RadioMetadata.CREATOR.createFromParcel(in); 1492 } else { 1493 mMetadata = null; 1494 } 1495 mFlags = in.readInt(); 1496 mVendorInfo = readStringMap(in); 1497 } 1498 1499 public static final Parcelable.Creator<ProgramInfo> CREATOR 1500 = new Parcelable.Creator<ProgramInfo>() { 1501 public ProgramInfo createFromParcel(Parcel in) { 1502 return new ProgramInfo(in); 1503 } 1504 1505 public ProgramInfo[] newArray(int size) { 1506 return new ProgramInfo[size]; 1507 } 1508 }; 1509 1510 @Override 1511 public void writeToParcel(Parcel dest, int flags) { 1512 dest.writeParcelable(mSelector, 0); 1513 dest.writeByte((byte)(mTuned ? 1 : 0)); 1514 dest.writeByte((byte)(mStereo ? 1 : 0)); 1515 dest.writeByte((byte)(mDigital ? 1 : 0)); 1516 dest.writeInt(mSignalStrength); 1517 if (mMetadata == null) { 1518 dest.writeByte((byte)0); 1519 } else { 1520 dest.writeByte((byte)1); 1521 mMetadata.writeToParcel(dest, flags); 1522 } 1523 dest.writeInt(mFlags); 1524 writeStringMap(dest, mVendorInfo); 1525 } 1526 1527 @Override 1528 public int describeContents() { 1529 return 0; 1530 } 1531 1532 @Override 1533 public String toString() { 1534 return "ProgramInfo [mSelector=" + mSelector 1535 + ", mTuned=" + mTuned + ", mStereo=" + mStereo + ", mDigital=" + mDigital 1536 + ", mFlags=" + mFlags + ", mSignalStrength=" + mSignalStrength 1537 + ((mMetadata == null) ? "" : (", mMetadata=" + mMetadata.toString())) 1538 + "]"; 1539 } 1540 1541 @Override 1542 public int hashCode() { 1543 final int prime = 31; 1544 int result = 1; 1545 result = prime * result + mSelector.hashCode(); 1546 result = prime * result + (mTuned ? 1 : 0); 1547 result = prime * result + (mStereo ? 1 : 0); 1548 result = prime * result + (mDigital ? 1 : 0); 1549 result = prime * result + mFlags; 1550 result = prime * result + mSignalStrength; 1551 result = prime * result + ((mMetadata == null) ? 0 : mMetadata.hashCode()); 1552 result = prime * result + mVendorInfo.hashCode(); 1553 return result; 1554 } 1555 1556 @Override 1557 public boolean equals(Object obj) { 1558 if (this == obj) 1559 return true; 1560 if (!(obj instanceof ProgramInfo)) 1561 return false; 1562 ProgramInfo other = (ProgramInfo) obj; 1563 if (!mSelector.equals(other.getSelector())) return false; 1564 if (mTuned != other.isTuned()) 1565 return false; 1566 if (mStereo != other.isStereo()) 1567 return false; 1568 if (mDigital != other.isDigital()) 1569 return false; 1570 if (mFlags != other.mFlags) 1571 return false; 1572 if (mSignalStrength != other.getSignalStrength()) 1573 return false; 1574 if (mMetadata == null) { 1575 if (other.getMetadata() != null) 1576 return false; 1577 } else if (!mMetadata.equals(other.getMetadata())) 1578 return false; 1579 if (!mVendorInfo.equals(other.mVendorInfo)) return false; 1580 return true; 1581 } 1582 } 1583 1584 1585 /** 1586 * Returns a list of descriptors for all broadcast radio modules present on the device. 1587 * @param modules An List of {@link ModuleProperties} where the list will be returned. 1588 * @return 1589 * <ul> 1590 * <li>{@link #STATUS_OK} in case of success, </li> 1591 * <li>{@link #STATUS_ERROR} in case of unspecified error, </li> 1592 * <li>{@link #STATUS_NO_INIT} if the native service cannot be reached, </li> 1593 * <li>{@link #STATUS_BAD_VALUE} if modules is null, </li> 1594 * <li>{@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails, </li> 1595 * </ul> 1596 */ 1597 @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) 1598 public int listModules(List<ModuleProperties> modules) { 1599 if (modules == null) { 1600 Log.e(TAG, "the output list must not be empty"); 1601 return STATUS_BAD_VALUE; 1602 } 1603 1604 Log.d(TAG, "Listing available tuners..."); 1605 List<ModuleProperties> returnedList; 1606 try { 1607 returnedList = mService.listModules(); 1608 } catch (RemoteException e) { 1609 Log.e(TAG, "Failed listing available tuners", e); 1610 return STATUS_DEAD_OBJECT; 1611 } 1612 1613 if (returnedList == null) { 1614 Log.e(TAG, "Returned list was a null"); 1615 return STATUS_ERROR; 1616 } 1617 1618 modules.addAll(returnedList); 1619 return STATUS_OK; 1620 } 1621 1622 private native int nativeListModules(List<ModuleProperties> modules); 1623 1624 /** 1625 * Open an interface to control a tuner on a given broadcast radio module. 1626 * Optionally selects and applies the configuration passed as "config" argument. 1627 * @param moduleId radio module identifier {@link ModuleProperties#getId()}. Mandatory. 1628 * @param config desired band and configuration to apply when enabling the hardware module. 1629 * optional, can be null. 1630 * @param withAudio {@code true} to request a tuner with an audio source. 1631 * This tuner is intended for live listening or recording or a radio program. 1632 * If {@code false}, the tuner can only be used to retrieve program informations. 1633 * @param callback {@link RadioTuner.Callback} interface. Mandatory. 1634 * @param handler the Handler on which the callbacks will be received. 1635 * Can be null if default handler is OK. 1636 * @return a valid {@link RadioTuner} interface in case of success or null in case of error. 1637 */ 1638 @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO) 1639 public RadioTuner openTuner(int moduleId, BandConfig config, boolean withAudio, 1640 RadioTuner.Callback callback, Handler handler) { 1641 if (callback == null) { 1642 throw new IllegalArgumentException("callback must not be empty"); 1643 } 1644 1645 Log.d(TAG, "Opening tuner " + moduleId + "..."); 1646 1647 ITuner tuner; 1648 TunerCallbackAdapter halCallback = new TunerCallbackAdapter(callback, handler); 1649 try { 1650 tuner = mService.openTuner(moduleId, config, withAudio, halCallback); 1651 } catch (RemoteException e) { 1652 Log.e(TAG, "Failed to open tuner", e); 1653 return null; 1654 } 1655 if (tuner == null) { 1656 Log.e(TAG, "Failed to open tuner"); 1657 return null; 1658 } 1659 return new TunerAdapter(tuner, config != null ? config.getType() : BAND_INVALID); 1660 } 1661 1662 @NonNull private final Context mContext; 1663 @NonNull private final IRadioService mService; 1664 1665 /** 1666 * @hide 1667 */ 1668 public RadioManager(@NonNull Context context) throws ServiceNotFoundException { 1669 mContext = context; 1670 mService = IRadioService.Stub.asInterface( 1671 ServiceManager.getServiceOrThrow(Context.RADIO_SERVICE)); 1672 } 1673} 1674