• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.hardware.radio;
18 
19 import android.Manifest;
20 import android.annotation.CallbackExecutor;
21 import android.annotation.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         @Nullable private final RadioAlert mAlert;
1551 
1552         /** @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)1553         public ProgramInfo(@NonNull ProgramSelector selector,
1554                 @Nullable ProgramSelector.Identifier logicallyTunedTo,
1555                 @Nullable ProgramSelector.Identifier physicallyTunedTo,
1556                 @Nullable Collection<ProgramSelector.Identifier> relatedContent,
1557                 int infoFlags, int signalQuality, @Nullable RadioMetadata metadata,
1558                 @Nullable Map<String, String> vendorInfo) {
1559             this(selector, logicallyTunedTo, physicallyTunedTo, relatedContent, infoFlags,
1560                     signalQuality, metadata, vendorInfo, /* alert= */ null);
1561         }
1562 
1563         /**
1564          * Constructor for program info.
1565          *
1566          * @param selector Program selector
1567          * @param logicallyTunedTo Program identifier logically tuned to
1568          * @param physicallyTunedTo Program identifier physically tuned to
1569          * @param relatedContent Related content
1570          * @param infoFlags Program info flags
1571          * @param signalQuality Signal quality
1572          * @param metadata Radio metadata
1573          * @param vendorInfo Vendor parameters
1574          * @param alert Radio alert
1575          * @hide
1576          */
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, @Nullable RadioAlert alert)1577         public ProgramInfo(@NonNull ProgramSelector selector,
1578                 @Nullable ProgramSelector.Identifier logicallyTunedTo,
1579                 @Nullable ProgramSelector.Identifier physicallyTunedTo,
1580                 @Nullable Collection<ProgramSelector.Identifier> relatedContent,
1581                 int infoFlags, int signalQuality, @Nullable RadioMetadata metadata,
1582                 @Nullable Map<String, String> vendorInfo, @Nullable RadioAlert alert) {
1583             mSelector = Objects.requireNonNull(selector);
1584             mLogicallyTunedTo = logicallyTunedTo;
1585             mPhysicallyTunedTo = physicallyTunedTo;
1586             if (relatedContent == null) {
1587                 mRelatedContent = Collections.emptyList();
1588             } else {
1589                 Preconditions.checkCollectionElementsNotNull(relatedContent, "relatedContent");
1590                 mRelatedContent = relatedContent;
1591             }
1592             mInfoFlags = infoFlags;
1593             mSignalQuality = signalQuality;
1594             mMetadata = metadata;
1595             mVendorInfo = (vendorInfo == null) ? new HashMap<>() : vendorInfo;
1596             mAlert = alert;
1597         }
1598 
1599         /**
1600          * Program selector, necessary for tuning to a program.
1601          *
1602          * @return the program selector.
1603          */
getSelector()1604         public @NonNull ProgramSelector getSelector() {
1605             return mSelector;
1606         }
1607 
1608         /**
1609          * Identifier currently used for program selection.
1610          *
1611          * <p>This identifier can be used to determine which technology is
1612          * currently being used for reception.
1613          *
1614          * <p>Some program selectors contain tuning information for different radio
1615          * technologies (i.e. FM RDS and DAB). For example, user may tune using
1616          * a ProgramSelector with RDS_PI primary identifier, but the tuner hardware
1617          * may choose to use DAB technology to make actual tuning. This identifier
1618          * must reflect that.
1619          */
getLogicallyTunedTo()1620         public @Nullable ProgramSelector.Identifier getLogicallyTunedTo() {
1621             return mLogicallyTunedTo;
1622         }
1623 
1624         /**
1625          * Identifier currently used by hardware to physically tune to a channel.
1626          *
1627          * <p>Some radio technologies broadcast the same program on multiple channels,
1628          * i.e. with RDS AF the same program may be broadcasted on multiple
1629          * alternative frequencies; the same DAB program may be broadcast on
1630          * multiple ensembles. This identifier points to the channel to which the
1631          * radio hardware is physically tuned to.
1632          */
getPhysicallyTunedTo()1633         public @Nullable ProgramSelector.Identifier getPhysicallyTunedTo() {
1634             return mPhysicallyTunedTo;
1635         }
1636 
1637         /**
1638          * Primary identifiers of related contents.
1639          *
1640          * <p>Some radio technologies provide pointers to other programs that carry
1641          * related content (i.e. DAB soft-links). This field is a list of pointers
1642          * to other programs on the program list.
1643          *
1644          * <p>Please note, that these identifiers does not have to exist on the program
1645          * list - i.e. DAB tuner may provide information on FM RDS alternatives
1646          * despite not supporting FM RDS. If the system has multiple tuners, another
1647          * one may have it on its list.
1648          */
getRelatedContent()1649         public @Nullable Collection<ProgramSelector.Identifier> getRelatedContent() {
1650             return mRelatedContent;
1651         }
1652 
1653         /**
1654          * Main channel expressed in units according to band type.
1655          * Currently all defined band types express channels as frequency in kHz
1656          * @return the program channel
1657          * @deprecated Use {@link ProgramInfo#getSelector} instead.
1658          */
1659         @Deprecated
getChannel()1660         public int getChannel() {
1661             try {
1662                 return (int) mSelector.getFirstId(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY);
1663             } catch (IllegalArgumentException ex) {
1664                 Log.w(TAG, "Not an AM/FM program");
1665                 return 0;
1666             }
1667         }
1668 
1669         /**
1670          * Sub channel ID. E.g. 1 for HD radio HD1
1671          * @return the program sub channel
1672          * @deprecated Use {@link ProgramInfo#getSelector} instead.
1673          */
1674         @Deprecated
getSubChannel()1675         public int getSubChannel() {
1676             try {
1677                 return (int) mSelector.getFirstId(
1678                         ProgramSelector.IDENTIFIER_TYPE_HD_SUBCHANNEL) + 1;
1679             } catch (IllegalArgumentException ex) {
1680                 // this is a normal behavior for analog AM/FM selector
1681                 return 0;
1682             }
1683         }
1684 
1685         /** {@code true} if the tuner is currently tuned on a valid station
1686          * @return {@code true} if currently tuned, {@code false} otherwise.
1687          */
isTuned()1688         public boolean isTuned() {
1689             return (mInfoFlags & FLAG_TUNED) != 0;
1690         }
1691 
1692         /**
1693          * {@code true} if the received program is stereo
1694          * @return {@code true} if stereo, {@code false} otherwise.
1695          */
isStereo()1696         public boolean isStereo() {
1697             return (mInfoFlags & FLAG_STEREO) != 0;
1698         }
1699 
1700         /**
1701          * {@code true} if the received program is digital (e.g. HD radio)
1702          * @return {@code true} if digital, {@code false} otherwise.
1703          * @deprecated Use {@link ProgramInfo#getLogicallyTunedTo()} instead.
1704          */
1705         @Deprecated
isDigital()1706         public boolean isDigital() {
1707             ProgramSelector.Identifier id = mLogicallyTunedTo;
1708             if (id == null) id = mSelector.getPrimaryId();
1709 
1710             int type = id.getType();
1711             return (type != ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY
1712                 && type != ProgramSelector.IDENTIFIER_TYPE_RDS_PI);
1713         }
1714 
1715         /**
1716          * {@code true} if the program is currently playing live stream.
1717          *
1718          * <p>This may result in a slightly altered reception parameters,
1719          * usually targeted at reduced latency.
1720          */
isLive()1721         public boolean isLive() {
1722             return (mInfoFlags & FLAG_LIVE) != 0;
1723         }
1724 
1725         /**
1726          * {@code true} if radio stream is not playing, i.e. due to bad reception
1727          * conditions or buffering. In this state volume knob MAY be disabled to
1728          * prevent user increasing volume too much.
1729          *
1730          * <p>It does NOT mean the user has muted audio.
1731          */
isMuted()1732         public boolean isMuted() {
1733             return (mInfoFlags & FLAG_MUTED) != 0;
1734         }
1735 
1736         /**
1737          * {@code true} if radio station transmits traffic information
1738          * regularily.
1739          */
isTrafficProgram()1740         public boolean isTrafficProgram() {
1741             return (mInfoFlags & FLAG_TRAFFIC_PROGRAM) != 0;
1742         }
1743 
1744         /**
1745          * {@code true} if radio station transmits traffic information
1746          * at the very moment.
1747          */
isTrafficAnnouncementActive()1748         public boolean isTrafficAnnouncementActive() {
1749             return (mInfoFlags & FLAG_TRAFFIC_ANNOUNCEMENT) != 0;
1750         }
1751 
1752         /**
1753          * @return {@code true} if the signal has been acquired.
1754          */
1755         @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
isSignalAcquired()1756         public boolean isSignalAcquired() {
1757             return (mInfoFlags & FLAG_SIGNAL_ACQUIRED) != 0;
1758         }
1759         /**
1760          * @return {@code true} if HD Station Information Service (SIS) information is available.
1761          */
1762         @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
isHdSisAvailable()1763         public boolean isHdSisAvailable() {
1764             return (mInfoFlags & FLAG_HD_SIS_ACQUIRED) != 0;
1765         }
1766         /**
1767          * @return {@code true} if HD audio is available.
1768          */
1769         @FlaggedApi(Flags.FLAG_HD_RADIO_IMPROVED)
isHdAudioAvailable()1770         public boolean isHdAudioAvailable() {
1771             return (mInfoFlags & FLAG_HD_AUDIO_ACQUIRED) != 0;
1772         }
1773 
1774         /**
1775          * Get alert message.
1776          *
1777          * <p>Alert message can be sent from a radio station of technologies such as HD radio to
1778          * the radio users for some emergency events.
1779          *
1780          * @return alert message if it exists, otherwise {@code null}
1781          */
1782         @FlaggedApi(Flags.FLAG_HD_RADIO_EMERGENCY_ALERT_SYSTEM)
getAlert()1783         @Nullable public RadioAlert getAlert() {
1784             return mAlert;
1785         }
1786 
1787         /**
1788          * Signal quality (as opposed to the name) indication from 0 (no signal)
1789          * to 100 (excellent)
1790          * @return the signal quality indication.
1791          */
getSignalStrength()1792         public int getSignalStrength() {
1793             return mSignalQuality;
1794         }
1795 
1796         /** Metadata currently received from this station.
1797          *
1798          * @return current meta data received from this program, {@code null} if no metadata have
1799          * been received
1800          */
getMetadata()1801         public RadioMetadata getMetadata() {
1802             return mMetadata;
1803         }
1804 
1805         /**
1806          * A map of vendor-specific opaque strings, passed from HAL without changes.
1807          * Format of these strings can vary across vendors.
1808          *
1809          * <p>It may be used for extra features, that's not supported by a platform,
1810          * for example: paid-service=true; bitrate=320kbps.
1811          *
1812          * <p>Keys must be prefixed with unique vendor Java-style namespace,
1813          * e.g. 'com.somecompany.parameter1'.
1814          */
getVendorInfo()1815         public @NonNull Map<String, String> getVendorInfo() {
1816             return mVendorInfo;
1817         }
1818 
ProgramInfo(Parcel in)1819         private ProgramInfo(Parcel in) {
1820             mSelector = Objects.requireNonNull(in.readTypedObject(ProgramSelector.CREATOR));
1821             mLogicallyTunedTo = in.readTypedObject(ProgramSelector.Identifier.CREATOR);
1822             mPhysicallyTunedTo = in.readTypedObject(ProgramSelector.Identifier.CREATOR);
1823             mRelatedContent = in.createTypedArrayList(ProgramSelector.Identifier.CREATOR);
1824             mInfoFlags = in.readInt();
1825             mSignalQuality = in.readInt();
1826             mMetadata = in.readTypedObject(RadioMetadata.CREATOR);
1827             mVendorInfo = Utils.readStringMap(in);
1828             if (Flags.hdRadioEmergencyAlertSystem()) {
1829                 boolean hasNonNullAlert = in.readBoolean();
1830                 mAlert = hasNonNullAlert ? in.readTypedObject(RadioAlert.CREATOR) : null;
1831             } else {
1832                 mAlert = null;
1833             }
1834         }
1835 
1836         public static final @android.annotation.NonNull Parcelable.Creator<ProgramInfo> CREATOR
1837                 = new Parcelable.Creator<ProgramInfo>() {
1838             public ProgramInfo createFromParcel(Parcel in) {
1839                 return new ProgramInfo(in);
1840             }
1841 
1842             public ProgramInfo[] newArray(int size) {
1843                 return new ProgramInfo[size];
1844             }
1845         };
1846 
1847         @Override
writeToParcel(Parcel dest, int flags)1848         public void writeToParcel(Parcel dest, int flags) {
1849             dest.writeTypedObject(mSelector, flags);
1850             dest.writeTypedObject(mLogicallyTunedTo, flags);
1851             dest.writeTypedObject(mPhysicallyTunedTo, flags);
1852             Utils.writeTypedCollection(dest, mRelatedContent);
1853             dest.writeInt(mInfoFlags);
1854             dest.writeInt(mSignalQuality);
1855             dest.writeTypedObject(mMetadata, flags);
1856             Utils.writeStringMap(dest, mVendorInfo);
1857             if (Flags.hdRadioEmergencyAlertSystem()) {
1858                 if (mAlert == null) {
1859                     dest.writeBoolean(false);
1860                 } else {
1861                     dest.writeBoolean(true);
1862                     dest.writeTypedObject(mAlert, flags);
1863                 }
1864             }
1865         }
1866 
1867         @Override
describeContents()1868         public int describeContents() {
1869             return 0;
1870         }
1871 
1872         @NonNull
1873         @Override
toString()1874         public String toString() {
1875             String prorgamInfoString = "ProgramInfo"
1876                     + " [selector=" + mSelector
1877                     + ", logicallyTunedTo=" + Objects.toString(mLogicallyTunedTo)
1878                     + ", physicallyTunedTo=" + Objects.toString(mPhysicallyTunedTo)
1879                     + ", relatedContent=" + mRelatedContent.size()
1880                     + ", infoFlags=" + mInfoFlags
1881                     + ", signalQuality=" + mSignalQuality
1882                     + ", metadata=" + Objects.toString(mMetadata);
1883             if (Flags.hdRadioEmergencyAlertSystem()) {
1884                 prorgamInfoString += ", alert=" + Objects.toString(mAlert);
1885             }
1886             prorgamInfoString += "]";
1887             return prorgamInfoString;
1888         }
1889 
1890         @Override
hashCode()1891         public int hashCode() {
1892             if (Flags.hdRadioEmergencyAlertSystem()) {
1893                 return Objects.hash(mSelector, mLogicallyTunedTo, mPhysicallyTunedTo,
1894                         mRelatedContent, mInfoFlags, mSignalQuality, mMetadata, mVendorInfo,
1895                         mAlert);
1896             }
1897             return Objects.hash(mSelector, mLogicallyTunedTo, mPhysicallyTunedTo,
1898                 mRelatedContent, mInfoFlags, mSignalQuality, mMetadata, mVendorInfo);
1899         }
1900 
1901         @Override
equals(@ullable Object obj)1902         public boolean equals(@Nullable Object obj) {
1903             if (this == obj) return true;
1904             if (!(obj instanceof ProgramInfo)) return false;
1905             ProgramInfo other = (ProgramInfo) obj;
1906 
1907             if (!mSelector.strictEquals(other.mSelector)) return false;
1908             if (!Objects.equals(mLogicallyTunedTo, other.mLogicallyTunedTo)) return false;
1909             if (!Objects.equals(mPhysicallyTunedTo, other.mPhysicallyTunedTo)) return false;
1910             if (!Objects.equals(mRelatedContent, other.mRelatedContent)) return false;
1911             if (mInfoFlags != other.mInfoFlags) return false;
1912             if (mSignalQuality != other.mSignalQuality) return false;
1913             if (!Objects.equals(mMetadata, other.mMetadata)) return false;
1914             if (!Objects.equals(mVendorInfo, other.mVendorInfo)) return false;
1915 
1916             if (Flags.hdRadioEmergencyAlertSystem()) {
1917                 return Objects.equals(mAlert, other.mAlert);
1918             }
1919             return true;
1920         }
1921     }
1922 
1923 
1924     /**
1925      * Returns a list of descriptors for all broadcast radio modules present on the device.
1926      * @param modules An List of {@link ModuleProperties} where the list will be returned.
1927      * @return
1928      * <ul>
1929      *  <li>{@link #STATUS_OK} in case of success, </li>
1930      *  <li>{@link #STATUS_ERROR} in case of unspecified error, </li>
1931      *  <li>{@link #STATUS_NO_INIT} if the native service cannot be reached, </li>
1932      *  <li>{@link #STATUS_BAD_VALUE} if modules is null, </li>
1933      *  <li>{@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails, </li>
1934      * </ul>
1935      */
1936     @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO)
1937     @RadioStatusType
listModules(List<ModuleProperties> modules)1938     public int listModules(List<ModuleProperties> modules) {
1939         if (modules == null) {
1940             Log.e(TAG, "the output list must not be empty");
1941             return STATUS_BAD_VALUE;
1942         }
1943 
1944         Log.d(TAG, "Listing available tuners...");
1945         List<ModuleProperties> returnedList;
1946         try {
1947             returnedList = mService.listModules();
1948         } catch (RemoteException e) {
1949             Log.e(TAG, "Failed listing available tuners", e);
1950             return STATUS_DEAD_OBJECT;
1951         }
1952 
1953         if (returnedList == null) {
1954             Log.e(TAG, "Returned list was a null");
1955             return STATUS_ERROR;
1956         }
1957 
1958         modules.addAll(returnedList);
1959         return STATUS_OK;
1960     }
1961 
nativeListModules(List<ModuleProperties> modules)1962     private native int nativeListModules(List<ModuleProperties> modules);
1963 
1964     /**
1965      * Open an interface to control a tuner on a given broadcast radio module.
1966      *
1967      * <p>Optionally selects and applies the configuration passed as "config" argument.
1968      * @param moduleId radio module identifier {@link ModuleProperties#getId()}. Mandatory.
1969      * @param config desired band and configuration to apply when enabling the hardware module.
1970      * optional, can be null.
1971      * @param withAudio {@code true} to request a tuner with an audio source.
1972      * This tuner is intended for live listening or recording or a radio program.
1973      * If {@code false}, the tuner can only be used to retrieve program information.
1974      * @param callback {@link RadioTuner.Callback} interface. Mandatory.
1975      * @param handler the Handler on which the callbacks will be received.
1976      * Can be null if default handler is OK.
1977      * @return a valid {@link RadioTuner} interface in case of success or null in case of error.
1978      */
1979     @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO)
openTuner(int moduleId, BandConfig config, boolean withAudio, RadioTuner.Callback callback, Handler handler)1980     public RadioTuner openTuner(int moduleId, BandConfig config, boolean withAudio,
1981             RadioTuner.Callback callback, Handler handler) {
1982         if (callback == null) {
1983             throw new IllegalArgumentException("callback must not be empty");
1984         }
1985 
1986         Log.d(TAG, "Opening tuner " + moduleId + "...");
1987 
1988         ITuner tuner;
1989         TunerCallbackAdapter halCallback = new TunerCallbackAdapter(callback, handler);
1990         try {
1991             tuner = mService.openTuner(moduleId, config, withAudio, halCallback);
1992         } catch (RemoteException | IllegalArgumentException | IllegalStateException ex) {
1993             Log.e(TAG, "Failed to open tuner", ex);
1994             return null;
1995         }
1996         if (tuner == null) {
1997             Log.e(TAG, "Failed to open tuner");
1998             return null;
1999         }
2000         return new TunerAdapter(tuner, halCallback,
2001                 config != null ? config.getType() : BAND_INVALID);
2002     }
2003 
2004     private final Map<Announcement.OnListUpdatedListener, ICloseHandle> mAnnouncementListeners =
2005             new HashMap<>();
2006 
2007     /**
2008      * Adds new announcement listener.
2009      *
2010      * @param enabledAnnouncementTypes a set of announcement types to listen to
2011      * @param listener announcement listener
2012      */
2013     @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO)
addAnnouncementListener(@onNull Set<Integer> enabledAnnouncementTypes, @NonNull Announcement.OnListUpdatedListener listener)2014     public void addAnnouncementListener(@NonNull Set<Integer> enabledAnnouncementTypes,
2015             @NonNull Announcement.OnListUpdatedListener listener) {
2016         addAnnouncementListener(cmd -> cmd.run(), enabledAnnouncementTypes, listener);
2017     }
2018 
2019     /**
2020      * Adds new announcement listener with executor.
2021      *
2022      * @param executor the executor
2023      * @param enabledAnnouncementTypes a set of announcement types to listen to
2024      * @param listener announcement listener
2025      */
2026     @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO)
addAnnouncementListener(@onNull @allbackExecutor Executor executor, @NonNull Set<Integer> enabledAnnouncementTypes, @NonNull Announcement.OnListUpdatedListener listener)2027     public void addAnnouncementListener(@NonNull @CallbackExecutor Executor executor,
2028             @NonNull Set<Integer> enabledAnnouncementTypes,
2029             @NonNull Announcement.OnListUpdatedListener listener) {
2030         Objects.requireNonNull(executor);
2031         Objects.requireNonNull(listener);
2032         int[] types = enabledAnnouncementTypes.stream().mapToInt(Integer::intValue).toArray();
2033         IAnnouncementListener listenerIface = new IAnnouncementListener.Stub() {
2034             public void onListUpdated(List<Announcement> activeAnnouncements) {
2035                 executor.execute(() -> listener.onListUpdated(activeAnnouncements));
2036             }
2037         };
2038         synchronized (mAnnouncementListeners) {
2039             ICloseHandle closeHandle = null;
2040             try {
2041                 closeHandle = mService.addAnnouncementListener(types, listenerIface);
2042             } catch (RemoteException ex) {
2043                 ex.rethrowFromSystemServer();
2044             }
2045             Objects.requireNonNull(closeHandle);
2046             ICloseHandle oldCloseHandle = mAnnouncementListeners.put(listener, closeHandle);
2047             if (oldCloseHandle != null) Utils.close(oldCloseHandle);
2048         }
2049     }
2050 
2051     /**
2052      * Removes previously registered announcement listener.
2053      *
2054      * @param listener announcement listener, previously registered with
2055      *        {@link #addAnnouncementListener(Executor, Set, Announcement.OnListUpdatedListener)}
2056      *        or {@link #addAnnouncementListener(Set, Announcement.OnListUpdatedListener)}
2057      */
2058     @RequiresPermission(Manifest.permission.ACCESS_BROADCAST_RADIO)
removeAnnouncementListener(@onNull Announcement.OnListUpdatedListener listener)2059     public void removeAnnouncementListener(@NonNull Announcement.OnListUpdatedListener listener) {
2060         Objects.requireNonNull(listener);
2061         synchronized (mAnnouncementListeners) {
2062             ICloseHandle closeHandle = mAnnouncementListeners.remove(listener);
2063             if (closeHandle != null) Utils.close(closeHandle);
2064         }
2065     }
2066 
2067     @NonNull private final Context mContext;
2068     @NonNull private final IRadioService mService;
2069 
2070     /**
2071      * @hide
2072      */
RadioManager(Context context)2073     public RadioManager(Context context) throws ServiceNotFoundException {
2074         this(context, IRadioService.Stub.asInterface(ServiceManager.getServiceOrThrow(
2075                 Context.RADIO_SERVICE)));
2076     }
2077 
2078     /**
2079      * @hide
2080      */
2081     @VisibleForTesting
RadioManager(Context context, IRadioService service)2082     public RadioManager(Context context, IRadioService service) {
2083         mContext = context;
2084         mService = service;
2085     }
2086 }
2087