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