• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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                     BandDescriptor.class);
441             mBands = new BandDescriptor[tmp.length];
442             for (int i = 0; i < tmp.length; i++) {
443                 mBands[i] = (BandDescriptor) tmp[i];
444             }
445             mIsBgScanSupported = in.readInt() == 1;
446             mSupportedProgramTypes = arrayToSet(in.createIntArray());
447             mSupportedIdentifierTypes = arrayToSet(in.createIntArray());
448             mDabFrequencyTable = Utils.readStringIntMap(in);
449             mVendorInfo = Utils.readStringMap(in);
450         }
451 
452         public static final @android.annotation.NonNull Parcelable.Creator<ModuleProperties> CREATOR
453                 = new Parcelable.Creator<ModuleProperties>() {
454             public ModuleProperties createFromParcel(Parcel in) {
455                 return new ModuleProperties(in);
456             }
457 
458             public ModuleProperties[] newArray(int size) {
459                 return new ModuleProperties[size];
460             }
461         };
462 
463         @Override
writeToParcel(Parcel dest, int flags)464         public void writeToParcel(Parcel dest, int flags) {
465             dest.writeInt(mId);
466             dest.writeString(mServiceName);
467             dest.writeInt(mClassId);
468             dest.writeString(mImplementor);
469             dest.writeString(mProduct);
470             dest.writeString(mVersion);
471             dest.writeString(mSerial);
472             dest.writeInt(mNumTuners);
473             dest.writeInt(mNumAudioSources);
474             dest.writeInt(mIsInitializationRequired ? 1 : 0);
475             dest.writeInt(mIsCaptureSupported ? 1 : 0);
476             dest.writeParcelableArray(mBands, flags);
477             dest.writeInt(mIsBgScanSupported ? 1 : 0);
478             dest.writeIntArray(setToArray(mSupportedProgramTypes));
479             dest.writeIntArray(setToArray(mSupportedIdentifierTypes));
480             Utils.writeStringIntMap(dest, mDabFrequencyTable);
481             Utils.writeStringMap(dest, mVendorInfo);
482         }
483 
484         @Override
describeContents()485         public int describeContents() {
486             return 0;
487         }
488 
489         @NonNull
490         @Override
toString()491         public String toString() {
492             return "ModuleProperties [mId=" + mId
493                     + ", mServiceName=" + mServiceName + ", mClassId=" + mClassId
494                     + ", mImplementor=" + mImplementor + ", mProduct=" + mProduct
495                     + ", mVersion=" + mVersion + ", mSerial=" + mSerial
496                     + ", mNumTuners=" + mNumTuners
497                     + ", mNumAudioSources=" + mNumAudioSources
498                     + ", mIsInitializationRequired=" + mIsInitializationRequired
499                     + ", mIsCaptureSupported=" + mIsCaptureSupported
500                     + ", mIsBgScanSupported=" + mIsBgScanSupported
501                     + ", mBands=" + Arrays.toString(mBands) + "]";
502         }
503 
504         @Override
hashCode()505         public int hashCode() {
506             return Objects.hash(mId, mServiceName, mClassId, mImplementor, mProduct, mVersion,
507                 mSerial, mNumTuners, mNumAudioSources, mIsInitializationRequired,
508                 mIsCaptureSupported, mBands, mIsBgScanSupported, mDabFrequencyTable, mVendorInfo);
509         }
510 
511         @Override
equals(@ullable Object obj)512         public boolean equals(@Nullable Object obj) {
513             if (this == obj) return true;
514             if (!(obj instanceof ModuleProperties)) return false;
515             ModuleProperties other = (ModuleProperties) obj;
516 
517             if (mId != other.getId()) return false;
518             if (!TextUtils.equals(mServiceName, other.mServiceName)) return false;
519             if (mClassId != other.mClassId) return false;
520             if (!Objects.equals(mImplementor, other.mImplementor)) return false;
521             if (!Objects.equals(mProduct, other.mProduct)) return false;
522             if (!Objects.equals(mVersion, other.mVersion)) return false;
523             if (!Objects.equals(mSerial, other.mSerial)) return false;
524             if (mNumTuners != other.mNumTuners) return false;
525             if (mNumAudioSources != other.mNumAudioSources) return false;
526             if (mIsInitializationRequired != other.mIsInitializationRequired) return false;
527             if (mIsCaptureSupported != other.mIsCaptureSupported) return false;
528             if (!Objects.equals(mBands, other.mBands)) return false;
529             if (mIsBgScanSupported != other.mIsBgScanSupported) return false;
530             if (!Objects.equals(mDabFrequencyTable, other.mDabFrequencyTable)) return false;
531             if (!Objects.equals(mVendorInfo, other.mVendorInfo)) return false;
532             return true;
533         }
534     }
535 
536     /** Radio band descriptor: an element in ModuleProperties bands array.
537      * It is either an instance of {@link FmBandDescriptor} or {@link AmBandDescriptor} */
538     public static class BandDescriptor implements Parcelable {
539 
540         private final int mRegion;
541         private final int mType;
542         private final int mLowerLimit;
543         private final int mUpperLimit;
544         private final int mSpacing;
545 
BandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing)546         BandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing) {
547             if (type != BAND_AM && type != BAND_FM && type != BAND_FM_HD && type != BAND_AM_HD) {
548                 throw new IllegalArgumentException("Unsupported band: " + type);
549             }
550             mRegion = region;
551             mType = type;
552             mLowerLimit = lowerLimit;
553             mUpperLimit = upperLimit;
554             mSpacing = spacing;
555         }
556 
557         /** Region this band applies to. E.g. {@link #REGION_ITU_1}
558          * @return the region this band is associated to.
559          */
getRegion()560         public int getRegion() {
561             return mRegion;
562         }
563         /** Band type, e.g {@link #BAND_FM}. Defines the subclass this descriptor can be cast to:
564          * <ul>
565          *  <li>{@link #BAND_FM} or {@link #BAND_FM_HD} cast to {@link FmBandDescriptor}, </li>
566          *  <li>{@link #BAND_AM} cast to {@link AmBandDescriptor}, </li>
567          * </ul>
568          * @return the band type.
569          */
getType()570         public int getType() {
571             return mType;
572         }
573 
574         /**
575          * Checks if the band is either AM or AM_HD.
576          *
577          * @return {@code true}, if band is AM or AM_HD.
578          */
isAmBand()579         public boolean isAmBand() {
580             return mType == BAND_AM || mType == BAND_AM_HD;
581         }
582 
583         /**
584          * Checks if the band is either FM or FM_HD.
585          *
586          * @return {@code true}, if band is FM or FM_HD.
587          */
isFmBand()588         public boolean isFmBand() {
589             return mType == BAND_FM || mType == BAND_FM_HD;
590         }
591 
592         /** Lower band limit expressed in units according to band type.
593          * Currently all defined band types express channels as frequency in kHz
594          * @return the lower band limit.
595          */
getLowerLimit()596         public int getLowerLimit() {
597             return mLowerLimit;
598         }
599         /** Upper band limit expressed in units according to band type.
600          * Currently all defined band types express channels as frequency in kHz
601          * @return the upper band limit.
602          */
getUpperLimit()603         public int getUpperLimit() {
604             return mUpperLimit;
605         }
606         /** Channel spacing in units according to band type.
607          * Currently all defined band types express channels as frequency in kHz
608          * @return the channel spacing.
609          */
getSpacing()610         public int getSpacing() {
611             return mSpacing;
612         }
613 
BandDescriptor(Parcel in)614         private BandDescriptor(Parcel in) {
615             mRegion = in.readInt();
616             mType = in.readInt();
617             mLowerLimit = in.readInt();
618             mUpperLimit = in.readInt();
619             mSpacing = in.readInt();
620         }
621 
lookupTypeFromParcel(Parcel in)622         private static int lookupTypeFromParcel(Parcel in) {
623             int pos = in.dataPosition();
624             in.readInt();  // skip region
625             int type = in.readInt();
626             in.setDataPosition(pos);
627             return type;
628         }
629 
630         public static final @android.annotation.NonNull Parcelable.Creator<BandDescriptor> CREATOR
631                 = new Parcelable.Creator<BandDescriptor>() {
632             public BandDescriptor createFromParcel(Parcel in) {
633                 int type = lookupTypeFromParcel(in);
634                 switch (type) {
635                     case BAND_FM:
636                     case BAND_FM_HD:
637                         return new FmBandDescriptor(in);
638                     case BAND_AM:
639                     case BAND_AM_HD:
640                         return new AmBandDescriptor(in);
641                     default:
642                         throw new IllegalArgumentException("Unsupported band: " + type);
643                 }
644             }
645 
646             public BandDescriptor[] newArray(int size) {
647                 return new BandDescriptor[size];
648             }
649         };
650 
651         @Override
writeToParcel(Parcel dest, int flags)652         public void writeToParcel(Parcel dest, int flags) {
653             dest.writeInt(mRegion);
654             dest.writeInt(mType);
655             dest.writeInt(mLowerLimit);
656             dest.writeInt(mUpperLimit);
657             dest.writeInt(mSpacing);
658         }
659 
660         @Override
describeContents()661         public int describeContents() {
662             return 0;
663         }
664 
665         @NonNull
666         @Override
toString()667         public String toString() {
668             return "BandDescriptor [mRegion=" + mRegion + ", mType=" + mType + ", mLowerLimit="
669                     + mLowerLimit + ", mUpperLimit=" + mUpperLimit + ", mSpacing=" + mSpacing + "]";
670         }
671 
672         @Override
hashCode()673         public int hashCode() {
674             final int prime = 31;
675             int result = 1;
676             result = prime * result + mRegion;
677             result = prime * result + mType;
678             result = prime * result + mLowerLimit;
679             result = prime * result + mUpperLimit;
680             result = prime * result + mSpacing;
681             return result;
682         }
683 
684         @Override
equals(@ullable Object obj)685         public boolean equals(@Nullable Object obj) {
686             if (this == obj)
687                 return true;
688             if (!(obj instanceof BandDescriptor))
689                 return false;
690             BandDescriptor other = (BandDescriptor) obj;
691             if (mRegion != other.getRegion())
692                 return false;
693             if (mType != other.getType())
694                 return false;
695             if (mLowerLimit != other.getLowerLimit())
696                 return false;
697             if (mUpperLimit != other.getUpperLimit())
698                 return false;
699             if (mSpacing != other.getSpacing())
700                 return false;
701             return true;
702         }
703     }
704 
705     /** FM band descriptor
706      * @see #BAND_FM
707      * @see #BAND_FM_HD */
708     public static class FmBandDescriptor extends BandDescriptor {
709         private final boolean mStereo;
710         private final boolean mRds;
711         private final boolean mTa;
712         private final boolean mAf;
713         private final boolean mEa;
714 
715         /** @hide */
FmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing, boolean stereo, boolean rds, boolean ta, boolean af, boolean ea)716         public FmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing,
717                 boolean stereo, boolean rds, boolean ta, boolean af, boolean ea) {
718             super(region, type, lowerLimit, upperLimit, spacing);
719             mStereo = stereo;
720             mRds = rds;
721             mTa = ta;
722             mAf = af;
723             mEa = ea;
724         }
725 
726         /** Stereo is supported
727          * @return {@code true} if stereo is supported, {@code false} otherwise.
728          */
isStereoSupported()729         public boolean isStereoSupported() {
730             return mStereo;
731         }
732         /** RDS or RBDS(if region is ITU2) is supported
733          * @return {@code true} if RDS or RBDS is supported, {@code false} otherwise.
734          */
isRdsSupported()735         public boolean isRdsSupported() {
736             return mRds;
737         }
738         /** Traffic announcement is supported
739          * @return {@code true} if TA is supported, {@code false} otherwise.
740          */
isTaSupported()741         public boolean isTaSupported() {
742             return mTa;
743         }
744         /** Alternate Frequency Switching is supported
745          * @return {@code true} if AF switching is supported, {@code false} otherwise.
746          */
isAfSupported()747         public boolean isAfSupported() {
748             return mAf;
749         }
750 
751         /** Emergency Announcement is supported
752          * @return {@code true} if Emergency annoucement is supported, {@code false} otherwise.
753          */
isEaSupported()754         public boolean isEaSupported() {
755             return mEa;
756         }
757 
758         /* Parcelable implementation */
FmBandDescriptor(Parcel in)759         private FmBandDescriptor(Parcel in) {
760             super(in);
761             mStereo = in.readByte() == 1;
762             mRds = in.readByte() == 1;
763             mTa = in.readByte() == 1;
764             mAf = in.readByte() == 1;
765             mEa = in.readByte() == 1;
766         }
767 
768         public static final @android.annotation.NonNull Parcelable.Creator<FmBandDescriptor> CREATOR
769                 = new Parcelable.Creator<FmBandDescriptor>() {
770             public FmBandDescriptor createFromParcel(Parcel in) {
771                 return new FmBandDescriptor(in);
772             }
773 
774             public FmBandDescriptor[] newArray(int size) {
775                 return new FmBandDescriptor[size];
776             }
777         };
778 
779         @Override
writeToParcel(Parcel dest, int flags)780         public void writeToParcel(Parcel dest, int flags) {
781             super.writeToParcel(dest, flags);
782             dest.writeByte((byte) (mStereo ? 1 : 0));
783             dest.writeByte((byte) (mRds ? 1 : 0));
784             dest.writeByte((byte) (mTa ? 1 : 0));
785             dest.writeByte((byte) (mAf ? 1 : 0));
786             dest.writeByte((byte) (mEa ? 1 : 0));
787         }
788 
789         @Override
describeContents()790         public int describeContents() {
791             return 0;
792         }
793 
794         @NonNull
795         @Override
toString()796         public String toString() {
797             return "FmBandDescriptor [ "+ super.toString() + " mStereo=" + mStereo
798                     + ", mRds=" + mRds + ", mTa=" + mTa + ", mAf=" + mAf +
799                     ", mEa =" + mEa + "]";
800         }
801 
802         @Override
hashCode()803         public int hashCode() {
804             final int prime = 31;
805             int result = super.hashCode();
806             result = prime * result + (mStereo ? 1 : 0);
807             result = prime * result + (mRds ? 1 : 0);
808             result = prime * result + (mTa ? 1 : 0);
809             result = prime * result + (mAf ? 1 : 0);
810             result = prime * result + (mEa ? 1 : 0);
811             return result;
812         }
813 
814         @Override
equals(@ullable Object obj)815         public boolean equals(@Nullable Object obj) {
816             if (this == obj)
817                 return true;
818             if (!super.equals(obj))
819                 return false;
820             if (!(obj instanceof FmBandDescriptor))
821                 return false;
822             FmBandDescriptor other = (FmBandDescriptor) obj;
823             if (mStereo != other.isStereoSupported())
824                 return false;
825             if (mRds != other.isRdsSupported())
826                 return false;
827             if (mTa != other.isTaSupported())
828                 return false;
829             if (mAf != other.isAfSupported())
830                 return false;
831             if (mEa != other.isEaSupported())
832                 return false;
833             return true;
834         }
835     }
836 
837     /** AM band descriptor.
838      * @see #BAND_AM */
839     public static class AmBandDescriptor extends BandDescriptor {
840 
841         private final boolean mStereo;
842 
843         /** @hide */
AmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing, boolean stereo)844         public AmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing,
845                 boolean stereo) {
846             super(region, type, lowerLimit, upperLimit, spacing);
847             mStereo = stereo;
848         }
849 
850         /** Stereo is supported
851          *  @return {@code true} if stereo is supported, {@code false} otherwise.
852          */
isStereoSupported()853         public boolean isStereoSupported() {
854             return mStereo;
855         }
856 
AmBandDescriptor(Parcel in)857         private AmBandDescriptor(Parcel in) {
858             super(in);
859             mStereo = in.readByte() == 1;
860         }
861 
862         public static final @android.annotation.NonNull Parcelable.Creator<AmBandDescriptor> CREATOR
863                 = new Parcelable.Creator<AmBandDescriptor>() {
864             public AmBandDescriptor createFromParcel(Parcel in) {
865                 return new AmBandDescriptor(in);
866             }
867 
868             public AmBandDescriptor[] newArray(int size) {
869                 return new AmBandDescriptor[size];
870             }
871         };
872 
873         @Override
writeToParcel(Parcel dest, int flags)874         public void writeToParcel(Parcel dest, int flags) {
875             super.writeToParcel(dest, flags);
876             dest.writeByte((byte) (mStereo ? 1 : 0));
877         }
878 
879         @Override
describeContents()880         public int describeContents() {
881             return 0;
882         }
883 
884         @NonNull
885         @Override
toString()886         public String toString() {
887             return "AmBandDescriptor [ "+ super.toString() + " mStereo=" + mStereo + "]";
888         }
889 
890         @Override
hashCode()891         public int hashCode() {
892             final int prime = 31;
893             int result = super.hashCode();
894             result = prime * result + (mStereo ? 1 : 0);
895             return result;
896         }
897 
898         @Override
equals(@ullable Object obj)899         public boolean equals(@Nullable Object obj) {
900             if (this == obj)
901                 return true;
902             if (!super.equals(obj))
903                 return false;
904             if (!(obj instanceof AmBandDescriptor))
905                 return false;
906             AmBandDescriptor other = (AmBandDescriptor) obj;
907             if (mStereo != other.isStereoSupported())
908                 return false;
909             return true;
910         }
911     }
912 
913 
914     /** Radio band configuration. */
915     public static class BandConfig implements Parcelable {
916 
917         @NonNull final BandDescriptor mDescriptor;
918 
BandConfig(BandDescriptor descriptor)919         BandConfig(BandDescriptor descriptor) {
920             mDescriptor = Objects.requireNonNull(descriptor);
921         }
922 
BandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing)923         BandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing) {
924             mDescriptor = new BandDescriptor(region, type, lowerLimit, upperLimit, spacing);
925         }
926 
BandConfig(Parcel in)927         private BandConfig(Parcel in) {
928             mDescriptor = new BandDescriptor(in);
929         }
930 
getDescriptor()931         BandDescriptor getDescriptor() {
932             return mDescriptor;
933         }
934 
935         /** Region this band applies to. E.g. {@link #REGION_ITU_1}
936          *  @return the region associated with this band.
937          */
getRegion()938         public int getRegion() {
939             return mDescriptor.getRegion();
940         }
941         /** Band type, e.g {@link #BAND_FM}. Defines the subclass this descriptor can be cast to:
942          * <ul>
943          *  <li>{@link #BAND_FM} or {@link #BAND_FM_HD} cast to {@link FmBandDescriptor}, </li>
944          *  <li>{@link #BAND_AM} cast to {@link AmBandDescriptor}, </li>
945          * </ul>
946          *  @return the band type.
947          */
getType()948         public int getType() {
949             return mDescriptor.getType();
950         }
951         /** Lower band limit expressed in units according to band type.
952          * Currently all defined band types express channels as frequency in kHz
953          *  @return the lower band limit.
954          */
getLowerLimit()955         public int getLowerLimit() {
956             return mDescriptor.getLowerLimit();
957         }
958         /** Upper band limit expressed in units according to band type.
959          * Currently all defined band types express channels as frequency in kHz
960          *  @return the upper band limit.
961          */
getUpperLimit()962         public int getUpperLimit() {
963             return mDescriptor.getUpperLimit();
964         }
965         /** Channel spacing in units according to band type.
966          * Currently all defined band types express channels as frequency in kHz
967          *  @return the channel spacing.
968          */
getSpacing()969         public int getSpacing() {
970             return mDescriptor.getSpacing();
971         }
972 
973 
974         public static final @android.annotation.NonNull Parcelable.Creator<BandConfig> CREATOR
975                 = new Parcelable.Creator<BandConfig>() {
976             public BandConfig createFromParcel(Parcel in) {
977                 int type = BandDescriptor.lookupTypeFromParcel(in);
978                 switch (type) {
979                     case BAND_FM:
980                     case BAND_FM_HD:
981                         return new FmBandConfig(in);
982                     case BAND_AM:
983                     case BAND_AM_HD:
984                         return new AmBandConfig(in);
985                     default:
986                         throw new IllegalArgumentException("Unsupported band: " + type);
987                 }
988             }
989 
990             public BandConfig[] newArray(int size) {
991                 return new BandConfig[size];
992             }
993         };
994 
995         @Override
writeToParcel(Parcel dest, int flags)996         public void writeToParcel(Parcel dest, int flags) {
997             mDescriptor.writeToParcel(dest, flags);
998         }
999 
1000         @Override
describeContents()1001         public int describeContents() {
1002             return 0;
1003         }
1004 
1005         @NonNull
1006         @Override
toString()1007         public String toString() {
1008             return "BandConfig [ " + mDescriptor.toString() + "]";
1009         }
1010 
1011         @Override
hashCode()1012         public int hashCode() {
1013             final int prime = 31;
1014             int result = 1;
1015             result = prime * result + mDescriptor.hashCode();
1016             return result;
1017         }
1018 
1019         @Override
equals(@ullable Object obj)1020         public boolean equals(@Nullable Object obj) {
1021             if (this == obj)
1022                 return true;
1023             if (!(obj instanceof BandConfig))
1024                 return false;
1025             BandConfig other = (BandConfig) obj;
1026             BandDescriptor otherDesc = other.getDescriptor();
1027             if ((mDescriptor == null) != (otherDesc == null)) return false;
1028             if (mDescriptor != null && !mDescriptor.equals(otherDesc)) return false;
1029             return true;
1030         }
1031     }
1032 
1033     /** FM band configuration.
1034      * @see #BAND_FM
1035      * @see #BAND_FM_HD */
1036     public static class FmBandConfig extends BandConfig {
1037         private final boolean mStereo;
1038         private final boolean mRds;
1039         private final boolean mTa;
1040         private final boolean mAf;
1041         private final boolean mEa;
1042 
1043         /** @hide */
FmBandConfig(FmBandDescriptor descriptor)1044         public FmBandConfig(FmBandDescriptor descriptor) {
1045             super((BandDescriptor)descriptor);
1046             mStereo = descriptor.isStereoSupported();
1047             mRds = descriptor.isRdsSupported();
1048             mTa = descriptor.isTaSupported();
1049             mAf = descriptor.isAfSupported();
1050             mEa = descriptor.isEaSupported();
1051         }
1052 
FmBandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing, boolean stereo, boolean rds, boolean ta, boolean af, boolean ea)1053         FmBandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing,
1054                 boolean stereo, boolean rds, boolean ta, boolean af, boolean ea) {
1055             super(region, type, lowerLimit, upperLimit, spacing);
1056             mStereo = stereo;
1057             mRds = rds;
1058             mTa = ta;
1059             mAf = af;
1060             mEa = ea;
1061         }
1062 
1063         /** Get stereo enable state
1064          * @return the enable state.
1065          */
getStereo()1066         public boolean getStereo() {
1067             return mStereo;
1068         }
1069 
1070         /** Get RDS or RBDS(if region is ITU2) enable state
1071          * @return the enable state.
1072          */
getRds()1073         public boolean getRds() {
1074             return mRds;
1075         }
1076 
1077         /** Get Traffic announcement enable state
1078          * @return the enable state.
1079          */
getTa()1080         public boolean getTa() {
1081             return mTa;
1082         }
1083 
1084         /** Get Alternate Frequency Switching enable state
1085          * @return the enable state.
1086          */
getAf()1087         public boolean getAf() {
1088             return mAf;
1089         }
1090 
1091         /**
1092          * Get Emergency announcement enable state
1093          * @return the enable state.
1094          */
getEa()1095         public boolean getEa() {
1096             return mEa;
1097         }
1098 
FmBandConfig(Parcel in)1099         private FmBandConfig(Parcel in) {
1100             super(in);
1101             mStereo = in.readByte() == 1;
1102             mRds = in.readByte() == 1;
1103             mTa = in.readByte() == 1;
1104             mAf = in.readByte() == 1;
1105             mEa = in.readByte() == 1;
1106         }
1107 
1108         public static final @android.annotation.NonNull Parcelable.Creator<FmBandConfig> CREATOR
1109                 = new Parcelable.Creator<FmBandConfig>() {
1110             public FmBandConfig createFromParcel(Parcel in) {
1111                 return new FmBandConfig(in);
1112             }
1113 
1114             public FmBandConfig[] newArray(int size) {
1115                 return new FmBandConfig[size];
1116             }
1117         };
1118 
1119         @Override
writeToParcel(Parcel dest, int flags)1120         public void writeToParcel(Parcel dest, int flags) {
1121             super.writeToParcel(dest, flags);
1122             dest.writeByte((byte) (mStereo ? 1 : 0));
1123             dest.writeByte((byte) (mRds ? 1 : 0));
1124             dest.writeByte((byte) (mTa ? 1 : 0));
1125             dest.writeByte((byte) (mAf ? 1 : 0));
1126             dest.writeByte((byte) (mEa ? 1 : 0));
1127         }
1128 
1129         @Override
describeContents()1130         public int describeContents() {
1131             return 0;
1132         }
1133 
1134         @NonNull
1135         @Override
toString()1136         public String toString() {
1137             return "FmBandConfig [" + super.toString()
1138                     + ", mStereo=" + mStereo + ", mRds=" + mRds + ", mTa=" + mTa
1139                     + ", mAf=" + mAf + ", mEa =" + mEa + "]";
1140         }
1141 
1142         @Override
hashCode()1143         public int hashCode() {
1144             final int prime = 31;
1145             int result = super.hashCode();
1146             result = prime * result + (mStereo ? 1 : 0);
1147             result = prime * result + (mRds ? 1 : 0);
1148             result = prime * result + (mTa ? 1 : 0);
1149             result = prime * result + (mAf ? 1 : 0);
1150             result = prime * result + (mEa ? 1 : 0);
1151             return result;
1152         }
1153 
1154         @Override
equals(@ullable Object obj)1155         public boolean equals(@Nullable Object obj) {
1156             if (this == obj)
1157                 return true;
1158             if (!super.equals(obj))
1159                 return false;
1160             if (!(obj instanceof FmBandConfig))
1161                 return false;
1162             FmBandConfig other = (FmBandConfig) obj;
1163             if (mStereo != other.mStereo)
1164                 return false;
1165             if (mRds != other.mRds)
1166                 return false;
1167             if (mTa != other.mTa)
1168                 return false;
1169             if (mAf != other.mAf)
1170                 return false;
1171             if (mEa != other.mEa)
1172                 return false;
1173             return true;
1174         }
1175 
1176         /**
1177          * Builder class for {@link FmBandConfig} objects.
1178          */
1179         public static class Builder {
1180             private final BandDescriptor mDescriptor;
1181             private boolean mStereo;
1182             private boolean mRds;
1183             private boolean mTa;
1184             private boolean mAf;
1185             private boolean mEa;
1186 
1187             /**
1188              * Constructs a new Builder with the defaults from an {@link FmBandDescriptor} .
1189              * @param descriptor the FmBandDescriptor defaults are read from .
1190              */
Builder(FmBandDescriptor descriptor)1191             public Builder(FmBandDescriptor descriptor) {
1192                 mDescriptor = new BandDescriptor(descriptor.getRegion(), descriptor.getType(),
1193                         descriptor.getLowerLimit(), descriptor.getUpperLimit(),
1194                         descriptor.getSpacing());
1195                 mStereo = descriptor.isStereoSupported();
1196                 mRds = descriptor.isRdsSupported();
1197                 mTa = descriptor.isTaSupported();
1198                 mAf = descriptor.isAfSupported();
1199                 mEa = descriptor.isEaSupported();
1200             }
1201 
1202             /**
1203              * Constructs a new Builder from a given {@link FmBandConfig}
1204              * @param config the FmBandConfig object whose data will be reused in the new Builder.
1205              */
Builder(FmBandConfig config)1206             public Builder(FmBandConfig config) {
1207                 mDescriptor = new BandDescriptor(config.getRegion(), config.getType(),
1208                         config.getLowerLimit(), config.getUpperLimit(), config.getSpacing());
1209                 mStereo = config.getStereo();
1210                 mRds = config.getRds();
1211                 mTa = config.getTa();
1212                 mAf = config.getAf();
1213                 mEa = config.getEa();
1214             }
1215 
1216             /**
1217              * Combines all of the parameters that have been set and return a new
1218              * {@link FmBandConfig} object.
1219              * @return a new {@link FmBandConfig} object
1220              */
build()1221             public FmBandConfig build() {
1222                 FmBandConfig config = new FmBandConfig(mDescriptor.getRegion(),
1223                         mDescriptor.getType(), mDescriptor.getLowerLimit(),
1224                         mDescriptor.getUpperLimit(), mDescriptor.getSpacing(),
1225                         mStereo, mRds, mTa, mAf, mEa);
1226                 return config;
1227             }
1228 
1229             /** Set stereo enable state
1230              * @param state The new enable state.
1231              * @return the same Builder instance.
1232              */
setStereo(boolean state)1233             public Builder setStereo(boolean state) {
1234                 mStereo = state;
1235                 return this;
1236             }
1237 
1238             /** Set RDS or RBDS(if region is ITU2) enable state
1239              * @param state The new enable state.
1240              * @return the same Builder instance.
1241              */
setRds(boolean state)1242             public Builder setRds(boolean state) {
1243                 mRds = state;
1244                 return this;
1245             }
1246 
1247             /** Set Traffic announcement enable state
1248              * @param state The new enable state.
1249              * @return the same Builder instance.
1250              */
setTa(boolean state)1251             public Builder setTa(boolean state) {
1252                 mTa = state;
1253                 return this;
1254             }
1255 
1256             /** Set Alternate Frequency Switching enable state
1257              * @param state The new enable state.
1258              * @return the same Builder instance.
1259              */
setAf(boolean state)1260             public Builder setAf(boolean state) {
1261                 mAf = state;
1262                 return this;
1263             }
1264 
1265             /** Set Emergency Announcement enable state
1266              * @param state The new enable state.
1267              * @return the same Builder instance.
1268              */
setEa(boolean state)1269             public Builder setEa(boolean state) {
1270                 mEa = state;
1271                 return this;
1272             }
1273         };
1274     }
1275 
1276     /** AM band configuration.
1277      * @see #BAND_AM */
1278     public static class AmBandConfig extends BandConfig {
1279         private final boolean mStereo;
1280 
1281         /** @hide */
AmBandConfig(AmBandDescriptor descriptor)1282         public AmBandConfig(AmBandDescriptor descriptor) {
1283             super((BandDescriptor)descriptor);
1284             mStereo = descriptor.isStereoSupported();
1285         }
1286 
AmBandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing, boolean stereo)1287         AmBandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing,
1288                 boolean stereo) {
1289             super(region, type, lowerLimit, upperLimit, spacing);
1290             mStereo = stereo;
1291         }
1292 
1293         /** Get stereo enable state
1294          * @return the enable state.
1295          */
getStereo()1296         public boolean getStereo() {
1297             return mStereo;
1298         }
1299 
AmBandConfig(Parcel in)1300         private AmBandConfig(Parcel in) {
1301             super(in);
1302             mStereo = in.readByte() == 1;
1303         }
1304 
1305         public static final @android.annotation.NonNull Parcelable.Creator<AmBandConfig> CREATOR
1306                 = new Parcelable.Creator<AmBandConfig>() {
1307             public AmBandConfig createFromParcel(Parcel in) {
1308                 return new AmBandConfig(in);
1309             }
1310 
1311             public AmBandConfig[] newArray(int size) {
1312                 return new AmBandConfig[size];
1313             }
1314         };
1315 
1316         @Override
writeToParcel(Parcel dest, int flags)1317         public void writeToParcel(Parcel dest, int flags) {
1318             super.writeToParcel(dest, flags);
1319             dest.writeByte((byte) (mStereo ? 1 : 0));
1320         }
1321 
1322         @Override
describeContents()1323         public int describeContents() {
1324             return 0;
1325         }
1326 
1327         @NonNull
1328         @Override
toString()1329         public String toString() {
1330             return "AmBandConfig [" + super.toString()
1331                     + ", mStereo=" + mStereo + "]";
1332         }
1333 
1334         @Override
hashCode()1335         public int hashCode() {
1336             final int prime = 31;
1337             int result = super.hashCode();
1338             result = prime * result + (mStereo ? 1 : 0);
1339             return result;
1340         }
1341 
1342         @Override
equals(@ullable Object obj)1343         public boolean equals(@Nullable Object obj) {
1344             if (this == obj)
1345                 return true;
1346             if (!super.equals(obj))
1347                 return false;
1348             if (!(obj instanceof AmBandConfig))
1349                 return false;
1350             AmBandConfig other = (AmBandConfig) obj;
1351             if (mStereo != other.getStereo())
1352                 return false;
1353             return true;
1354         }
1355 
1356         /**
1357          * Builder class for {@link AmBandConfig} objects.
1358          */
1359         public static class Builder {
1360             private final BandDescriptor mDescriptor;
1361             private boolean mStereo;
1362 
1363             /**
1364              * Constructs a new Builder with the defaults from an {@link AmBandDescriptor} .
1365              * @param descriptor the FmBandDescriptor defaults are read from .
1366              */
Builder(AmBandDescriptor descriptor)1367             public Builder(AmBandDescriptor descriptor) {
1368                 mDescriptor = new BandDescriptor(descriptor.getRegion(), descriptor.getType(),
1369                         descriptor.getLowerLimit(), descriptor.getUpperLimit(),
1370                         descriptor.getSpacing());
1371                 mStereo = descriptor.isStereoSupported();
1372             }
1373 
1374             /**
1375              * Constructs a new Builder from a given {@link AmBandConfig}
1376              * @param config the FmBandConfig object whose data will be reused in the new Builder.
1377              */
Builder(AmBandConfig config)1378             public Builder(AmBandConfig config) {
1379                 mDescriptor = new BandDescriptor(config.getRegion(), config.getType(),
1380                         config.getLowerLimit(), config.getUpperLimit(), config.getSpacing());
1381                 mStereo = config.getStereo();
1382             }
1383 
1384             /**
1385              * Combines all of the parameters that have been set and return a new
1386              * {@link AmBandConfig} object.
1387              * @return a new {@link AmBandConfig} object
1388              */
build()1389             public AmBandConfig build() {
1390                 AmBandConfig config = new AmBandConfig(mDescriptor.getRegion(),
1391                         mDescriptor.getType(), mDescriptor.getLowerLimit(),
1392                         mDescriptor.getUpperLimit(), mDescriptor.getSpacing(),
1393                         mStereo);
1394                 return config;
1395             }
1396 
1397             /** Set stereo enable state
1398              * @param state The new enable state.
1399              * @return the same Builder instance.
1400              */
setStereo(boolean state)1401             public Builder setStereo(boolean state) {
1402                 mStereo = state;
1403                 return this;
1404             }
1405         };
1406     }
1407 
1408     /** Radio program information. */
1409     public static class ProgramInfo implements Parcelable {
1410 
1411         // sourced from hardware/interfaces/broadcastradio/2.0/types.hal
1412         private static final int FLAG_LIVE = 1 << 0;
1413         private static final int FLAG_MUTED = 1 << 1;
1414         private static final int FLAG_TRAFFIC_PROGRAM = 1 << 2;
1415         private static final int FLAG_TRAFFIC_ANNOUNCEMENT = 1 << 3;
1416         private static final int FLAG_TUNED = 1 << 4;
1417         private static final int FLAG_STEREO = 1 << 5;
1418 
1419         @NonNull private final ProgramSelector mSelector;
1420         @Nullable private final ProgramSelector.Identifier mLogicallyTunedTo;
1421         @Nullable private final ProgramSelector.Identifier mPhysicallyTunedTo;
1422         @NonNull private final Collection<ProgramSelector.Identifier> mRelatedContent;
1423         private final int mInfoFlags;
1424         private final int mSignalQuality;
1425         @Nullable private final RadioMetadata mMetadata;
1426         @NonNull private final Map<String, String> mVendorInfo;
1427 
1428         /** @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)1429         public ProgramInfo(@NonNull ProgramSelector selector,
1430                 @Nullable ProgramSelector.Identifier logicallyTunedTo,
1431                 @Nullable ProgramSelector.Identifier physicallyTunedTo,
1432                 @Nullable Collection<ProgramSelector.Identifier> relatedContent,
1433                 int infoFlags, int signalQuality, @Nullable RadioMetadata metadata,
1434                 @Nullable Map<String, String> vendorInfo) {
1435             mSelector = Objects.requireNonNull(selector);
1436             mLogicallyTunedTo = logicallyTunedTo;
1437             mPhysicallyTunedTo = physicallyTunedTo;
1438             if (relatedContent == null) {
1439                 mRelatedContent = Collections.emptyList();
1440             } else {
1441                 Preconditions.checkCollectionElementsNotNull(relatedContent, "relatedContent");
1442                 mRelatedContent = relatedContent;
1443             }
1444             mInfoFlags = infoFlags;
1445             mSignalQuality = signalQuality;
1446             mMetadata = metadata;
1447             mVendorInfo = (vendorInfo == null) ? new HashMap<>() : vendorInfo;
1448         }
1449 
1450         /**
1451          * Program selector, necessary for tuning to a program.
1452          *
1453          * @return the program selector.
1454          */
getSelector()1455         public @NonNull ProgramSelector getSelector() {
1456             return mSelector;
1457         }
1458 
1459         /**
1460          * Identifier currently used for program selection.
1461          *
1462          * This identifier can be used to determine which technology is
1463          * currently being used for reception.
1464          *
1465          * Some program selectors contain tuning information for different radio
1466          * technologies (i.e. FM RDS and DAB). For example, user may tune using
1467          * a ProgramSelector with RDS_PI primary identifier, but the tuner hardware
1468          * may choose to use DAB technology to make actual tuning. This identifier
1469          * must reflect that.
1470          */
getLogicallyTunedTo()1471         public @Nullable ProgramSelector.Identifier getLogicallyTunedTo() {
1472             return mLogicallyTunedTo;
1473         }
1474 
1475         /**
1476          * Identifier currently used by hardware to physically tune to a channel.
1477          *
1478          * Some radio technologies broadcast the same program on multiple channels,
1479          * i.e. with RDS AF the same program may be broadcasted on multiple
1480          * alternative frequencies; the same DAB program may be broadcast on
1481          * multiple ensembles. This identifier points to the channel to which the
1482          * radio hardware is physically tuned to.
1483          */
getPhysicallyTunedTo()1484         public @Nullable ProgramSelector.Identifier getPhysicallyTunedTo() {
1485             return mPhysicallyTunedTo;
1486         }
1487 
1488         /**
1489          * Primary identifiers of related contents.
1490          *
1491          * Some radio technologies provide pointers to other programs that carry
1492          * related content (i.e. DAB soft-links). This field is a list of pointers
1493          * to other programs on the program list.
1494          *
1495          * Please note, that these identifiers does not have to exist on the program
1496          * list - i.e. DAB tuner may provide information on FM RDS alternatives
1497          * despite not supporting FM RDS. If the system has multiple tuners, another
1498          * one may have it on its list.
1499          */
getRelatedContent()1500         public @Nullable Collection<ProgramSelector.Identifier> getRelatedContent() {
1501             return mRelatedContent;
1502         }
1503 
1504         /** Main channel expressed in units according to band type.
1505          * Currently all defined band types express channels as frequency in kHz
1506          * @return the program channel
1507          * @deprecated Use {@link getSelector()} instead.
1508          */
1509         @Deprecated
getChannel()1510         public int getChannel() {
1511             try {
1512                 return (int) mSelector.getFirstId(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY);
1513             } catch (IllegalArgumentException ex) {
1514                 Log.w(TAG, "Not an AM/FM program");
1515                 return 0;
1516             }
1517         }
1518 
1519         /** Sub channel ID. E.g 1 for HD radio HD1
1520          * @return the program sub channel
1521          * @deprecated Use {@link getSelector()} instead.
1522          */
1523         @Deprecated
getSubChannel()1524         public int getSubChannel() {
1525             try {
1526                 return (int) mSelector.getFirstId(
1527                         ProgramSelector.IDENTIFIER_TYPE_HD_SUBCHANNEL) + 1;
1528             } catch (IllegalArgumentException ex) {
1529                 // this is a normal behavior for analog AM/FM selector
1530                 return 0;
1531             }
1532         }
1533 
1534         /** {@code true} if the tuner is currently tuned on a valid station
1535          * @return {@code true} if currently tuned, {@code false} otherwise.
1536          */
isTuned()1537         public boolean isTuned() {
1538             return (mInfoFlags & FLAG_TUNED) != 0;
1539         }
1540 
1541         /** {@code true} if the received program is stereo
1542          * @return {@code true} if stereo, {@code false} otherwise.
1543          */
isStereo()1544         public boolean isStereo() {
1545             return (mInfoFlags & FLAG_STEREO) != 0;
1546         }
1547 
1548         /** {@code true} if the received program is digital (e.g HD radio)
1549          * @return {@code true} if digital, {@code false} otherwise.
1550          * @deprecated Use {@link getLogicallyTunedTo()} instead.
1551          */
1552         @Deprecated
isDigital()1553         public boolean isDigital() {
1554             ProgramSelector.Identifier id = mLogicallyTunedTo;
1555             if (id == null) id = mSelector.getPrimaryId();
1556 
1557             int type = id.getType();
1558             return (type != ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY
1559                 && type != ProgramSelector.IDENTIFIER_TYPE_RDS_PI);
1560         }
1561 
1562         /**
1563          * {@code true} if the program is currently playing live stream.
1564          * This may result in a slightly altered reception parameters,
1565          * usually targetted at reduced latency.
1566          */
isLive()1567         public boolean isLive() {
1568             return (mInfoFlags & FLAG_LIVE) != 0;
1569         }
1570 
1571         /**
1572          * {@code true} if radio stream is not playing, ie. due to bad reception
1573          * conditions or buffering. In this state volume knob MAY be disabled to
1574          * prevent user increasing volume too much.
1575          * It does NOT mean the user has muted audio.
1576          */
isMuted()1577         public boolean isMuted() {
1578             return (mInfoFlags & FLAG_MUTED) != 0;
1579         }
1580 
1581         /**
1582          * {@code true} if radio station transmits traffic information
1583          * regularily.
1584          */
isTrafficProgram()1585         public boolean isTrafficProgram() {
1586             return (mInfoFlags & FLAG_TRAFFIC_PROGRAM) != 0;
1587         }
1588 
1589         /**
1590          * {@code true} if radio station transmits traffic information
1591          * at the very moment.
1592          */
isTrafficAnnouncementActive()1593         public boolean isTrafficAnnouncementActive() {
1594             return (mInfoFlags & FLAG_TRAFFIC_ANNOUNCEMENT) != 0;
1595         }
1596 
1597         /**
1598          * Signal quality (as opposed to the name) indication from 0 (no signal)
1599          * to 100 (excellent)
1600          * @return the signal quality indication.
1601          */
getSignalStrength()1602         public int getSignalStrength() {
1603             return mSignalQuality;
1604         }
1605 
1606         /** Metadata currently received from this station.
1607          * null if no metadata have been received
1608          * @return current meta data received from this program.
1609          */
getMetadata()1610         public RadioMetadata getMetadata() {
1611             return mMetadata;
1612         }
1613 
1614         /**
1615          * A map of vendor-specific opaque strings, passed from HAL without changes.
1616          * Format of these strings can vary across vendors.
1617          *
1618          * It may be used for extra features, that's not supported by a platform,
1619          * for example: paid-service=true; bitrate=320kbps.
1620          *
1621          * Keys must be prefixed with unique vendor Java-style namespace,
1622          * eg. 'com.somecompany.parameter1'.
1623          */
getVendorInfo()1624         public @NonNull Map<String, String> getVendorInfo() {
1625             return mVendorInfo;
1626         }
1627 
ProgramInfo(Parcel in)1628         private ProgramInfo(Parcel in) {
1629             mSelector = Objects.requireNonNull(in.readTypedObject(ProgramSelector.CREATOR));
1630             mLogicallyTunedTo = in.readTypedObject(ProgramSelector.Identifier.CREATOR);
1631             mPhysicallyTunedTo = in.readTypedObject(ProgramSelector.Identifier.CREATOR);
1632             mRelatedContent = in.createTypedArrayList(ProgramSelector.Identifier.CREATOR);
1633             mInfoFlags = in.readInt();
1634             mSignalQuality = in.readInt();
1635             mMetadata = in.readTypedObject(RadioMetadata.CREATOR);
1636             mVendorInfo = Utils.readStringMap(in);
1637         }
1638 
1639         public static final @android.annotation.NonNull Parcelable.Creator<ProgramInfo> CREATOR
1640                 = new Parcelable.Creator<ProgramInfo>() {
1641             public ProgramInfo createFromParcel(Parcel in) {
1642                 return new ProgramInfo(in);
1643             }
1644 
1645             public ProgramInfo[] newArray(int size) {
1646                 return new ProgramInfo[size];
1647             }
1648         };
1649 
1650         @Override
writeToParcel(Parcel dest, int flags)1651         public void writeToParcel(Parcel dest, int flags) {
1652             dest.writeTypedObject(mSelector, flags);
1653             dest.writeTypedObject(mLogicallyTunedTo, flags);
1654             dest.writeTypedObject(mPhysicallyTunedTo, flags);
1655             Utils.writeTypedCollection(dest, mRelatedContent);
1656             dest.writeInt(mInfoFlags);
1657             dest.writeInt(mSignalQuality);
1658             dest.writeTypedObject(mMetadata, flags);
1659             Utils.writeStringMap(dest, mVendorInfo);
1660         }
1661 
1662         @Override
describeContents()1663         public int describeContents() {
1664             return 0;
1665         }
1666 
1667         @NonNull
1668         @Override
toString()1669         public String toString() {
1670             return "ProgramInfo"
1671                     + " [selector=" + mSelector
1672                     + ", logicallyTunedTo=" + Objects.toString(mLogicallyTunedTo)
1673                     + ", physicallyTunedTo=" + Objects.toString(mPhysicallyTunedTo)
1674                     + ", relatedContent=" + mRelatedContent.size()
1675                     + ", infoFlags=" + mInfoFlags
1676                     + ", mSignalQuality=" + mSignalQuality
1677                     + ", mMetadata=" + Objects.toString(mMetadata)
1678                     + "]";
1679         }
1680 
1681         @Override
hashCode()1682         public int hashCode() {
1683             return Objects.hash(mSelector, mLogicallyTunedTo, mPhysicallyTunedTo,
1684                 mRelatedContent, mInfoFlags, mSignalQuality, mMetadata, mVendorInfo);
1685         }
1686 
1687         @Override
equals(@ullable Object obj)1688         public boolean equals(@Nullable Object obj) {
1689             if (this == obj) return true;
1690             if (!(obj instanceof ProgramInfo)) return false;
1691             ProgramInfo other = (ProgramInfo) obj;
1692 
1693             if (!Objects.equals(mSelector, other.mSelector)) return false;
1694             if (!Objects.equals(mLogicallyTunedTo, other.mLogicallyTunedTo)) return false;
1695             if (!Objects.equals(mPhysicallyTunedTo, other.mPhysicallyTunedTo)) return false;
1696             if (!Objects.equals(mRelatedContent, other.mRelatedContent)) return false;
1697             if (mInfoFlags != other.mInfoFlags) return false;
1698             if (mSignalQuality != other.mSignalQuality) return false;
1699             if (!Objects.equals(mMetadata, other.mMetadata)) return false;
1700             if (!Objects.equals(mVendorInfo, other.mVendorInfo)) return false;
1701 
1702             return true;
1703         }
1704     }
1705 
1706 
1707     /**
1708      * Returns a list of descriptors for all broadcast radio modules present on the device.
1709      * @param modules An List of {@link ModuleProperties} where the list will be returned.
1710      * @return
1711      * <ul>
1712      *  <li>{@link #STATUS_OK} in case of success, </li>
1713      *  <li>{@link #STATUS_ERROR} in case of unspecified error, </li>
1714      *  <li>{@link #STATUS_NO_INIT} if the native service cannot be reached, </li>
1715      *  <li>{@link #STATUS_BAD_VALUE} if modules is null, </li>
1716      *  <li>{@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails, </li>
1717      * </ul>
1718      */
1719     @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO)
listModules(List<ModuleProperties> modules)1720     public int listModules(List<ModuleProperties> modules) {
1721         if (modules == null) {
1722             Log.e(TAG, "the output list must not be empty");
1723             return STATUS_BAD_VALUE;
1724         }
1725 
1726         Log.d(TAG, "Listing available tuners...");
1727         List<ModuleProperties> returnedList;
1728         try {
1729             returnedList = mService.listModules();
1730         } catch (RemoteException e) {
1731             Log.e(TAG, "Failed listing available tuners", e);
1732             return STATUS_DEAD_OBJECT;
1733         }
1734 
1735         if (returnedList == null) {
1736             Log.e(TAG, "Returned list was a null");
1737             return STATUS_ERROR;
1738         }
1739 
1740         modules.addAll(returnedList);
1741         return STATUS_OK;
1742     }
1743 
nativeListModules(List<ModuleProperties> modules)1744     private native int nativeListModules(List<ModuleProperties> modules);
1745 
1746     /**
1747      * Open an interface to control a tuner on a given broadcast radio module.
1748      * Optionally selects and applies the configuration passed as "config" argument.
1749      * @param moduleId radio module identifier {@link ModuleProperties#getId()}. Mandatory.
1750      * @param config desired band and configuration to apply when enabling the hardware module.
1751      * optional, can be null.
1752      * @param withAudio {@code true} to request a tuner with an audio source.
1753      * This tuner is intended for live listening or recording or a radio program.
1754      * If {@code false}, the tuner can only be used to retrieve program informations.
1755      * @param callback {@link RadioTuner.Callback} interface. Mandatory.
1756      * @param handler the Handler on which the callbacks will be received.
1757      * Can be null if default handler is OK.
1758      * @return a valid {@link RadioTuner} interface in case of success or null in case of error.
1759      */
1760     @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO)
openTuner(int moduleId, BandConfig config, boolean withAudio, RadioTuner.Callback callback, Handler handler)1761     public RadioTuner openTuner(int moduleId, BandConfig config, boolean withAudio,
1762             RadioTuner.Callback callback, Handler handler) {
1763         if (callback == null) {
1764             throw new IllegalArgumentException("callback must not be empty");
1765         }
1766 
1767         Log.d(TAG, "Opening tuner " + moduleId + "...");
1768 
1769         ITuner tuner;
1770         TunerCallbackAdapter halCallback = new TunerCallbackAdapter(callback, handler);
1771         try {
1772             tuner = mService.openTuner(moduleId, config, withAudio, halCallback);
1773         } catch (RemoteException | IllegalArgumentException | IllegalStateException ex) {
1774             Log.e(TAG, "Failed to open tuner", ex);
1775             return null;
1776         }
1777         if (tuner == null) {
1778             Log.e(TAG, "Failed to open tuner");
1779             return null;
1780         }
1781         return new TunerAdapter(tuner, halCallback,
1782                 config != null ? config.getType() : BAND_INVALID);
1783     }
1784 
1785     private final Map<Announcement.OnListUpdatedListener, ICloseHandle> mAnnouncementListeners =
1786             new HashMap<>();
1787 
1788     /**
1789      * Adds new announcement listener.
1790      *
1791      * @param enabledAnnouncementTypes a set of announcement types to listen to
1792      * @param listener announcement listener
1793      */
1794     @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO)
addAnnouncementListener(@onNull Set<Integer> enabledAnnouncementTypes, @NonNull Announcement.OnListUpdatedListener listener)1795     public void addAnnouncementListener(@NonNull Set<Integer> enabledAnnouncementTypes,
1796             @NonNull Announcement.OnListUpdatedListener listener) {
1797         addAnnouncementListener(cmd -> cmd.run(), enabledAnnouncementTypes, listener);
1798     }
1799 
1800     /**
1801      * Adds new announcement listener with executor.
1802      *
1803      * @param executor the executor
1804      * @param enabledAnnouncementTypes a set of announcement types to listen to
1805      * @param listener announcement listener
1806      */
1807     @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO)
addAnnouncementListener(@onNull @allbackExecutor Executor executor, @NonNull Set<Integer> enabledAnnouncementTypes, @NonNull Announcement.OnListUpdatedListener listener)1808     public void addAnnouncementListener(@NonNull @CallbackExecutor Executor executor,
1809             @NonNull Set<Integer> enabledAnnouncementTypes,
1810             @NonNull Announcement.OnListUpdatedListener listener) {
1811         Objects.requireNonNull(executor);
1812         Objects.requireNonNull(listener);
1813         int[] types = enabledAnnouncementTypes.stream().mapToInt(Integer::intValue).toArray();
1814         IAnnouncementListener listenerIface = new IAnnouncementListener.Stub() {
1815             public void onListUpdated(List<Announcement> activeAnnouncements) {
1816                 executor.execute(() -> listener.onListUpdated(activeAnnouncements));
1817             }
1818         };
1819         synchronized (mAnnouncementListeners) {
1820             ICloseHandle closeHandle = null;
1821             try {
1822                 closeHandle = mService.addAnnouncementListener(types, listenerIface);
1823             } catch (RemoteException ex) {
1824                 ex.rethrowFromSystemServer();
1825             }
1826             Objects.requireNonNull(closeHandle);
1827             ICloseHandle oldCloseHandle = mAnnouncementListeners.put(listener, closeHandle);
1828             if (oldCloseHandle != null) Utils.close(oldCloseHandle);
1829         }
1830     }
1831 
1832     /**
1833      * Removes previously registered announcement listener.
1834      *
1835      * @param listener announcement listener, previously registered with
1836      *        {@link addAnnouncementListener}
1837      */
1838     @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO)
removeAnnouncementListener(@onNull Announcement.OnListUpdatedListener listener)1839     public void removeAnnouncementListener(@NonNull Announcement.OnListUpdatedListener listener) {
1840         Objects.requireNonNull(listener);
1841         synchronized (mAnnouncementListeners) {
1842             ICloseHandle closeHandle = mAnnouncementListeners.remove(listener);
1843             if (closeHandle != null) Utils.close(closeHandle);
1844         }
1845     }
1846 
1847     @NonNull private final Context mContext;
1848     @NonNull private final IRadioService mService;
1849 
1850     /**
1851      * @hide
1852      */
RadioManager(@onNull Context context)1853     public RadioManager(@NonNull Context context) throws ServiceNotFoundException {
1854         mContext = context;
1855         mService = IRadioService.Stub.asInterface(
1856                 ServiceManager.getServiceOrThrow(Context.RADIO_SERVICE));
1857     }
1858 }
1859