RadioManager.java revision c4cd823a2e4ce62d3a1c6ee6c71bb406c564608c
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
17package android.hardware.radio;
18
19import android.annotation.IntDef;
20import android.annotation.NonNull;
21import android.annotation.SystemApi;
22import android.annotation.SystemService;
23import android.content.Context;
24import android.os.Handler;
25import android.os.Parcel;
26import android.os.Parcelable;
27import android.os.RemoteException;
28import android.os.ServiceManager;
29import android.os.ServiceManager.ServiceNotFoundException;
30import android.text.TextUtils;
31import android.util.Log;
32
33import java.lang.annotation.Retention;
34import java.lang.annotation.RetentionPolicy;
35import java.util.Arrays;
36import java.util.List;
37
38/**
39 * The RadioManager class allows to control a broadcast radio tuner present on the device.
40 * It provides data structures and methods to query for available radio modules, list their
41 * properties and open an interface to control tuning operations and receive callbacks when
42 * asynchronous operations complete or events occur.
43 * @hide
44 */
45@SystemApi
46@SystemService(Context.RADIO_SERVICE)
47public class RadioManager {
48    private static final String TAG = "RadioManager";
49
50    /** Method return status: successful operation */
51    public static final int STATUS_OK = 0;
52    /** Method return status: unspecified error */
53    public static final int STATUS_ERROR = Integer.MIN_VALUE;
54    /** Method return status: permission denied */
55    public static final int STATUS_PERMISSION_DENIED = -1;
56    /** Method return status: initialization failure */
57    public static final int STATUS_NO_INIT = -19;
58    /** Method return status: invalid argument provided */
59    public static final int STATUS_BAD_VALUE = -22;
60    /** Method return status: cannot reach service */
61    public static final int STATUS_DEAD_OBJECT = -32;
62    /** Method return status: invalid or out of sequence operation */
63    public static final int STATUS_INVALID_OPERATION = -38;
64    /** Method return status: time out before operation completion */
65    public static final int STATUS_TIMED_OUT = -110;
66
67
68    // keep in sync with radio_class_t in /system/core/incluse/system/radio.h
69    /** Radio module class supporting FM (including HD radio) and AM */
70    public static final int CLASS_AM_FM = 0;
71    /** Radio module class supporting satellite radio */
72    public static final int CLASS_SAT = 1;
73    /** Radio module class supporting Digital terrestrial radio */
74    public static final int CLASS_DT = 2;
75
76    public static final int BAND_INVALID = -1;
77    /** AM radio band (LW/MW/SW).
78     * @see BandDescriptor */
79    public static final int BAND_AM = 0;
80    /** FM radio band.
81     * @see BandDescriptor */
82    public static final int BAND_FM = 1;
83    /** FM HD radio or DRM  band.
84     * @see BandDescriptor */
85    public static final int BAND_FM_HD = 2;
86    /** AM HD radio or DRM band.
87     * @see BandDescriptor */
88    public static final int BAND_AM_HD = 3;
89    @IntDef(prefix = { "BAND_" }, value = {
90        BAND_INVALID,
91        BAND_AM,
92        BAND_FM,
93        BAND_AM_HD,
94        BAND_FM_HD,
95    })
96    @Retention(RetentionPolicy.SOURCE)
97    public @interface Band {}
98
99    // keep in sync with radio_region_t in /system/core/incluse/system/radio.h
100    /** Africa, Europe.
101     * @see BandDescriptor */
102    public static final int REGION_ITU_1  = 0;
103    /** Americas.
104     * @see BandDescriptor */
105    public static final int REGION_ITU_2  = 1;
106    /** Russia.
107     * @see BandDescriptor */
108    public static final int REGION_OIRT   = 2;
109    /** Japan.
110     * @see BandDescriptor */
111    public static final int REGION_JAPAN  = 3;
112    /** Korea.
113     * @see BandDescriptor */
114    public static final int REGION_KOREA  = 4;
115
116    /*****************************************************************************
117     * Lists properties, options and radio bands supported by a given broadcast radio module.
118     * Each module has a unique ID used to address it when calling RadioManager APIs.
119     * Module properties are returned by {@link #listModules(List <ModuleProperties>)} method.
120     ****************************************************************************/
121    public static class ModuleProperties implements Parcelable {
122
123        private final int mId;
124        @NonNull private final String mServiceName;
125        private final int mClassId;
126        private final String mImplementor;
127        private final String mProduct;
128        private final String mVersion;
129        private final String mSerial;
130        private final int mNumTuners;
131        private final int mNumAudioSources;
132        private final boolean mIsCaptureSupported;
133        private final BandDescriptor[] mBands;
134        private final boolean mIsBgScanSupported;
135        private final String mVendorExension;
136
137        ModuleProperties(int id, String serviceName, int classId, String implementor,
138                String product, String version, String serial, int numTuners, int numAudioSources,
139                boolean isCaptureSupported, BandDescriptor[] bands, boolean isBgScanSupported,
140                String vendorExension) {
141            mId = id;
142            mServiceName = TextUtils.isEmpty(serviceName) ? "default" : serviceName;
143            mClassId = classId;
144            mImplementor = implementor;
145            mProduct = product;
146            mVersion = version;
147            mSerial = serial;
148            mNumTuners = numTuners;
149            mNumAudioSources = numAudioSources;
150            mIsCaptureSupported = isCaptureSupported;
151            mBands = bands;
152            mIsBgScanSupported = isBgScanSupported;
153            mVendorExension = vendorExension;
154        }
155
156
157        /** Unique module identifier provided by the native service.
158         * For use with {@link #openTuner(int, BandConfig, boolean, Callback, Handler)}.
159         * @return the radio module unique identifier.
160         */
161        public int getId() {
162            return mId;
163        }
164
165        /**
166         * Module service (driver) name as registered with HIDL.
167         * @return the module service name.
168         */
169        public @NonNull String getServiceName() {
170            return mServiceName;
171        }
172
173        /** Module class identifier: {@link #CLASS_AM_FM}, {@link #CLASS_SAT}, {@link #CLASS_DT}
174         * @return the radio module class identifier.
175         */
176        public int getClassId() {
177            return mClassId;
178        }
179
180        /** Human readable broadcast radio module implementor
181         * @return the name of the radio module implementator.
182         */
183        public String getImplementor() {
184            return mImplementor;
185        }
186
187        /** Human readable broadcast radio module product name
188         * @return the radio module product name.
189         */
190        public String getProduct() {
191            return mProduct;
192        }
193
194        /** Human readable broadcast radio module version number
195         * @return the radio module version.
196         */
197        public String getVersion() {
198            return mVersion;
199        }
200
201        /** Radio module serial number.
202         * Can be used for subscription services.
203         * @return the radio module serial number.
204         */
205        public String getSerial() {
206            return mSerial;
207        }
208
209        /** Number of tuners available.
210         * This is the number of tuners that can be open simultaneously.
211         * @return the number of tuners supported.
212         */
213        public int getNumTuners() {
214            return mNumTuners;
215        }
216
217        /** Number tuner audio sources available. Must be less or equal to getNumTuners().
218         * When more than one tuner is supported, one is usually for playback and has one
219         * associated audio source and the other is for pre scanning and building a
220         * program list.
221         * @return the number of audio sources available.
222         */
223        public int getNumAudioSources() {
224            return mNumAudioSources;
225        }
226
227        /** {@code true} if audio capture is possible from radio tuner output.
228         * This indicates if routing to audio devices not connected to the same HAL as the FM radio
229         * is possible (e.g. to USB) or DAR (Digital Audio Recorder) feature can be implemented.
230         * @return {@code true} if audio capture is possible, {@code false} otherwise.
231         */
232        public boolean isCaptureSupported() {
233            return mIsCaptureSupported;
234        }
235
236        /**
237         * {@code true} if the module supports background scanning. At the given time it may not
238         * be available though, see {@link RadioTuner#startBackgroundScan()}.
239         *
240         * @return {@code true} if background scanning is supported (not necessary available
241         * at a given time), {@code false} otherwise.
242         */
243        public boolean isBackgroundScanningSupported() {
244            return mIsBgScanSupported;
245        }
246
247        /**
248         * Opaque vendor-specific string, passed from HAL without changes.
249         * Format of this string can vary across vendors.
250         *
251         * It may be used for extra features, that's not supported by a platform,
252         * for example: "preset-slots=6;ultra-hd-capable=false".
253         *
254         * Client application MUST verify vendor/product name from the
255         * ModuleProperties class before doing any interpretation of this value.
256         */
257        public @NonNull String getVendorExension() {
258            return mVendorExension == null ? "" : mVendorExension;
259        }
260
261        /** List of descriptors for all bands supported by this module.
262         * @return an array of {@link BandDescriptor}.
263         */
264        public BandDescriptor[] getBands() {
265            return mBands;
266        }
267
268        private ModuleProperties(Parcel in) {
269            mId = in.readInt();
270            String serviceName = in.readString();
271            mServiceName = TextUtils.isEmpty(serviceName) ? "default" : serviceName;
272            mClassId = in.readInt();
273            mImplementor = in.readString();
274            mProduct = in.readString();
275            mVersion = in.readString();
276            mSerial = in.readString();
277            mNumTuners = in.readInt();
278            mNumAudioSources = in.readInt();
279            mIsCaptureSupported = in.readInt() == 1;
280            Parcelable[] tmp = in.readParcelableArray(BandDescriptor.class.getClassLoader());
281            mBands = new BandDescriptor[tmp.length];
282            for (int i = 0; i < tmp.length; i++) {
283                mBands[i] = (BandDescriptor) tmp[i];
284            }
285            mIsBgScanSupported = in.readInt() == 1;
286            mVendorExension = in.readString();
287        }
288
289        public static final Parcelable.Creator<ModuleProperties> CREATOR
290                = new Parcelable.Creator<ModuleProperties>() {
291            public ModuleProperties createFromParcel(Parcel in) {
292                return new ModuleProperties(in);
293            }
294
295            public ModuleProperties[] newArray(int size) {
296                return new ModuleProperties[size];
297            }
298        };
299
300        @Override
301        public void writeToParcel(Parcel dest, int flags) {
302            dest.writeInt(mId);
303            dest.writeString(mServiceName);
304            dest.writeInt(mClassId);
305            dest.writeString(mImplementor);
306            dest.writeString(mProduct);
307            dest.writeString(mVersion);
308            dest.writeString(mSerial);
309            dest.writeInt(mNumTuners);
310            dest.writeInt(mNumAudioSources);
311            dest.writeInt(mIsCaptureSupported ? 1 : 0);
312            dest.writeParcelableArray(mBands, flags);
313            dest.writeInt(mIsBgScanSupported ? 1 : 0);
314            dest.writeString(mVendorExension);
315        }
316
317        @Override
318        public int describeContents() {
319            return 0;
320        }
321
322        @Override
323        public String toString() {
324            return "ModuleProperties [mId=" + mId
325                    + ", mServiceName=" + mServiceName + ", mClassId=" + mClassId
326                    + ", mImplementor=" + mImplementor + ", mProduct=" + mProduct
327                    + ", mVersion=" + mVersion + ", mSerial=" + mSerial
328                    + ", mNumTuners=" + mNumTuners
329                    + ", mNumAudioSources=" + mNumAudioSources
330                    + ", mIsCaptureSupported=" + mIsCaptureSupported
331                    + ", mIsBgScanSupported=" + mIsBgScanSupported
332                    + ", mBands=" + Arrays.toString(mBands) + "]";
333        }
334
335        @Override
336        public int hashCode() {
337            final int prime = 31;
338            int result = 1;
339            result = prime * result + mId;
340            result = prime * result + mServiceName.hashCode();
341            result = prime * result + mClassId;
342            result = prime * result + ((mImplementor == null) ? 0 : mImplementor.hashCode());
343            result = prime * result + ((mProduct == null) ? 0 : mProduct.hashCode());
344            result = prime * result + ((mVersion == null) ? 0 : mVersion.hashCode());
345            result = prime * result + ((mSerial == null) ? 0 : mSerial.hashCode());
346            result = prime * result + mNumTuners;
347            result = prime * result + mNumAudioSources;
348            result = prime * result + (mIsCaptureSupported ? 1 : 0);
349            result = prime * result + Arrays.hashCode(mBands);
350            result = prime * result + (mIsBgScanSupported ? 1 : 0);
351            result = prime * result + ((mVendorExension == null) ? 0 : mVendorExension.hashCode());
352            return result;
353        }
354
355        @Override
356        public boolean equals(Object obj) {
357            if (this == obj)
358                return true;
359            if (!(obj instanceof ModuleProperties))
360                return false;
361            ModuleProperties other = (ModuleProperties) obj;
362            if (mId != other.getId())
363                return false;
364            if (!TextUtils.equals(mServiceName, other.mServiceName)) return false;
365            if (mClassId != other.getClassId())
366                return false;
367            if (mImplementor == null) {
368                if (other.getImplementor() != null)
369                    return false;
370            } else if (!mImplementor.equals(other.getImplementor()))
371                return false;
372            if (mProduct == null) {
373                if (other.getProduct() != null)
374                    return false;
375            } else if (!mProduct.equals(other.getProduct()))
376                return false;
377            if (mVersion == null) {
378                if (other.getVersion() != null)
379                    return false;
380            } else if (!mVersion.equals(other.getVersion()))
381                return false;
382            if (mSerial == null) {
383                if (other.getSerial() != null)
384                    return false;
385            } else if (!mSerial.equals(other.getSerial()))
386                return false;
387            if (mNumTuners != other.getNumTuners())
388                return false;
389            if (mNumAudioSources != other.getNumAudioSources())
390                return false;
391            if (mIsCaptureSupported != other.isCaptureSupported())
392                return false;
393            if (!Arrays.equals(mBands, other.getBands()))
394                return false;
395            if (mIsBgScanSupported != other.isBackgroundScanningSupported())
396                return false;
397            if (!TextUtils.equals(mVendorExension, other.mVendorExension))
398                return false;
399            return true;
400        }
401    }
402
403    /** Radio band descriptor: an element in ModuleProperties bands array.
404     * It is either an instance of {@link FmBandDescriptor} or {@link AmBandDescriptor} */
405    public static class BandDescriptor implements Parcelable {
406
407        private final int mRegion;
408        private final int mType;
409        private final int mLowerLimit;
410        private final int mUpperLimit;
411        private final int mSpacing;
412
413        BandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing) {
414            if (type != BAND_AM && type != BAND_FM && type != BAND_FM_HD && type != BAND_AM_HD) {
415                throw new IllegalArgumentException("Unsupported band: " + type);
416            }
417            mRegion = region;
418            mType = type;
419            mLowerLimit = lowerLimit;
420            mUpperLimit = upperLimit;
421            mSpacing = spacing;
422        }
423
424        /** Region this band applies to. E.g. {@link #REGION_ITU_1}
425         * @return the region this band is associated to.
426         */
427        public int getRegion() {
428            return mRegion;
429        }
430        /** Band type, e.g {@link #BAND_FM}. Defines the subclass this descriptor can be cast to:
431         * <ul>
432         *  <li>{@link #BAND_FM} or {@link #BAND_FM_HD} cast to {@link FmBandDescriptor}, </li>
433         *  <li>{@link #BAND_AM} cast to {@link AmBandDescriptor}, </li>
434         * </ul>
435         * @return the band type.
436         */
437        public int getType() {
438            return mType;
439        }
440
441        /**
442         * Checks if the band is either AM or AM_HD.
443         *
444         * @return {@code true}, if band is AM or AM_HD.
445         */
446        public boolean isAmBand() {
447            return mType == BAND_AM || mType == BAND_AM_HD;
448        }
449
450        /**
451         * Checks if the band is either FM or FM_HD.
452         *
453         * @return {@code true}, if band is FM or FM_HD.
454         */
455        public boolean isFmBand() {
456            return mType == BAND_FM || mType == BAND_FM_HD;
457        }
458
459        /** Lower band limit expressed in units according to band type.
460         * Currently all defined band types express channels as frequency in kHz
461         * @return the lower band limit.
462         */
463        public int getLowerLimit() {
464            return mLowerLimit;
465        }
466        /** Upper band limit expressed in units according to band type.
467         * Currently all defined band types express channels as frequency in kHz
468         * @return the upper band limit.
469         */
470        public int getUpperLimit() {
471            return mUpperLimit;
472        }
473        /** Channel spacing in units according to band type.
474         * Currently all defined band types express channels as frequency in kHz
475         * @return the channel spacing.
476         */
477        public int getSpacing() {
478            return mSpacing;
479        }
480
481        private BandDescriptor(Parcel in) {
482            mRegion = in.readInt();
483            mType = in.readInt();
484            mLowerLimit = in.readInt();
485            mUpperLimit = in.readInt();
486            mSpacing = in.readInt();
487        }
488
489        private static int lookupTypeFromParcel(Parcel in) {
490            int pos = in.dataPosition();
491            in.readInt();  // skip region
492            int type = in.readInt();
493            in.setDataPosition(pos);
494            return type;
495        }
496
497        public static final Parcelable.Creator<BandDescriptor> CREATOR
498                = new Parcelable.Creator<BandDescriptor>() {
499            public BandDescriptor createFromParcel(Parcel in) {
500                int type = lookupTypeFromParcel(in);
501                switch (type) {
502                    case BAND_FM:
503                    case BAND_FM_HD:
504                        return new FmBandDescriptor(in);
505                    case BAND_AM:
506                    case BAND_AM_HD:
507                        return new AmBandDescriptor(in);
508                    default:
509                        throw new IllegalArgumentException("Unsupported band: " + type);
510                }
511            }
512
513            public BandDescriptor[] newArray(int size) {
514                return new BandDescriptor[size];
515            }
516        };
517
518        @Override
519        public void writeToParcel(Parcel dest, int flags) {
520            dest.writeInt(mRegion);
521            dest.writeInt(mType);
522            dest.writeInt(mLowerLimit);
523            dest.writeInt(mUpperLimit);
524            dest.writeInt(mSpacing);
525        }
526
527        @Override
528        public int describeContents() {
529            return 0;
530        }
531
532        @Override
533        public String toString() {
534            return "BandDescriptor [mRegion=" + mRegion + ", mType=" + mType + ", mLowerLimit="
535                    + mLowerLimit + ", mUpperLimit=" + mUpperLimit + ", mSpacing=" + mSpacing + "]";
536        }
537
538        @Override
539        public int hashCode() {
540            final int prime = 31;
541            int result = 1;
542            result = prime * result + mRegion;
543            result = prime * result + mType;
544            result = prime * result + mLowerLimit;
545            result = prime * result + mUpperLimit;
546            result = prime * result + mSpacing;
547            return result;
548        }
549
550        @Override
551        public boolean equals(Object obj) {
552            if (this == obj)
553                return true;
554            if (!(obj instanceof BandDescriptor))
555                return false;
556            BandDescriptor other = (BandDescriptor) obj;
557            if (mRegion != other.getRegion())
558                return false;
559            if (mType != other.getType())
560                return false;
561            if (mLowerLimit != other.getLowerLimit())
562                return false;
563            if (mUpperLimit != other.getUpperLimit())
564                return false;
565            if (mSpacing != other.getSpacing())
566                return false;
567            return true;
568        }
569    }
570
571    /** FM band descriptor
572     * @see #BAND_FM
573     * @see #BAND_FM_HD */
574    public static class FmBandDescriptor extends BandDescriptor {
575        private final boolean mStereo;
576        private final boolean mRds;
577        private final boolean mTa;
578        private final boolean mAf;
579        private final boolean mEa;
580
581        FmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing,
582                boolean stereo, boolean rds, boolean ta, boolean af, boolean ea) {
583            super(region, type, lowerLimit, upperLimit, spacing);
584            mStereo = stereo;
585            mRds = rds;
586            mTa = ta;
587            mAf = af;
588            mEa = ea;
589        }
590
591        /** Stereo is supported
592         * @return {@code true} if stereo is supported, {@code false} otherwise.
593         */
594        public boolean isStereoSupported() {
595            return mStereo;
596        }
597        /** RDS or RBDS(if region is ITU2) is supported
598         * @return {@code true} if RDS or RBDS is supported, {@code false} otherwise.
599         */
600        public boolean isRdsSupported() {
601            return mRds;
602        }
603        /** Traffic announcement is supported
604         * @return {@code true} if TA is supported, {@code false} otherwise.
605         */
606        public boolean isTaSupported() {
607            return mTa;
608        }
609        /** Alternate Frequency Switching is supported
610         * @return {@code true} if AF switching is supported, {@code false} otherwise.
611         */
612        public boolean isAfSupported() {
613            return mAf;
614        }
615
616        /** Emergency Announcement is supported
617         * @return {@code true} if Emergency annoucement is supported, {@code false} otherwise.
618         */
619        public boolean isEaSupported() {
620            return mEa;
621        }
622
623        /* Parcelable implementation */
624        private FmBandDescriptor(Parcel in) {
625            super(in);
626            mStereo = in.readByte() == 1;
627            mRds = in.readByte() == 1;
628            mTa = in.readByte() == 1;
629            mAf = in.readByte() == 1;
630            mEa = in.readByte() == 1;
631        }
632
633        public static final Parcelable.Creator<FmBandDescriptor> CREATOR
634                = new Parcelable.Creator<FmBandDescriptor>() {
635            public FmBandDescriptor createFromParcel(Parcel in) {
636                return new FmBandDescriptor(in);
637            }
638
639            public FmBandDescriptor[] newArray(int size) {
640                return new FmBandDescriptor[size];
641            }
642        };
643
644        @Override
645        public void writeToParcel(Parcel dest, int flags) {
646            super.writeToParcel(dest, flags);
647            dest.writeByte((byte) (mStereo ? 1 : 0));
648            dest.writeByte((byte) (mRds ? 1 : 0));
649            dest.writeByte((byte) (mTa ? 1 : 0));
650            dest.writeByte((byte) (mAf ? 1 : 0));
651            dest.writeByte((byte) (mEa ? 1 : 0));
652        }
653
654        @Override
655        public int describeContents() {
656            return 0;
657        }
658
659        @Override
660        public String toString() {
661            return "FmBandDescriptor [ "+ super.toString() + " mStereo=" + mStereo
662                    + ", mRds=" + mRds + ", mTa=" + mTa + ", mAf=" + mAf +
663                    ", mEa =" + mEa + "]";
664        }
665
666        @Override
667        public int hashCode() {
668            final int prime = 31;
669            int result = super.hashCode();
670            result = prime * result + (mStereo ? 1 : 0);
671            result = prime * result + (mRds ? 1 : 0);
672            result = prime * result + (mTa ? 1 : 0);
673            result = prime * result + (mAf ? 1 : 0);
674            result = prime * result + (mEa ? 1 : 0);
675            return result;
676        }
677
678        @Override
679        public boolean equals(Object obj) {
680            if (this == obj)
681                return true;
682            if (!super.equals(obj))
683                return false;
684            if (!(obj instanceof FmBandDescriptor))
685                return false;
686            FmBandDescriptor other = (FmBandDescriptor) obj;
687            if (mStereo != other.isStereoSupported())
688                return false;
689            if (mRds != other.isRdsSupported())
690                return false;
691            if (mTa != other.isTaSupported())
692                return false;
693            if (mAf != other.isAfSupported())
694                return false;
695            if (mEa != other.isEaSupported())
696                return false;
697            return true;
698        }
699    }
700
701    /** AM band descriptor.
702     * @see #BAND_AM */
703    public static class AmBandDescriptor extends BandDescriptor {
704
705        private final boolean mStereo;
706
707        AmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing,
708                boolean stereo) {
709            super(region, type, lowerLimit, upperLimit, spacing);
710            mStereo = stereo;
711        }
712
713        /** Stereo is supported
714         *  @return {@code true} if stereo is supported, {@code false} otherwise.
715         */
716        public boolean isStereoSupported() {
717            return mStereo;
718        }
719
720        private AmBandDescriptor(Parcel in) {
721            super(in);
722            mStereo = in.readByte() == 1;
723        }
724
725        public static final Parcelable.Creator<AmBandDescriptor> CREATOR
726                = new Parcelable.Creator<AmBandDescriptor>() {
727            public AmBandDescriptor createFromParcel(Parcel in) {
728                return new AmBandDescriptor(in);
729            }
730
731            public AmBandDescriptor[] newArray(int size) {
732                return new AmBandDescriptor[size];
733            }
734        };
735
736        @Override
737        public void writeToParcel(Parcel dest, int flags) {
738            super.writeToParcel(dest, flags);
739            dest.writeByte((byte) (mStereo ? 1 : 0));
740        }
741
742        @Override
743        public int describeContents() {
744            return 0;
745        }
746
747        @Override
748        public String toString() {
749            return "AmBandDescriptor [ "+ super.toString() + " mStereo=" + mStereo + "]";
750        }
751
752        @Override
753        public int hashCode() {
754            final int prime = 31;
755            int result = super.hashCode();
756            result = prime * result + (mStereo ? 1 : 0);
757            return result;
758        }
759
760        @Override
761        public boolean equals(Object obj) {
762            if (this == obj)
763                return true;
764            if (!super.equals(obj))
765                return false;
766            if (!(obj instanceof AmBandDescriptor))
767                return false;
768            AmBandDescriptor other = (AmBandDescriptor) obj;
769            if (mStereo != other.isStereoSupported())
770                return false;
771            return true;
772        }
773    }
774
775
776    /** Radio band configuration. */
777    public static class BandConfig implements Parcelable {
778
779        final BandDescriptor mDescriptor;
780
781        BandConfig(BandDescriptor descriptor) {
782            mDescriptor = descriptor;
783        }
784
785        BandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing) {
786            mDescriptor = new BandDescriptor(region, type, lowerLimit, upperLimit, spacing);
787        }
788
789        private BandConfig(Parcel in) {
790            mDescriptor = new BandDescriptor(in);
791        }
792
793        BandDescriptor getDescriptor() {
794            return mDescriptor;
795        }
796
797        /** Region this band applies to. E.g. {@link #REGION_ITU_1}
798         *  @return the region associated with this band.
799         */
800        public int getRegion() {
801            return mDescriptor.getRegion();
802        }
803        /** Band type, e.g {@link #BAND_FM}. Defines the subclass this descriptor can be cast to:
804         * <ul>
805         *  <li>{@link #BAND_FM} or {@link #BAND_FM_HD} cast to {@link FmBandDescriptor}, </li>
806         *  <li>{@link #BAND_AM} cast to {@link AmBandDescriptor}, </li>
807         * </ul>
808         *  @return the band type.
809         */
810        public int getType() {
811            return mDescriptor.getType();
812        }
813        /** Lower band limit expressed in units according to band type.
814         * Currently all defined band types express channels as frequency in kHz
815         *  @return the lower band limit.
816         */
817        public int getLowerLimit() {
818            return mDescriptor.getLowerLimit();
819        }
820        /** Upper band limit expressed in units according to band type.
821         * Currently all defined band types express channels as frequency in kHz
822         *  @return the upper band limit.
823         */
824        public int getUpperLimit() {
825            return mDescriptor.getUpperLimit();
826        }
827        /** Channel spacing in units according to band type.
828         * Currently all defined band types express channels as frequency in kHz
829         *  @return the channel spacing.
830         */
831        public int getSpacing() {
832            return mDescriptor.getSpacing();
833        }
834
835
836        public static final Parcelable.Creator<BandConfig> CREATOR
837                = new Parcelable.Creator<BandConfig>() {
838            public BandConfig createFromParcel(Parcel in) {
839                int type = BandDescriptor.lookupTypeFromParcel(in);
840                switch (type) {
841                    case BAND_FM:
842                    case BAND_FM_HD:
843                        return new FmBandConfig(in);
844                    case BAND_AM:
845                    case BAND_AM_HD:
846                        return new AmBandConfig(in);
847                    default:
848                        throw new IllegalArgumentException("Unsupported band: " + type);
849                }
850            }
851
852            public BandConfig[] newArray(int size) {
853                return new BandConfig[size];
854            }
855        };
856
857        @Override
858        public void writeToParcel(Parcel dest, int flags) {
859            mDescriptor.writeToParcel(dest, flags);
860        }
861
862        @Override
863        public int describeContents() {
864            return 0;
865        }
866
867        @Override
868        public String toString() {
869            return "BandConfig [ " + mDescriptor.toString() + "]";
870        }
871
872        @Override
873        public int hashCode() {
874            final int prime = 31;
875            int result = 1;
876            result = prime * result + mDescriptor.hashCode();
877            return result;
878        }
879
880        @Override
881        public boolean equals(Object obj) {
882            if (this == obj)
883                return true;
884            if (!(obj instanceof BandConfig))
885                return false;
886            BandConfig other = (BandConfig) obj;
887            BandDescriptor otherDesc = other.getDescriptor();
888            if ((mDescriptor == null) != (otherDesc == null)) return false;
889            if (mDescriptor != null && !mDescriptor.equals(otherDesc)) return false;
890            return true;
891        }
892    }
893
894    /** FM band configuration.
895     * @see #BAND_FM
896     * @see #BAND_FM_HD */
897    public static class FmBandConfig extends BandConfig {
898        private final boolean mStereo;
899        private final boolean mRds;
900        private final boolean mTa;
901        private final boolean mAf;
902        private final boolean mEa;
903
904        FmBandConfig(FmBandDescriptor descriptor) {
905            super((BandDescriptor)descriptor);
906            mStereo = descriptor.isStereoSupported();
907            mRds = descriptor.isRdsSupported();
908            mTa = descriptor.isTaSupported();
909            mAf = descriptor.isAfSupported();
910            mEa = descriptor.isEaSupported();
911        }
912
913        FmBandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing,
914                boolean stereo, boolean rds, boolean ta, boolean af, boolean ea) {
915            super(region, type, lowerLimit, upperLimit, spacing);
916            mStereo = stereo;
917            mRds = rds;
918            mTa = ta;
919            mAf = af;
920            mEa = ea;
921        }
922
923        /** Get stereo enable state
924         * @return the enable state.
925         */
926        public boolean getStereo() {
927            return mStereo;
928        }
929
930        /** Get RDS or RBDS(if region is ITU2) enable state
931         * @return the enable state.
932         */
933        public boolean getRds() {
934            return mRds;
935        }
936
937        /** Get Traffic announcement enable state
938         * @return the enable state.
939         */
940        public boolean getTa() {
941            return mTa;
942        }
943
944        /** Get Alternate Frequency Switching enable state
945         * @return the enable state.
946         */
947        public boolean getAf() {
948            return mAf;
949        }
950
951        /**
952         * Get Emergency announcement enable state
953         * @return the enable state.
954         */
955        public boolean getEa() {
956            return mEa;
957        }
958
959        private FmBandConfig(Parcel in) {
960            super(in);
961            mStereo = in.readByte() == 1;
962            mRds = in.readByte() == 1;
963            mTa = in.readByte() == 1;
964            mAf = in.readByte() == 1;
965            mEa = in.readByte() == 1;
966        }
967
968        public static final Parcelable.Creator<FmBandConfig> CREATOR
969                = new Parcelable.Creator<FmBandConfig>() {
970            public FmBandConfig createFromParcel(Parcel in) {
971                return new FmBandConfig(in);
972            }
973
974            public FmBandConfig[] newArray(int size) {
975                return new FmBandConfig[size];
976            }
977        };
978
979        @Override
980        public void writeToParcel(Parcel dest, int flags) {
981            super.writeToParcel(dest, flags);
982            dest.writeByte((byte) (mStereo ? 1 : 0));
983            dest.writeByte((byte) (mRds ? 1 : 0));
984            dest.writeByte((byte) (mTa ? 1 : 0));
985            dest.writeByte((byte) (mAf ? 1 : 0));
986            dest.writeByte((byte) (mEa ? 1 : 0));
987        }
988
989        @Override
990        public int describeContents() {
991            return 0;
992        }
993
994        @Override
995        public String toString() {
996            return "FmBandConfig [" + super.toString()
997                    + ", mStereo=" + mStereo + ", mRds=" + mRds + ", mTa=" + mTa
998                    + ", mAf=" + mAf + ", mEa =" + mEa + "]";
999        }
1000
1001        @Override
1002        public int hashCode() {
1003            final int prime = 31;
1004            int result = super.hashCode();
1005            result = prime * result + (mStereo ? 1 : 0);
1006            result = prime * result + (mRds ? 1 : 0);
1007            result = prime * result + (mTa ? 1 : 0);
1008            result = prime * result + (mAf ? 1 : 0);
1009            result = prime * result + (mEa ? 1 : 0);
1010            return result;
1011        }
1012
1013        @Override
1014        public boolean equals(Object obj) {
1015            if (this == obj)
1016                return true;
1017            if (!super.equals(obj))
1018                return false;
1019            if (!(obj instanceof FmBandConfig))
1020                return false;
1021            FmBandConfig other = (FmBandConfig) obj;
1022            if (mStereo != other.mStereo)
1023                return false;
1024            if (mRds != other.mRds)
1025                return false;
1026            if (mTa != other.mTa)
1027                return false;
1028            if (mAf != other.mAf)
1029                return false;
1030            if (mEa != other.mEa)
1031                return false;
1032            return true;
1033        }
1034
1035        /**
1036         * Builder class for {@link FmBandConfig} objects.
1037         */
1038        public static class Builder {
1039            private final BandDescriptor mDescriptor;
1040            private boolean mStereo;
1041            private boolean mRds;
1042            private boolean mTa;
1043            private boolean mAf;
1044            private boolean mEa;
1045
1046            /**
1047             * Constructs a new Builder with the defaults from an {@link FmBandDescriptor} .
1048             * @param descriptor the FmBandDescriptor defaults are read from .
1049             */
1050            public Builder(FmBandDescriptor descriptor) {
1051                mDescriptor = new BandDescriptor(descriptor.getRegion(), descriptor.getType(),
1052                        descriptor.getLowerLimit(), descriptor.getUpperLimit(),
1053                        descriptor.getSpacing());
1054                mStereo = descriptor.isStereoSupported();
1055                mRds = descriptor.isRdsSupported();
1056                mTa = descriptor.isTaSupported();
1057                mAf = descriptor.isAfSupported();
1058                mEa = descriptor.isEaSupported();
1059            }
1060
1061            /**
1062             * Constructs a new Builder from a given {@link FmBandConfig}
1063             * @param config the FmBandConfig object whose data will be reused in the new Builder.
1064             */
1065            public Builder(FmBandConfig config) {
1066                mDescriptor = new BandDescriptor(config.getRegion(), config.getType(),
1067                        config.getLowerLimit(), config.getUpperLimit(), config.getSpacing());
1068                mStereo = config.getStereo();
1069                mRds = config.getRds();
1070                mTa = config.getTa();
1071                mAf = config.getAf();
1072                mEa = config.getEa();
1073            }
1074
1075            /**
1076             * Combines all of the parameters that have been set and return a new
1077             * {@link FmBandConfig} object.
1078             * @return a new {@link FmBandConfig} object
1079             */
1080            public FmBandConfig build() {
1081                FmBandConfig config = new FmBandConfig(mDescriptor.getRegion(),
1082                        mDescriptor.getType(), mDescriptor.getLowerLimit(),
1083                        mDescriptor.getUpperLimit(), mDescriptor.getSpacing(),
1084                        mStereo, mRds, mTa, mAf, mEa);
1085                return config;
1086            }
1087
1088            /** Set stereo enable state
1089             * @param state The new enable state.
1090             * @return the same Builder instance.
1091             */
1092            public Builder setStereo(boolean state) {
1093                mStereo = state;
1094                return this;
1095            }
1096
1097            /** Set RDS or RBDS(if region is ITU2) enable state
1098             * @param state The new enable state.
1099             * @return the same Builder instance.
1100             */
1101            public Builder setRds(boolean state) {
1102                mRds = state;
1103                return this;
1104            }
1105
1106            /** Set Traffic announcement enable state
1107             * @param state The new enable state.
1108             * @return the same Builder instance.
1109             */
1110            public Builder setTa(boolean state) {
1111                mTa = state;
1112                return this;
1113            }
1114
1115            /** Set Alternate Frequency Switching enable state
1116             * @param state The new enable state.
1117             * @return the same Builder instance.
1118             */
1119            public Builder setAf(boolean state) {
1120                mAf = state;
1121                return this;
1122            }
1123
1124            /** Set Emergency Announcement enable state
1125             * @param state The new enable state.
1126             * @return the same Builder instance.
1127             */
1128            public Builder setEa(boolean state) {
1129                mEa = state;
1130                return this;
1131            }
1132        };
1133    }
1134
1135    /** AM band configuration.
1136     * @see #BAND_AM */
1137    public static class AmBandConfig extends BandConfig {
1138        private final boolean mStereo;
1139
1140        AmBandConfig(AmBandDescriptor descriptor) {
1141            super((BandDescriptor)descriptor);
1142            mStereo = descriptor.isStereoSupported();
1143        }
1144
1145        AmBandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing,
1146                boolean stereo) {
1147            super(region, type, lowerLimit, upperLimit, spacing);
1148            mStereo = stereo;
1149        }
1150
1151        /** Get stereo enable state
1152         * @return the enable state.
1153         */
1154        public boolean getStereo() {
1155            return mStereo;
1156        }
1157
1158        private AmBandConfig(Parcel in) {
1159            super(in);
1160            mStereo = in.readByte() == 1;
1161        }
1162
1163        public static final Parcelable.Creator<AmBandConfig> CREATOR
1164                = new Parcelable.Creator<AmBandConfig>() {
1165            public AmBandConfig createFromParcel(Parcel in) {
1166                return new AmBandConfig(in);
1167            }
1168
1169            public AmBandConfig[] newArray(int size) {
1170                return new AmBandConfig[size];
1171            }
1172        };
1173
1174        @Override
1175        public void writeToParcel(Parcel dest, int flags) {
1176            super.writeToParcel(dest, flags);
1177            dest.writeByte((byte) (mStereo ? 1 : 0));
1178        }
1179
1180        @Override
1181        public int describeContents() {
1182            return 0;
1183        }
1184
1185        @Override
1186        public String toString() {
1187            return "AmBandConfig [" + super.toString()
1188                    + ", mStereo=" + mStereo + "]";
1189        }
1190
1191        @Override
1192        public int hashCode() {
1193            final int prime = 31;
1194            int result = super.hashCode();
1195            result = prime * result + (mStereo ? 1 : 0);
1196            return result;
1197        }
1198
1199        @Override
1200        public boolean equals(Object obj) {
1201            if (this == obj)
1202                return true;
1203            if (!super.equals(obj))
1204                return false;
1205            if (!(obj instanceof AmBandConfig))
1206                return false;
1207            AmBandConfig other = (AmBandConfig) obj;
1208            if (mStereo != other.getStereo())
1209                return false;
1210            return true;
1211        }
1212
1213        /**
1214         * Builder class for {@link AmBandConfig} objects.
1215         */
1216        public static class Builder {
1217            private final BandDescriptor mDescriptor;
1218            private boolean mStereo;
1219
1220            /**
1221             * Constructs a new Builder with the defaults from an {@link AmBandDescriptor} .
1222             * @param descriptor the FmBandDescriptor defaults are read from .
1223             */
1224            public Builder(AmBandDescriptor descriptor) {
1225                mDescriptor = new BandDescriptor(descriptor.getRegion(), descriptor.getType(),
1226                        descriptor.getLowerLimit(), descriptor.getUpperLimit(),
1227                        descriptor.getSpacing());
1228                mStereo = descriptor.isStereoSupported();
1229            }
1230
1231            /**
1232             * Constructs a new Builder from a given {@link AmBandConfig}
1233             * @param config the FmBandConfig object whose data will be reused in the new Builder.
1234             */
1235            public Builder(AmBandConfig config) {
1236                mDescriptor = new BandDescriptor(config.getRegion(), config.getType(),
1237                        config.getLowerLimit(), config.getUpperLimit(), config.getSpacing());
1238                mStereo = config.getStereo();
1239            }
1240
1241            /**
1242             * Combines all of the parameters that have been set and return a new
1243             * {@link AmBandConfig} object.
1244             * @return a new {@link AmBandConfig} object
1245             */
1246            public AmBandConfig build() {
1247                AmBandConfig config = new AmBandConfig(mDescriptor.getRegion(),
1248                        mDescriptor.getType(), mDescriptor.getLowerLimit(),
1249                        mDescriptor.getUpperLimit(), mDescriptor.getSpacing(),
1250                        mStereo);
1251                return config;
1252            }
1253
1254            /** Set stereo enable state
1255             * @param state The new enable state.
1256             * @return the same Builder instance.
1257             */
1258            public Builder setStereo(boolean state) {
1259                mStereo = state;
1260                return this;
1261            }
1262        };
1263    }
1264
1265    /** Radio program information returned by
1266     * {@link RadioTuner#getProgramInformation(RadioManager.ProgramInfo[])} */
1267    public static class ProgramInfo implements Parcelable {
1268
1269        // sourced from hardware/interfaces/broadcastradio/1.1/types.hal
1270        private static final int FLAG_LIVE = 1 << 0;
1271        private static final int FLAG_MUTED = 1 << 1;
1272        private static final int FLAG_TRAFFIC_PROGRAM = 1 << 2;
1273        private static final int FLAG_TRAFFIC_ANNOUNCEMENT = 1 << 3;
1274
1275        @NonNull private final ProgramSelector mSelector;
1276        private final boolean mTuned;
1277        private final boolean mStereo;
1278        private final boolean mDigital;
1279        private final int mFlags;
1280        private final int mSignalStrength;
1281        private final RadioMetadata mMetadata;
1282        private final String mVendorExension;
1283
1284        ProgramInfo(@NonNull ProgramSelector selector, boolean tuned, boolean stereo,
1285                boolean digital, int signalStrength, RadioMetadata metadata, int flags,
1286                String vendorExension) {
1287            mSelector = selector;
1288            mTuned = tuned;
1289            mStereo = stereo;
1290            mDigital = digital;
1291            mFlags = flags;
1292            mSignalStrength = signalStrength;
1293            mMetadata = metadata;
1294            mVendorExension = vendorExension;
1295        }
1296
1297        /**
1298         * Program selector, necessary for tuning to a program.
1299         *
1300         * @return the program selector.
1301         */
1302        public @NonNull ProgramSelector getSelector() {
1303            return mSelector;
1304        }
1305
1306        /** Main channel expressed in units according to band type.
1307         * Currently all defined band types express channels as frequency in kHz
1308         * @return the program channel
1309         * @deprecated Use {@link getSelector()} instead.
1310         */
1311        @Deprecated
1312        public int getChannel() {
1313            try {
1314                return (int) mSelector.getFirstId(ProgramSelector.IDENTIFIER_TYPE_AMFM_FREQUENCY);
1315            } catch (IllegalArgumentException ex) {
1316                Log.w(TAG, "Not an AM/FM program");
1317                return 0;
1318            }
1319        }
1320
1321        /** Sub channel ID. E.g 1 for HD radio HD1
1322         * @return the program sub channel
1323         * @deprecated Use {@link getSelector()} instead.
1324         */
1325        @Deprecated
1326        public int getSubChannel() {
1327            try {
1328                return (int) mSelector.getFirstId(
1329                        ProgramSelector.IDENTIFIER_TYPE_HD_SUBCHANNEL) + 1;
1330            } catch (IllegalArgumentException ex) {
1331                // this is a normal behavior for analog AM/FM selector
1332                return 0;
1333            }
1334        }
1335
1336        /** {@code true} if the tuner is currently tuned on a valid station
1337         * @return {@code true} if currently tuned, {@code false} otherwise.
1338         */
1339        public boolean isTuned() {
1340            return mTuned;
1341        }
1342        /** {@code true} if the received program is stereo
1343         * @return {@code true} if stereo, {@code false} otherwise.
1344         */
1345        public boolean isStereo() {
1346            return mStereo;
1347        }
1348        /** {@code true} if the received program is digital (e.g HD radio)
1349         * @return {@code true} if digital, {@code false} otherwise.
1350         */
1351        public boolean isDigital() {
1352            return mDigital;
1353        }
1354
1355        /**
1356         * {@code true} if the program is currently playing live stream.
1357         * This may result in a slightly altered reception parameters,
1358         * usually targetted at reduced latency.
1359         */
1360        public boolean isLive() {
1361            return (mFlags & FLAG_LIVE) != 0;
1362        }
1363
1364        /**
1365         * {@code true} if radio stream is not playing, ie. due to bad reception
1366         * conditions or buffering. In this state volume knob MAY be disabled to
1367         * prevent user increasing volume too much.
1368         * It does NOT mean the user has muted audio.
1369         */
1370        public boolean isMuted() {
1371            return (mFlags & FLAG_MUTED) != 0;
1372        }
1373
1374        /**
1375         * {@code true} if radio station transmits traffic information
1376         * regularily.
1377         */
1378        public boolean isTrafficProgram() {
1379            return (mFlags & FLAG_TRAFFIC_PROGRAM) != 0;
1380        }
1381
1382        /**
1383         * {@code true} if radio station transmits traffic information
1384         * at the very moment.
1385         */
1386        public boolean isTrafficAnnouncementActive() {
1387            return (mFlags & FLAG_TRAFFIC_ANNOUNCEMENT) != 0;
1388        }
1389
1390        /** Signal strength indicator from 0 (no signal) to 100 (excellent)
1391         * @return the signal strength indication.
1392         */
1393        public int getSignalStrength() {
1394            return mSignalStrength;
1395        }
1396        /** Metadata currently received from this station.
1397         * null if no metadata have been received
1398         * @return current meta data received from this program.
1399         */
1400        public RadioMetadata getMetadata() {
1401            return mMetadata;
1402        }
1403
1404        /**
1405         * Opaque vendor-specific string, passed from HAL without changes.
1406         * Format of this string can vary across vendors.
1407         *
1408         * It may be used for extra features, that's not supported by a platform,
1409         * for example: "paid-service=true;bitrate=320kbps".
1410         *
1411         * Client application MUST verify vendor/product name from the
1412         * ModuleProperties class before doing any interpretation of this value.
1413         */
1414        public @NonNull String getVendorExension() {
1415            return mVendorExension == null ? "" : mVendorExension;
1416        }
1417
1418        private ProgramInfo(Parcel in) {
1419            mSelector = in.readParcelable(null);
1420            mTuned = in.readByte() == 1;
1421            mStereo = in.readByte() == 1;
1422            mDigital = in.readByte() == 1;
1423            mSignalStrength = in.readInt();
1424            if (in.readByte() == 1) {
1425                mMetadata = RadioMetadata.CREATOR.createFromParcel(in);
1426            } else {
1427                mMetadata = null;
1428            }
1429            mFlags = in.readInt();
1430            mVendorExension = in.readString();
1431        }
1432
1433        public static final Parcelable.Creator<ProgramInfo> CREATOR
1434                = new Parcelable.Creator<ProgramInfo>() {
1435            public ProgramInfo createFromParcel(Parcel in) {
1436                return new ProgramInfo(in);
1437            }
1438
1439            public ProgramInfo[] newArray(int size) {
1440                return new ProgramInfo[size];
1441            }
1442        };
1443
1444        @Override
1445        public void writeToParcel(Parcel dest, int flags) {
1446            dest.writeParcelable(mSelector, 0);
1447            dest.writeByte((byte)(mTuned ? 1 : 0));
1448            dest.writeByte((byte)(mStereo ? 1 : 0));
1449            dest.writeByte((byte)(mDigital ? 1 : 0));
1450            dest.writeInt(mSignalStrength);
1451            if (mMetadata == null) {
1452                dest.writeByte((byte)0);
1453            } else {
1454                dest.writeByte((byte)1);
1455                mMetadata.writeToParcel(dest, flags);
1456            }
1457            dest.writeInt(mFlags);
1458            dest.writeString(mVendorExension);
1459        }
1460
1461        @Override
1462        public int describeContents() {
1463            return 0;
1464        }
1465
1466        @Override
1467        public String toString() {
1468            return "ProgramInfo [mSelector=" + mSelector
1469                    + ", mTuned=" + mTuned + ", mStereo=" + mStereo + ", mDigital=" + mDigital
1470                    + ", mFlags=" + mFlags + ", mSignalStrength=" + mSignalStrength
1471                    + ((mMetadata == null) ? "" : (", mMetadata=" + mMetadata.toString()))
1472                    + "]";
1473        }
1474
1475        @Override
1476        public int hashCode() {
1477            final int prime = 31;
1478            int result = 1;
1479            result = prime * result + mSelector.hashCode();
1480            result = prime * result + (mTuned ? 1 : 0);
1481            result = prime * result + (mStereo ? 1 : 0);
1482            result = prime * result + (mDigital ? 1 : 0);
1483            result = prime * result + mFlags;
1484            result = prime * result + mSignalStrength;
1485            result = prime * result + ((mMetadata == null) ? 0 : mMetadata.hashCode());
1486            result = prime * result + ((mVendorExension == null) ? 0 : mVendorExension.hashCode());
1487            return result;
1488        }
1489
1490        @Override
1491        public boolean equals(Object obj) {
1492            if (this == obj)
1493                return true;
1494            if (!(obj instanceof ProgramInfo))
1495                return false;
1496            ProgramInfo other = (ProgramInfo) obj;
1497            if (!mSelector.equals(other.getSelector())) return false;
1498            if (mTuned != other.isTuned())
1499                return false;
1500            if (mStereo != other.isStereo())
1501                return false;
1502            if (mDigital != other.isDigital())
1503                return false;
1504            if (mFlags != other.mFlags)
1505                return false;
1506            if (mSignalStrength != other.getSignalStrength())
1507                return false;
1508            if (mMetadata == null) {
1509                if (other.getMetadata() != null)
1510                    return false;
1511            } else if (!mMetadata.equals(other.getMetadata()))
1512                return false;
1513            if (!TextUtils.equals(mVendorExension, other.mVendorExension))
1514                return false;
1515            return true;
1516        }
1517    }
1518
1519
1520    /**
1521     * Returns a list of descriptors for all broadcast radio modules present on the device.
1522     * @param modules An List of {@link ModuleProperties} where the list will be returned.
1523     * @return
1524     * <ul>
1525     *  <li>{@link #STATUS_OK} in case of success, </li>
1526     *  <li>{@link #STATUS_ERROR} in case of unspecified error, </li>
1527     *  <li>{@link #STATUS_NO_INIT} if the native service cannot be reached, </li>
1528     *  <li>{@link #STATUS_BAD_VALUE} if modules is null, </li>
1529     *  <li>{@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails, </li>
1530     * </ul>
1531     */
1532    public int listModules(List<ModuleProperties> modules) {
1533        if (modules == null) {
1534            Log.e(TAG, "the output list must not be empty");
1535            return STATUS_BAD_VALUE;
1536        }
1537
1538        Log.d(TAG, "Listing available tuners...");
1539        List<ModuleProperties> returnedList;
1540        try {
1541            returnedList = mService.listModules();
1542        } catch (RemoteException e) {
1543            Log.e(TAG, "Failed listing available tuners", e);
1544            return STATUS_DEAD_OBJECT;
1545        }
1546
1547        if (returnedList == null) {
1548            Log.e(TAG, "Returned list was a null");
1549            return STATUS_ERROR;
1550        }
1551
1552        modules.addAll(returnedList);
1553        return STATUS_OK;
1554    }
1555
1556    private native int nativeListModules(List<ModuleProperties> modules);
1557
1558    /**
1559     * Open an interface to control a tuner on a given broadcast radio module.
1560     * Optionally selects and applies the configuration passed as "config" argument.
1561     * @param moduleId radio module identifier {@link ModuleProperties#getId()}. Mandatory.
1562     * @param config desired band and configuration to apply when enabling the hardware module.
1563     * optional, can be null.
1564     * @param withAudio {@code true} to request a tuner with an audio source.
1565     * This tuner is intended for live listening or recording or a radio program.
1566     * If {@code false}, the tuner can only be used to retrieve program informations.
1567     * @param callback {@link RadioTuner.Callback} interface. Mandatory.
1568     * @param handler the Handler on which the callbacks will be received.
1569     * Can be null if default handler is OK.
1570     * @return a valid {@link RadioTuner} interface in case of success or null in case of error.
1571     */
1572    public RadioTuner openTuner(int moduleId, BandConfig config, boolean withAudio,
1573            RadioTuner.Callback callback, Handler handler) {
1574        if (callback == null) {
1575            throw new IllegalArgumentException("callback must not be empty");
1576        }
1577
1578        Log.d(TAG, "Opening tuner " + moduleId + "...");
1579
1580        ITuner tuner;
1581        TunerCallbackAdapter halCallback = new TunerCallbackAdapter(callback, handler);
1582        try {
1583            tuner = mService.openTuner(moduleId, config, withAudio, halCallback);
1584        } catch (RemoteException e) {
1585            Log.e(TAG, "Failed to open tuner", e);
1586            return null;
1587        }
1588        if (tuner == null) {
1589            Log.e(TAG, "Failed to open tuner");
1590            return null;
1591        }
1592        halCallback.attachTuner(tuner);
1593        return new TunerAdapter(tuner, config != null ? config.getType() : BAND_INVALID);
1594    }
1595
1596    @NonNull private final Context mContext;
1597    @NonNull private final IRadioService mService;
1598
1599    /**
1600     * @hide
1601     */
1602    public RadioManager(@NonNull Context context) throws ServiceNotFoundException {
1603        mContext = context;
1604        mService = IRadioService.Stub.asInterface(
1605                ServiceManager.getServiceOrThrow(Context.RADIO_SERVICE));
1606    }
1607}
1608