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