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