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