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