diff options
author | Eric Laurent <elaurent@google.com> | 2015-03-05 15:18:44 -0800 |
---|---|---|
committer | Eric Laurent <elaurent@google.com> | 2015-03-13 15:34:50 -0700 |
commit | 2035ac85f61b0e7fa384d52fcfa99766424c122c (patch) | |
tree | 10ed78f42510184719a85f845ad51dc17a9bb465 /core/java | |
parent | 633cb563f36001d6973b69291086cbdfe50612c5 (diff) | |
download | frameworks_base-2035ac85f61b0e7fa384d52fcfa99766424c122c.zip frameworks_base-2035ac85f61b0e7fa384d52fcfa99766424c122c.tar.gz frameworks_base-2035ac85f61b0e7fa384d52fcfa99766424c122c.tar.bz2 |
broadcast radio API
Initial implementation of system APIs for broadcast
radio framework. Added manager and interfaces to control
a broadcast radio function exposed by the radio HAL.
- RadioManager: contains data structures and definitions as well as
top level API for feature discovery and tuner interface instantiation.
- RadioTuner: interface to control a broadcast radio tuner.
- RadioModule: framework component implementing the RadioTuner interface
and controlling a HW radio module via the radio HAL.
- RadioMetadata: representation of radio meta data (Station name, PTY,
song title, artwork, etc...) communicated by the framework to the client.
Change-Id: Iee42a185c694503e25f0b2dcfa417d88f5e9549b
Diffstat (limited to 'core/java')
-rw-r--r-- | core/java/android/app/SystemServiceRegistry.java | 8 | ||||
-rw-r--r-- | core/java/android/content/Context.java | 12 | ||||
-rw-r--r-- | core/java/android/hardware/radio/RadioManager.java | 1308 | ||||
-rw-r--r-- | core/java/android/hardware/radio/RadioMetadata.java | 449 | ||||
-rw-r--r-- | core/java/android/hardware/radio/RadioModule.java | 218 | ||||
-rw-r--r-- | core/java/android/hardware/radio/RadioTuner.java | 302 |
6 files changed, 2297 insertions, 0 deletions
diff --git a/core/java/android/app/SystemServiceRegistry.java b/core/java/android/app/SystemServiceRegistry.java index 993f416..fd7bae7 100644 --- a/core/java/android/app/SystemServiceRegistry.java +++ b/core/java/android/app/SystemServiceRegistry.java @@ -49,6 +49,7 @@ import android.hardware.hdmi.IHdmiControlService; import android.hardware.input.InputManager; import android.hardware.usb.IUsbManager; import android.hardware.usb.UsbManager; +import android.hardware.radio.RadioManager; import android.location.CountryDetector; import android.location.ICountryDetector; import android.location.ILocationManager; @@ -683,6 +684,13 @@ final class SystemServiceRegistry { IBinder b = ServiceManager.getService(Context.MIDI_SERVICE); return new MidiManager(ctx, IMidiManager.Stub.asInterface(b)); }}); + + registerService(Context.RADIO_SERVICE, RadioManager.class, + new CachedServiceFetcher<RadioManager>() { + @Override + public RadioManager createService(ContextImpl ctx) { + return new RadioManager(ctx); + }}); } /** diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java index 61cdec3..80b5e0b 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -2200,6 +2200,7 @@ public abstract class Context { //@hide: PERSISTENT_DATA_BLOCK_SERVICE, MEDIA_PROJECTION_SERVICE, MIDI_SERVICE, + RADIO_SERVICE, }) @Retention(RetentionPolicy.SOURCE) public @interface ServiceName {} @@ -3026,6 +3027,17 @@ public abstract class Context { */ public static final String MIDI_SERVICE = "midi"; + + /** + * Use with {@link #getSystemService} to retrieve a + * {@link android.hardware.radio.RadioManager} for accessing the broadcast radio service. + * + * @see #getSystemService + * @hide + */ + public static final String RADIO_SERVICE = "radio"; + + /** * Determine whether the given permission is allowed for a particular * process and user ID running in the system. diff --git a/core/java/android/hardware/radio/RadioManager.java b/core/java/android/hardware/radio/RadioManager.java new file mode 100644 index 0000000..32930a7 --- /dev/null +++ b/core/java/android/hardware/radio/RadioManager.java @@ -0,0 +1,1308 @@ +/** + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.radio; + +import android.annotation.SystemApi; +import android.content.Context; +import android.os.Handler; +import android.os.Parcel; +import android.os.Parcelable; +import java.util.List; +import java.util.Arrays; + +/** + * The RadioManager class allows to control a broadcast radio tuner present on the device. + * It provides data structures and methods to query for available radio modules, list their + * properties and open an interface to control tuning operations and receive callbacks when + * asynchronous operations complete or events occur. + * @hide + */ +@SystemApi +public class RadioManager { + + /** Method return status: successful operation */ + public static final int STATUS_OK = 0; + /** Method return status: unspecified error */ + public static final int STATUS_ERROR = Integer.MIN_VALUE; + /** Method return status: permission denied */ + public static final int STATUS_PERMISSION_DENIED = -1; + /** Method return status: initialization failure */ + public static final int STATUS_NO_INIT = -19; + /** Method return status: invalid argument provided */ + public static final int STATUS_BAD_VALUE = -22; + /** Method return status: cannot reach service */ + public static final int STATUS_DEAD_OBJECT = -32; + /** Method return status: invalid or out of sequence operation */ + public static final int STATUS_INVALID_OPERATION = -38; + /** Method return status: time out before operation completion */ + public static final int STATUS_TIMED_OUT = -110; + + + // keep in sync with radio_class_t in /system/core/incluse/system/radio.h + /** Radio module class supporting FM (including HD radio) and AM */ + public static final int CLASS_AM_FM = 0; + /** Radio module class supporting satellite radio */ + public static final int CLASS_SAT = 1; + /** Radio module class supporting Digital terrestrial radio */ + public static final int CLASS_DT = 2; + + // keep in sync with radio_band_t in /system/core/incluse/system/radio.h + /** AM radio band (LW/MW/SW). + * @see BandDescriptor */ + public static final int BAND_AM = 0; + /** FM radio band. + * @see BandDescriptor */ + public static final int BAND_FM = 1; + /** FM HD radio or DRM band. + * @see BandDescriptor */ + public static final int BAND_FM_HD = 2; + /** AM HD radio or DRM band. + * @see BandDescriptor */ + public static final int BAND_AM_HD = 3; + + // keep in sync with radio_region_t in /system/core/incluse/system/radio.h + /** Africa, Europe. + * @see BandDescriptor */ + public static final int REGION_ITU_1 = 0; + /** Americas. + * @see BandDescriptor */ + public static final int REGION_ITU_2 = 1; + /** Russia. + * @see BandDescriptor */ + public static final int REGION_OIRT = 2; + /** Japan. + * @see BandDescriptor */ + public static final int REGION_JAPAN = 3; + /** Korea. + * @see BandDescriptor */ + public static final int REGION_KOREA = 4; + + /***************************************************************************** + * Lists properties, options and radio bands supported by a given broadcast radio module. + * Each module has a unique ID used to address it when calling RadioManager APIs. + * Module properties are returned by {@link #listModules(List <ModuleProperties>)} method. + ****************************************************************************/ + public static class ModuleProperties implements Parcelable { + + private final int mId; + private final int mClassId; + private final String mImplementor; + private final String mProduct; + private final String mVersion; + private final String mSerial; + private final int mNumTuners; + private final int mNumAudioSources; + private final boolean mIsCaptureSupported; + private final BandDescriptor[] mBands; + + ModuleProperties(int id, int classId, String implementor, String product, String version, + String serial, int numTuners, int numAudioSources, boolean isCaptureSupported, + BandDescriptor[] bands) { + mId = id; + mClassId = classId; + mImplementor = implementor; + mProduct = product; + mVersion = version; + mSerial = serial; + mNumTuners = numTuners; + mNumAudioSources = numAudioSources; + mIsCaptureSupported = isCaptureSupported; + mBands = bands; + } + + + /** Unique module identifier provided by the native service. + * For use with {@link #openTuner(int, BandConfig, boolean, Callback, Handler)}. + * @return the radio module unique identifier. + */ + public int getId() { + return mId; + } + + /** Module class identifier: {@link #CLASS_AM_FM}, {@link #CLASS_SAT}, {@link #CLASS_DT} + * @return the radio module class identifier. + */ + public int getClassId() { + return mClassId; + } + + /** Human readable broadcast radio module implementor + * @return the name of the radio module implementator. + */ + public String getImplementor() { + return mImplementor; + } + + /** Human readable broadcast radio module product name + * @return the radio module product name. + */ + public String getProduct() { + return mProduct; + } + + /** Human readable broadcast radio module version number + * @return the radio module version. + */ + public String getVersion() { + return mVersion; + } + + /** Radio module serial number. + * Can be used for subscription services. + * @return the radio module serial number. + */ + public String getSerial() { + return mSerial; + } + + /** Number of tuners available. + * This is the number of tuners that can be open simultaneously. + * @return the number of tuners supported. + */ + public int getNumTuners() { + return mNumTuners; + } + + /** Number tuner audio sources available. Must be less or equal to getNumTuners(). + * When more than one tuner is supported, one is usually for playback and has one + * associated audio source and the other is for pre scanning and building a + * program list. + * @return the number of audio sources available. + */ + public int getNumAudioSources() { + return mNumAudioSources; + } + + /** {@code true} if audio capture is possible from radio tuner output. + * This indicates if routing to audio devices not connected to the same HAL as the FM radio + * is possible (e.g. to USB) or DAR (Digital Audio Recorder) feature can be implemented. + * @return {@code true} if audio capture is possible, {@code false} otherwise. + */ + public boolean isCaptureSupported() { + return mIsCaptureSupported; + } + + /** List of descriptors for all bands supported by this module. + * @return an array of {@link BandDescriptor}. + */ + public BandDescriptor[] getBands() { + return mBands; + } + + private ModuleProperties(Parcel in) { + mId = in.readInt(); + mClassId = in.readInt(); + mImplementor = in.readString(); + mProduct = in.readString(); + mVersion = in.readString(); + mSerial = in.readString(); + mNumTuners = in.readInt(); + mNumAudioSources = in.readInt(); + mIsCaptureSupported = in.readInt() == 1; + Parcelable[] tmp = in.readParcelableArray(BandDescriptor.class.getClassLoader()); + mBands = new BandDescriptor[tmp.length]; + for (int i = 0; i < tmp.length; i++) { + mBands[i] = (BandDescriptor) tmp[i]; + } + } + + public static final Parcelable.Creator<ModuleProperties> CREATOR + = new Parcelable.Creator<ModuleProperties>() { + public ModuleProperties createFromParcel(Parcel in) { + return new ModuleProperties(in); + } + + public ModuleProperties[] newArray(int size) { + return new ModuleProperties[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mId); + dest.writeInt(mClassId); + dest.writeString(mImplementor); + dest.writeString(mProduct); + dest.writeString(mVersion); + dest.writeString(mSerial); + dest.writeInt(mNumTuners); + dest.writeInt(mNumAudioSources); + dest.writeInt(mIsCaptureSupported ? 1 : 0); + dest.writeParcelableArray(mBands, flags); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "ModuleProperties [mId=" + mId + ", mClassId=" + mClassId + + ", mImplementor=" + mImplementor + ", mProduct=" + mProduct + + ", mVersion=" + mVersion + ", mSerial=" + mSerial + + ", mNumTuners=" + mNumTuners + + ", mNumAudioSources=" + mNumAudioSources + + ", mIsCaptureSupported=" + mIsCaptureSupported + + ", mBands=" + Arrays.toString(mBands) + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + mId; + result = prime * result + mClassId; + result = prime * result + ((mImplementor == null) ? 0 : mImplementor.hashCode()); + result = prime * result + ((mProduct == null) ? 0 : mProduct.hashCode()); + result = prime * result + ((mVersion == null) ? 0 : mVersion.hashCode()); + result = prime * result + ((mSerial == null) ? 0 : mSerial.hashCode()); + result = prime * result + mNumTuners; + result = prime * result + mNumAudioSources; + result = prime * result + (mIsCaptureSupported ? 1 : 0); + result = prime * result + Arrays.hashCode(mBands); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!(obj instanceof ModuleProperties)) + return false; + ModuleProperties other = (ModuleProperties) obj; + if (mId != other.getId()) + return false; + if (mClassId != other.getClassId()) + return false; + if (mImplementor == null) { + if (other.getImplementor() != null) + return false; + } else if (!mImplementor.equals(other.getImplementor())) + return false; + if (mProduct == null) { + if (other.getProduct() != null) + return false; + } else if (!mProduct.equals(other.getProduct())) + return false; + if (mVersion == null) { + if (other.getVersion() != null) + return false; + } else if (!mVersion.equals(other.getVersion())) + return false; + if (mSerial == null) { + if (other.getSerial() != null) + return false; + } else if (!mSerial.equals(other.getSerial())) + return false; + if (mNumTuners != other.getNumTuners()) + return false; + if (mNumAudioSources != other.getNumAudioSources()) + return false; + if (mIsCaptureSupported != other.isCaptureSupported()) + return false; + if (!Arrays.equals(mBands, other.getBands())) + return false; + return true; + } + } + + /** Radio band descriptor: an element in ModuleProperties bands array. + * It is either an instance of {@link FmBandDescriptor} or {@link AmBandDescriptor} */ + public static class BandDescriptor implements Parcelable { + + private final int mRegion; + private final int mType; + private final int mLowerLimit; + private final int mUpperLimit; + private final int mSpacing; + + BandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing) { + mRegion = region; + mType = type; + mLowerLimit = lowerLimit; + mUpperLimit = upperLimit; + mSpacing = spacing; + } + + /** Region this band applies to. E.g. {@link #REGION_ITU_1} + * @return the region this band is associated to. + */ + public int getRegion() { + return mRegion; + } + /** Band type, e.g {@link #BAND_FM}. Defines the subclass this descriptor can be cast to: + * <ul> + * <li>{@link #BAND_FM} or {@link #BAND_FM_HD} cast to {@link FmBandDescriptor}, </li> + * <li>{@link #BAND_AM} cast to {@link AmBandDescriptor}, </li> + * </ul> + * @return the band type. + */ + public int getType() { + return mType; + } + /** Lower band limit expressed in units according to band type. + * Currently all defined band types express channels as frequency in kHz + * @return the lower band limit. + */ + public int getLowerLimit() { + return mLowerLimit; + } + /** Upper band limit expressed in units according to band type. + * Currently all defined band types express channels as frequency in kHz + * @return the upper band limit. + */ + public int getUpperLimit() { + return mUpperLimit; + } + /** Channel spacing in units according to band type. + * Currently all defined band types express channels as frequency in kHz + * @return the channel spacing. + */ + public int getSpacing() { + return mSpacing; + } + + private BandDescriptor(Parcel in) { + mRegion = in.readInt(); + mType = in.readInt(); + mLowerLimit = in.readInt(); + mUpperLimit = in.readInt(); + mSpacing = in.readInt(); + } + + public static final Parcelable.Creator<BandDescriptor> CREATOR + = new Parcelable.Creator<BandDescriptor>() { + public BandDescriptor createFromParcel(Parcel in) { + return new BandDescriptor(in); + } + + public BandDescriptor[] newArray(int size) { + return new BandDescriptor[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mRegion); + dest.writeInt(mType); + dest.writeInt(mLowerLimit); + dest.writeInt(mUpperLimit); + dest.writeInt(mSpacing); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "BandDescriptor [mRegion=" + mRegion + ", mType=" + mType + ", mLowerLimit=" + + mLowerLimit + ", mUpperLimit=" + mUpperLimit + ", mSpacing=" + mSpacing + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + mRegion; + result = prime * result + mType; + result = prime * result + mLowerLimit; + result = prime * result + mUpperLimit; + result = prime * result + mSpacing; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!(obj instanceof BandDescriptor)) + return false; + BandDescriptor other = (BandDescriptor) obj; + if (mRegion != other.getRegion()) + return false; + if (mType != other.getType()) + return false; + if (mLowerLimit != other.getLowerLimit()) + return false; + if (mUpperLimit != other.getUpperLimit()) + return false; + if (mSpacing != other.getSpacing()) + return false; + return true; + } + } + + /** FM band descriptor + * @see #BAND_FM + * @see #BAND_FM_HD */ + public static class FmBandDescriptor extends BandDescriptor { + private final boolean mStereo; + private final boolean mRds; + private final boolean mTa; + private final boolean mAf; + + FmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing, + boolean stereo, boolean rds, boolean ta, boolean af) { + super(region, type, lowerLimit, upperLimit, spacing); + mStereo = stereo; + mRds = rds; + mTa = ta; + mAf = af; + } + + /** Stereo is supported + * @return {@code true} if stereo is supported, {@code false} otherwise. + */ + public boolean isStereoSupported() { + return mStereo; + } + /** RDS or RBDS(if region is ITU2) is supported + * @return {@code true} if RDS or RBDS is supported, {@code false} otherwise. + */ + public boolean isRdsSupported() { + return mRds; + } + /** Traffic announcement is supported + * @return {@code true} if TA is supported, {@code false} otherwise. + */ + public boolean isTaSupported() { + return mTa; + } + /** Alternate Frequency Switching is supported + * @return {@code true} if AF switching is supported, {@code false} otherwise. + */ + public boolean isAfSupported() { + return mAf; + } + + /* Parcelable implementation */ + private FmBandDescriptor(Parcel in) { + super(in); + mStereo = in.readByte() == 1; + mRds = in.readByte() == 1; + mTa = in.readByte() == 1; + mAf = in.readByte() == 1; + } + + public static final Parcelable.Creator<FmBandDescriptor> CREATOR + = new Parcelable.Creator<FmBandDescriptor>() { + public FmBandDescriptor createFromParcel(Parcel in) { + return new FmBandDescriptor(in); + } + + public FmBandDescriptor[] newArray(int size) { + return new FmBandDescriptor[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeByte((byte) (mStereo ? 1 : 0)); + dest.writeByte((byte) (mRds ? 1 : 0)); + dest.writeByte((byte) (mTa ? 1 : 0)); + dest.writeByte((byte) (mAf ? 1 : 0)); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "FmBandDescriptor [ "+ super.toString() + " mStereo=" + mStereo + + ", mRds=" + mRds + ", mTa=" + mTa + ", mAf=" + mAf + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + (mStereo ? 1 : 0); + result = prime * result + (mRds ? 1 : 0); + result = prime * result + (mTa ? 1 : 0); + result = prime * result + (mAf ? 1 : 0); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (!(obj instanceof FmBandDescriptor)) + return false; + FmBandDescriptor other = (FmBandDescriptor) obj; + if (mStereo != other.isStereoSupported()) + return false; + if (mRds != other.isRdsSupported()) + return false; + if (mTa != other.isTaSupported()) + return false; + if (mAf != other.isAfSupported()) + return false; + return true; + } + } + + /** AM band descriptor. + * @see #BAND_AM */ + public static class AmBandDescriptor extends BandDescriptor { + + private final boolean mStereo; + + AmBandDescriptor(int region, int type, int lowerLimit, int upperLimit, int spacing, + boolean stereo) { + super(region, type, lowerLimit, upperLimit, spacing); + mStereo = stereo; + } + + /** Stereo is supported + * @return {@code true} if stereo is supported, {@code false} otherwise. + */ + public boolean isStereoSupported() { + return mStereo; + } + + private AmBandDescriptor(Parcel in) { + super(in); + mStereo = in.readByte() == 1; + } + + public static final Parcelable.Creator<AmBandDescriptor> CREATOR + = new Parcelable.Creator<AmBandDescriptor>() { + public AmBandDescriptor createFromParcel(Parcel in) { + return new AmBandDescriptor(in); + } + + public AmBandDescriptor[] newArray(int size) { + return new AmBandDescriptor[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeByte((byte) (mStereo ? 1 : 0)); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "AmBandDescriptor [ "+ super.toString() + " mStereo=" + mStereo + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + (mStereo ? 1 : 0); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (!(obj instanceof AmBandDescriptor)) + return false; + AmBandDescriptor other = (AmBandDescriptor) obj; + if (mStereo != other.isStereoSupported()) + return false; + return true; + } + } + + + /** Radio band configuration. */ + public static class BandConfig implements Parcelable { + + final BandDescriptor mDescriptor; + + BandConfig(BandDescriptor descriptor) { + mDescriptor = descriptor; + } + + BandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing) { + mDescriptor = new BandDescriptor(region, type, lowerLimit, upperLimit, spacing); + } + + private BandConfig(Parcel in) { + mDescriptor = new BandDescriptor(in); + } + + BandDescriptor getDescriptor() { + return mDescriptor; + } + + /** Region this band applies to. E.g. {@link #REGION_ITU_1} + * @return the region associated with this band. + */ + public int getRegion() { + return mDescriptor.getRegion(); + } + /** Band type, e.g {@link #BAND_FM}. Defines the subclass this descriptor can be cast to: + * <ul> + * <li>{@link #BAND_FM} or {@link #BAND_FM_HD} cast to {@link FmBandDescriptor}, </li> + * <li>{@link #BAND_AM} cast to {@link AmBandDescriptor}, </li> + * </ul> + * @return the band type. + */ + public int getType() { + return mDescriptor.getType(); + } + /** Lower band limit expressed in units according to band type. + * Currently all defined band types express channels as frequency in kHz + * @return the lower band limit. + */ + public int getLowerLimit() { + return mDescriptor.getLowerLimit(); + } + /** Upper band limit expressed in units according to band type. + * Currently all defined band types express channels as frequency in kHz + * @return the upper band limit. + */ + public int getUpperLimit() { + return mDescriptor.getUpperLimit(); + } + /** Channel spacing in units according to band type. + * Currently all defined band types express channels as frequency in kHz + * @return the channel spacing. + */ + public int getSpacing() { + return mDescriptor.getSpacing(); + } + + + public static final Parcelable.Creator<BandConfig> CREATOR + = new Parcelable.Creator<BandConfig>() { + public BandConfig createFromParcel(Parcel in) { + return new BandConfig(in); + } + + public BandConfig[] newArray(int size) { + return new BandConfig[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + mDescriptor.writeToParcel(dest, flags); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "BandConfig [ " + mDescriptor.toString() + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + mDescriptor.hashCode(); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!(obj instanceof BandConfig)) + return false; + BandConfig other = (BandConfig) obj; + if (mDescriptor != other.getDescriptor()) + return false; + return true; + } + } + + /** FM band configuration. + * @see #BAND_FM + * @see #BAND_FM_HD */ + public static class FmBandConfig extends BandConfig { + private final boolean mStereo; + private final boolean mRds; + private final boolean mTa; + private final boolean mAf; + + FmBandConfig(FmBandDescriptor descriptor) { + super((BandDescriptor)descriptor); + mStereo = descriptor.isStereoSupported(); + mRds = descriptor.isRdsSupported(); + mTa = descriptor.isTaSupported(); + mAf = descriptor.isAfSupported(); + } + + FmBandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing, + boolean stereo, boolean rds, boolean ta, boolean af) { + super(region, type, lowerLimit, upperLimit, spacing); + mStereo = stereo; + mRds = rds; + mTa = ta; + mAf = af; + } + + /** Get stereo enable state + * @return the enable state. + */ + public boolean getStereo() { + return mStereo; + } + + /** Get RDS or RBDS(if region is ITU2) enable state + * @return the enable state. + */ + public boolean getRds() { + return mRds; + } + + /** Get Traffic announcement enable state + * @return the enable state. + */ + public boolean getTa() { + return mTa; + } + + /** Get Alternate Frequency Switching enable state + * @return the enable state. + */ + public boolean getAf() { + return mAf; + } + + private FmBandConfig(Parcel in) { + super(in); + mStereo = in.readByte() == 1; + mRds = in.readByte() == 1; + mTa = in.readByte() == 1; + mAf = in.readByte() == 1; + } + + public static final Parcelable.Creator<FmBandConfig> CREATOR + = new Parcelable.Creator<FmBandConfig>() { + public FmBandConfig createFromParcel(Parcel in) { + return new FmBandConfig(in); + } + + public FmBandConfig[] newArray(int size) { + return new FmBandConfig[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeByte((byte) (mStereo ? 1 : 0)); + dest.writeByte((byte) (mRds ? 1 : 0)); + dest.writeByte((byte) (mTa ? 1 : 0)); + dest.writeByte((byte) (mAf ? 1 : 0)); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "FmBandConfig [" + super.toString() + + ", mStereo=" + mStereo + ", mRds=" + mRds + ", mTa=" + mTa + + ", mAf=" + mAf + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + (mStereo ? 1 : 0); + result = prime * result + (mRds ? 1 : 0); + result = prime * result + (mTa ? 1 : 0); + result = prime * result + (mAf ? 1 : 0); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (!(obj instanceof FmBandConfig)) + return false; + FmBandConfig other = (FmBandConfig) obj; + if (mStereo != other.mStereo) + return false; + if (mRds != other.mRds) + return false; + if (mTa != other.mTa) + return false; + if (mAf != other.mAf) + return false; + return true; + } + + /** + * Builder class for {@link FmBandConfig} objects. + */ + public static class Builder { + private final BandDescriptor mDescriptor; + private boolean mStereo; + private boolean mRds; + private boolean mTa; + private boolean mAf; + + /** + * Constructs a new Builder with the defaults from an {@link FmBandDescriptor} . + * @param descriptor the FmBandDescriptor defaults are read from . + */ + public Builder(FmBandDescriptor descriptor) { + mDescriptor = new BandDescriptor(descriptor.getRegion(), descriptor.getType(), + descriptor.getLowerLimit(), descriptor.getUpperLimit(), + descriptor.getSpacing()); + mStereo = descriptor.isStereoSupported(); + mRds = descriptor.isRdsSupported(); + mTa = descriptor.isTaSupported(); + mAf = descriptor.isAfSupported(); + } + + /** + * Constructs a new Builder from a given {@link FmBandConfig} + * @param config the FmBandConfig object whose data will be reused in the new Builder. + */ + public Builder(FmBandConfig config) { + mDescriptor = new BandDescriptor(config.getRegion(), config.getType(), + config.getLowerLimit(), config.getUpperLimit(), config.getSpacing()); + mStereo = config.getStereo(); + mRds = config.getRds(); + mTa = config.getTa(); + mAf = config.getAf(); + } + + /** + * Combines all of the parameters that have been set and return a new + * {@link FmBandConfig} object. + * @return a new {@link FmBandConfig} object + */ + public FmBandConfig build() { + FmBandConfig config = new FmBandConfig(mDescriptor.getRegion(), + mDescriptor.getType(), mDescriptor.getLowerLimit(), + mDescriptor.getUpperLimit(), mDescriptor.getSpacing(), + mStereo, mRds, mTa, mAf); + return config; + } + + /** Set stereo enable state + * @param state The new enable state. + * @return the same Builder instance. + */ + public Builder setStereo(boolean state) { + mStereo = state; + return this; + } + + /** Set RDS or RBDS(if region is ITU2) enable state + * @param state The new enable state. + * @return the same Builder instance. + */ + public Builder setRds(boolean state) { + mRds = state; + return this; + } + + /** Set Traffic announcement enable state + * @param state The new enable state. + * @return the same Builder instance. + */ + public Builder setTa(boolean state) { + mTa = state; + return this; + } + + /** Set Alternate Frequency Switching enable state + * @param state The new enable state. + * @return the same Builder instance. + */ + public Builder setAf(boolean state) { + mAf = state; + return this; + } + }; + } + + /** AM band configuration. + * @see #BAND_AM */ + public static class AmBandConfig extends BandConfig { + private final boolean mStereo; + + AmBandConfig(AmBandDescriptor descriptor) { + super((BandDescriptor)descriptor); + mStereo = descriptor.isStereoSupported(); + } + + AmBandConfig(int region, int type, int lowerLimit, int upperLimit, int spacing, + boolean stereo) { + super(region, type, lowerLimit, upperLimit, spacing); + mStereo = stereo; + } + + /** Get stereo enable state + * @return the enable state. + */ + public boolean getStereo() { + return mStereo; + } + + private AmBandConfig(Parcel in) { + super(in); + mStereo = in.readByte() == 1; + } + + public static final Parcelable.Creator<AmBandConfig> CREATOR + = new Parcelable.Creator<AmBandConfig>() { + public AmBandConfig createFromParcel(Parcel in) { + return new AmBandConfig(in); + } + + public AmBandConfig[] newArray(int size) { + return new AmBandConfig[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + super.writeToParcel(dest, flags); + dest.writeByte((byte) (mStereo ? 1 : 0)); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "AmBandConfig [" + super.toString() + + ", mStereo=" + mStereo + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + result = prime * result + (mStereo ? 1 : 0); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (!(obj instanceof AmBandConfig)) + return false; + AmBandConfig other = (AmBandConfig) obj; + if (mStereo != other.getStereo()) + return false; + return true; + } + + /** + * Builder class for {@link AmBandConfig} objects. + */ + public static class Builder { + private final BandDescriptor mDescriptor; + private boolean mStereo; + + /** + * Constructs a new Builder with the defaults from an {@link AmBandDescriptor} . + * @param descriptor the FmBandDescriptor defaults are read from . + */ + public Builder(AmBandDescriptor descriptor) { + mDescriptor = new BandDescriptor(descriptor.getRegion(), descriptor.getType(), + descriptor.getLowerLimit(), descriptor.getUpperLimit(), + descriptor.getSpacing()); + mStereo = descriptor.isStereoSupported(); + } + + /** + * Constructs a new Builder from a given {@link AmBandConfig} + * @param config the FmBandConfig object whose data will be reused in the new Builder. + */ + public Builder(AmBandConfig config) { + mDescriptor = new BandDescriptor(config.getRegion(), config.getType(), + config.getLowerLimit(), config.getUpperLimit(), config.getSpacing()); + mStereo = config.getStereo(); + } + + /** + * Combines all of the parameters that have been set and return a new + * {@link AmBandConfig} object. + * @return a new {@link AmBandConfig} object + */ + public AmBandConfig build() { + AmBandConfig config = new AmBandConfig(mDescriptor.getRegion(), + mDescriptor.getType(), mDescriptor.getLowerLimit(), + mDescriptor.getUpperLimit(), mDescriptor.getSpacing(), + mStereo); + return config; + } + + /** Set stereo enable state + * @param state The new enable state. + * @return the same Builder instance. + */ + public Builder setStereo(boolean state) { + mStereo = state; + return this; + } + }; + } + + /** Radio program information returned by + * {@link RadioTuner#getProgramInformation(RadioManager.ProgramInfo[])} */ + public static class ProgramInfo implements Parcelable { + + private final int mChannel; + private final int mSubChannel; + private final boolean mTuned; + private final boolean mStereo; + private final boolean mDigital; + private final int mSignalStrength; + private final RadioMetadata mMetadata; + + ProgramInfo(int channel, int subChannel, boolean tuned, boolean stereo, + boolean digital, int signalStrength, RadioMetadata metadata) { + mChannel = channel; + mSubChannel = subChannel; + mTuned = tuned; + mStereo = stereo; + mDigital = digital; + mSignalStrength = signalStrength; + mMetadata = metadata; + } + + /** Main channel expressed in units according to band type. + * Currently all defined band types express channels as frequency in kHz + * @return the program channel + */ + public int getChannel() { + return mChannel; + } + /** Sub channel ID. E.g 1 for HD radio HD1 + * @return the program sub channel + */ + public int getSubChannel() { + return mSubChannel; + } + /** {@code true} if the tuner is currently tuned on a valid station + * @return {@code true} if currently tuned, {@code false} otherwise. + */ + public boolean isTuned() { + return mTuned; + } + /** {@code true} if the received program is stereo + * @return {@code true} if stereo, {@code false} otherwise. + */ + public boolean isStereo() { + return mStereo; + } + /** {@code true} if the received program is digital (e.g HD radio) + * @return {@code true} if digital, {@code false} otherwise. + */ + public boolean isDigital() { + return mDigital; + } + /** Signal strength indicator from 0 (no signal) to 100 (excellent) + * @return the signal strength indication. + */ + public int getSignalStrength() { + return mSignalStrength; + } + /** Metadata currently received from this station. + * null if no metadata have been received + * @return current meta data received from this program. + */ + public RadioMetadata getMetadata() { + return mMetadata; + } + + private ProgramInfo(Parcel in) { + mChannel = in.readInt(); + mSubChannel = in.readInt(); + mTuned = in.readByte() == 1; + mStereo = in.readByte() == 1; + mDigital = in.readByte() == 1; + mSignalStrength = in.readInt(); + if (in.readByte() == 1) { + mMetadata = RadioMetadata.CREATOR.createFromParcel(in); + } else { + mMetadata = null; + } + } + + public static final Parcelable.Creator<ProgramInfo> CREATOR + = new Parcelable.Creator<ProgramInfo>() { + public ProgramInfo createFromParcel(Parcel in) { + return new ProgramInfo(in); + } + + public ProgramInfo[] newArray(int size) { + return new ProgramInfo[size]; + } + }; + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mChannel); + dest.writeInt(mSubChannel); + dest.writeByte((byte)(mTuned ? 1 : 0)); + dest.writeByte((byte)(mStereo ? 1 : 0)); + dest.writeByte((byte)(mDigital ? 1 : 0)); + dest.writeInt(mSignalStrength); + if (mMetadata == null) { + dest.writeByte((byte)0); + } else { + dest.writeByte((byte)1); + mMetadata.writeToParcel(dest, flags); + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + return "ProgramInfo [mChannel=" + mChannel + ", mSubChannel=" + mSubChannel + + ", mTuned=" + mTuned + ", mStereo=" + mStereo + ", mDigital=" + mDigital + + ", mSignalStrength=" + mSignalStrength + + ((mMetadata == null) ? "" : (", mMetadata=" + mMetadata.toString())) + + "]"; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + mChannel; + result = prime * result + mSubChannel; + result = prime * result + (mTuned ? 1 : 0); + result = prime * result + (mStereo ? 1 : 0); + result = prime * result + (mDigital ? 1 : 0); + result = prime * result + mSignalStrength; + result = prime * result + ((mMetadata == null) ? 0 : mMetadata.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!(obj instanceof ProgramInfo)) + return false; + ProgramInfo other = (ProgramInfo) obj; + if (mChannel != other.getChannel()) + return false; + if (mSubChannel != other.getSubChannel()) + return false; + if (mTuned != other.isTuned()) + return false; + if (mStereo != other.isStereo()) + return false; + if (mDigital != other.isDigital()) + return false; + if (mSignalStrength != other.getSignalStrength()) + return false; + if (mMetadata == null) { + if (other.getMetadata() != null) + return false; + } else if (!mMetadata.equals(other.getMetadata())) + return false; + return true; + } + } + + + /** + * Returns a list of descriptors for all broadcast radio modules present on the device. + * @param modules An List of {@link ModuleProperties} where the list will be returned. + * @return + * <ul> + * <li>{@link #STATUS_OK} in case of success, </li> + * <li>{@link #STATUS_ERROR} in case of unspecified error, </li> + * <li>{@link #STATUS_NO_INIT} if the native service cannot be reached, </li> + * <li>{@link #STATUS_BAD_VALUE} if modules is null, </li> + * <li>{@link #STATUS_DEAD_OBJECT} if the binder transaction to the native service fails, </li> + * </ul> + */ + public native int listModules(List <ModuleProperties> modules); + + /** + * Open an interface to control a tuner on a given broadcast radio module. + * Optionally selects and applies the configuration passed as "config" argument. + * @param moduleId radio module identifier {@link ModuleProperties#getId()}. Mandatory. + * @param config desired band and configuration to apply when enabling the hardware module. + * optional, can be null. + * @param withAudio {@code true} to request a tuner with an audio source. + * This tuner is intended for live listening or recording or a radio program. + * If {@code false}, the tuner can only be used to retrieve program informations. + * @param callback {@link RadioTuner.Callback} interface. Mandatory. + * @param handler the Handler on which the callbacks will be received. + * Can be null if default handler is OK. + * @return a valid {@link RadioTuner} interface in case of success or null in case of error. + */ + public RadioTuner openTuner(int moduleId, BandConfig config, boolean withAudio, + RadioTuner.Callback callback, Handler handler) { + if (callback == null) { + return null; + } + RadioModule module = new RadioModule(moduleId, config, withAudio, callback, handler); + if (module != null) { + if (!module.initCheck()) { + module = null; + } + } + return (RadioTuner)module; + } + + private final Context mContext; + + /** + * @hide + */ + public RadioManager(Context context) { + mContext = context; + } +} diff --git a/core/java/android/hardware/radio/RadioMetadata.java b/core/java/android/hardware/radio/RadioMetadata.java new file mode 100644 index 0000000..8b1851b --- /dev/null +++ b/core/java/android/hardware/radio/RadioMetadata.java @@ -0,0 +1,449 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package android.hardware.radio; + +import android.annotation.NonNull; +import android.annotation.SystemApi; +import android.content.ContentResolver; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.net.Uri; +import android.os.Bundle; +import android.os.Parcel; +import android.os.Parcelable; +import android.text.TextUtils; +import android.util.ArrayMap; +import android.util.Log; +import android.util.SparseArray; + +import java.util.ArrayList; +import java.util.Set; + +/** + * Contains meta data about a radio program such as station name, song title, artist etc... + * @hide + */ +@SystemApi +public final class RadioMetadata implements Parcelable { + private static final String TAG = "RadioMetadata"; + + /** + * The RDS Program Information. + */ + public static final String METADATA_KEY_RDS_PI = "android.hardware.radio.metadata.RDS_PI"; + + /** + * The RDS Program Service. + */ + public static final String METADATA_KEY_RDS_PS = "android.hardware.radio.metadata.RDS_PS"; + + /** + * The RDS PTY. + */ + public static final String METADATA_KEY_RDS_PTY = "android.hardware.radio.metadata.RDS_PTY"; + + /** + * The RBDS PTY. + */ + public static final String METADATA_KEY_RBDS_PTY = "android.hardware.radio.metadata.RBDS_PTY"; + + /** + * The RBDS Radio Text. + */ + public static final String METADATA_KEY_RDS_RT = "android.hardware.radio.metadata.RDS_RT"; + + /** + * The song title. + */ + public static final String METADATA_KEY_TITLE = "android.hardware.radio.metadata.TITLE"; + + /** + * The artist name. + */ + public static final String METADATA_KEY_ARTIST = "android.hardware.radio.metadata.ARTIST"; + + /** + * The album name. + */ + public static final String METADATA_KEY_ALBUM = "android.hardware.radio.metadata.ALBUM"; + + /** + * The music genre. + */ + public static final String METADATA_KEY_GENRE = "android.hardware.radio.metadata.GENRE"; + + /** + * The radio station icon {@link Bitmap}. + */ + public static final String METADATA_KEY_ICON = "android.hardware.radio.metadata.ICON"; + + /** + * The artwork for the song/album {@link Bitmap}. + */ + public static final String METADATA_KEY_ART = "android.hardware.radio.metadata.ART"; + + + private static final int METADATA_TYPE_INVALID = -1; + private static final int METADATA_TYPE_INT = 0; + private static final int METADATA_TYPE_TEXT = 1; + private static final int METADATA_TYPE_BITMAP = 2; + + private static final ArrayMap<String, Integer> METADATA_KEYS_TYPE; + + static { + METADATA_KEYS_TYPE = new ArrayMap<String, Integer>(); + METADATA_KEYS_TYPE.put(METADATA_KEY_RDS_PI, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_RDS_PS, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_RDS_PTY, METADATA_TYPE_INT); + METADATA_KEYS_TYPE.put(METADATA_KEY_RBDS_PTY, METADATA_TYPE_INT); + METADATA_KEYS_TYPE.put(METADATA_KEY_RDS_RT, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_TITLE, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_ARTIST, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_ALBUM, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_GENRE, METADATA_TYPE_TEXT); + METADATA_KEYS_TYPE.put(METADATA_KEY_ICON, METADATA_TYPE_BITMAP); + METADATA_KEYS_TYPE.put(METADATA_KEY_ART, METADATA_TYPE_BITMAP); + } + + // keep in sync with: system/media/radio/include/system/radio_metadata.h + private static final int NATIVE_KEY_INVALID = -1; + private static final int NATIVE_KEY_RDS_PI = 0; + private static final int NATIVE_KEY_RDS_PS = 1; + private static final int NATIVE_KEY_RDS_PTY = 2; + private static final int NATIVE_KEY_RBDS_PTY = 3; + private static final int NATIVE_KEY_RDS_RT = 4; + private static final int NATIVE_KEY_TITLE = 5; + private static final int NATIVE_KEY_ARTIST = 6; + private static final int NATIVE_KEY_ALBUM = 7; + private static final int NATIVE_KEY_GENRE = 8; + private static final int NATIVE_KEY_ICON = 9; + private static final int NATIVE_KEY_ART = 10; + + private static final SparseArray<String> NATIVE_KEY_MAPPING; + + static { + NATIVE_KEY_MAPPING = new SparseArray<String>(); + NATIVE_KEY_MAPPING.put(NATIVE_KEY_RDS_PI, METADATA_KEY_RDS_PI); + NATIVE_KEY_MAPPING.put(NATIVE_KEY_RDS_PS, METADATA_KEY_RDS_PS); + NATIVE_KEY_MAPPING.put(NATIVE_KEY_RDS_PTY, METADATA_KEY_RDS_PTY); + NATIVE_KEY_MAPPING.put(NATIVE_KEY_RBDS_PTY, METADATA_KEY_RBDS_PTY); + NATIVE_KEY_MAPPING.put(NATIVE_KEY_RDS_RT, METADATA_KEY_RDS_RT); + NATIVE_KEY_MAPPING.put(NATIVE_KEY_TITLE, METADATA_KEY_TITLE); + NATIVE_KEY_MAPPING.put(NATIVE_KEY_ARTIST, METADATA_KEY_ARTIST); + NATIVE_KEY_MAPPING.put(NATIVE_KEY_ALBUM, METADATA_KEY_ALBUM); + NATIVE_KEY_MAPPING.put(NATIVE_KEY_GENRE, METADATA_KEY_GENRE); + NATIVE_KEY_MAPPING.put(NATIVE_KEY_ICON, METADATA_KEY_ICON); + NATIVE_KEY_MAPPING.put(NATIVE_KEY_ART, METADATA_KEY_ART); + } + + private final Bundle mBundle; + + RadioMetadata() { + mBundle = new Bundle(); + } + + private RadioMetadata(Bundle bundle) { + mBundle = new Bundle(bundle); + } + + private RadioMetadata(Parcel in) { + mBundle = in.readBundle(); + } + + /** + * Returns {@code true} if the given key is contained in the meta data + * + * @param key a String key + * @return {@code true} if the key exists in this meta data, {@code false} otherwise + */ + public boolean containsKey(String key) { + return mBundle.containsKey(key); + } + + /** + * Returns the text value associated with the given key as a String, or null + * if the key is not found in the meta data. + * + * @param key The key the value is stored under + * @return a String value, or null + */ + public String getString(String key) { + return mBundle.getString(key); + } + + /** + * Returns the value associated with the given key, + * or 0 if the key is not found in the meta data. + * + * @param key The key the value is stored under + * @return an int value + */ + public int getInt(String key) { + return mBundle.getInt(key, 0); + } + + /** + * Returns a {@link Bitmap} for the given key or null if the key is not found in the meta data. + * + * @param key The key the value is stored under + * @return a {@link Bitmap} or null + */ + public Bitmap getBitmap(String key) { + Bitmap bmp = null; + try { + bmp = mBundle.getParcelable(key); + } catch (Exception e) { + // ignore, value was not a bitmap + Log.w(TAG, "Failed to retrieve a key as Bitmap.", e); + } + return bmp; + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeBundle(mBundle); + } + + /** + * Returns the number of fields in this meta data. + * + * @return the number of fields in the meta data. + */ + public int size() { + return mBundle.size(); + } + + /** + * Returns a Set containing the Strings used as keys in this meta data. + * + * @return a Set of String keys + */ + public Set<String> keySet() { + return mBundle.keySet(); + } + + /** + * Helper for getting the String key used by {@link RadioMetadata} from the + * corrsponding native integer key. + * + * @param editorKey The key used by the editor + * @return the key used by this class or null if no mapping exists + * @hide + */ + public static String getKeyFromNativeKey(int nativeKey) { + return NATIVE_KEY_MAPPING.get(nativeKey, null); + } + + public static final Parcelable.Creator<RadioMetadata> CREATOR = + new Parcelable.Creator<RadioMetadata>() { + @Override + public RadioMetadata createFromParcel(Parcel in) { + return new RadioMetadata(in); + } + + @Override + public RadioMetadata[] newArray(int size) { + return new RadioMetadata[size]; + } + }; + + /** + * Use to build RadioMetadata objects. + */ + public static final class Builder { + private final Bundle mBundle; + + /** + * Create an empty Builder. Any field that should be included in the + * {@link RadioMetadata} must be added. + */ + public Builder() { + mBundle = new Bundle(); + } + + /** + * Create a Builder using a {@link RadioMetadata} instance to set the + * initial values. All fields in the source meta data will be included in + * the new meta data. Fields can be overwritten by adding the same key. + * + * @param source + */ + public Builder(RadioMetadata source) { + mBundle = new Bundle(source.mBundle); + } + + /** + * Create a Builder using a {@link RadioMetadata} instance to set + * initial values, but replace bitmaps with a scaled down copy if they + * are larger than maxBitmapSize. + * + * @param source The original meta data to copy. + * @param maxBitmapSize The maximum height/width for bitmaps contained + * in the meta data. + * @hide + */ + public Builder(RadioMetadata source, int maxBitmapSize) { + this(source); + for (String key : mBundle.keySet()) { + Object value = mBundle.get(key); + if (value != null && value instanceof Bitmap) { + Bitmap bmp = (Bitmap) value; + if (bmp.getHeight() > maxBitmapSize || bmp.getWidth() > maxBitmapSize) { + putBitmap(key, scaleBitmap(bmp, maxBitmapSize)); + } + } + } + } + + /** + * Put a String value into the meta data. Custom keys may be used, but if + * the METADATA_KEYs defined in this class are used they may only be one + * of the following: + * <ul> + * <li>{@link #METADATA_KEY_RDS_PI}</li> + * <li>{@link #METADATA_KEY_RDS_PS}</li> + * <li>{@link #METADATA_KEY_RDS_RT}</li> + * <li>{@link #METADATA_KEY_TITLE}</li> + * <li>{@link #METADATA_KEY_ARTIST}</li> + * <li>{@link #METADATA_KEY_ALBUM}</li> + * <li>{@link #METADATA_KEY_GENRE}</li> + * </ul> + * + * @param key The key for referencing this value + * @param value The String value to store + * @return the same Builder instance + */ + public Builder putString(String key, String value) { + if (!METADATA_KEYS_TYPE.containsKey(key) || + METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) { + throw new IllegalArgumentException("The " + key + + " key cannot be used to put a String"); + } + mBundle.putString(key, value); + return this; + } + + /** + * Put an int value into the meta data. Custom keys may be used, but if + * the METADATA_KEYs defined in this class are used they may only be one + * of the following: + * <ul> + * <li>{@link #METADATA_KEY_RDS_PTY}</li> + * <li>{@link #METADATA_KEY_RBDS_PTY}</li> + * </ul> + * + * @param key The key for referencing this value + * @param value The int value to store + * @return the same Builder instance + */ + public Builder putInt(String key, int value) { + if (!METADATA_KEYS_TYPE.containsKey(key) || + METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_INT) { + throw new IllegalArgumentException("The " + key + + " key cannot be used to put a long"); + } + mBundle.putInt(key, value); + return this; + } + + /** + * Put a {@link Bitmap} into the meta data. Custom keys may be used, but + * if the METADATA_KEYs defined in this class are used they may only be + * one of the following: + * <ul> + * <li>{@link #METADATA_KEY_ICON}</li> + * <li>{@link #METADATA_KEY_ART}</li> + * </ul> + * <p> + * + * @param key The key for referencing this value + * @param value The Bitmap to store + * @return the same Builder instance + */ + public Builder putBitmap(String key, Bitmap value) { + if (!METADATA_KEYS_TYPE.containsKey(key) || + METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_BITMAP) { + throw new IllegalArgumentException("The " + key + + " key cannot be used to put a Bitmap"); + } + mBundle.putParcelable(key, value); + return this; + } + + /** + * Creates a {@link RadioMetadata} instance with the specified fields. + * + * @return a new {@link RadioMetadata} object + */ + public RadioMetadata build() { + return new RadioMetadata(mBundle); + } + + private Bitmap scaleBitmap(Bitmap bmp, int maxSize) { + float maxSizeF = maxSize; + float widthScale = maxSizeF / bmp.getWidth(); + float heightScale = maxSizeF / bmp.getHeight(); + float scale = Math.min(widthScale, heightScale); + int height = (int) (bmp.getHeight() * scale); + int width = (int) (bmp.getWidth() * scale); + return Bitmap.createScaledBitmap(bmp, width, height, true); + } + } + + int putIntFromNative(int nativeKey, int value) { + String key = getKeyFromNativeKey(nativeKey); + if (!METADATA_KEYS_TYPE.containsKey(key) || + METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_INT) { + return -1; + } + mBundle.putInt(key, value); + return 0; + } + + int putStringFromNative(int nativeKey, String value) { + String key = getKeyFromNativeKey(nativeKey); + if (!METADATA_KEYS_TYPE.containsKey(key) || + METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_TEXT) { + return -1; + } + mBundle.putString(key, value); + return 0; + } + + int putBitmapFromNative(int nativeKey, byte[] value) { + String key = getKeyFromNativeKey(nativeKey); + if (!METADATA_KEYS_TYPE.containsKey(key) || + METADATA_KEYS_TYPE.get(key) != METADATA_TYPE_BITMAP) { + return -1; + } + Bitmap bmp = null; + try { + bmp = BitmapFactory.decodeByteArray(value, 0, value.length); + } catch (Exception e) { + } finally { + if (bmp == null) { + return -1; + } + mBundle.putParcelable(key, bmp); + return 0; + } + } +} diff --git a/core/java/android/hardware/radio/RadioModule.java b/core/java/android/hardware/radio/RadioModule.java new file mode 100644 index 0000000..15916ae --- /dev/null +++ b/core/java/android/hardware/radio/RadioModule.java @@ -0,0 +1,218 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.radio; + +import android.annotation.SystemApi; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import java.lang.ref.WeakReference; +import java.util.UUID; + +/** + * A RadioModule implements the RadioTuner interface for a broadcast radio tuner physically + * present on the device and exposed by the radio HAL. + * + * @hide + */ +public class RadioModule extends RadioTuner { + private long mNativeContext = 0; + private int mId; + private NativeEventHandlerDelegate mEventHandlerDelegate; + + RadioModule(int moduleId, RadioManager.BandConfig config, boolean withAudio, + RadioTuner.Callback callback, Handler handler) { + mId = moduleId; + mEventHandlerDelegate = new NativeEventHandlerDelegate(callback, handler); + native_setup(new WeakReference<RadioModule>(this), config, withAudio); + } + private native void native_setup(Object module_this, + RadioManager.BandConfig config, boolean withAudio); + + @Override + protected void finalize() { + native_finalize(); + } + private native void native_finalize(); + + boolean initCheck() { + return mNativeContext != 0; + } + + // RadioTuner implementation + public native void close(); + + public native int setConfiguration(RadioManager.BandConfig config); + + public native int getConfiguration(RadioManager.BandConfig[] config); + + public native int setMute(boolean mute); + + public native boolean getMute(); + + public native int step(int direction, boolean skipSubChannel); + + public native int scan(int direction, boolean skipSubChannel); + + public native int tune(int channel, int subChannel); + + public native int cancel(); + + public native int getProgramInformation(RadioManager.ProgramInfo[] info); + + public native boolean isAntennaConnected(); + + public native boolean hasControl(); + + + /* keep in sync with radio_event_type_t in system/core/include/system/radio.h */ + static final int EVENT_HW_FAILURE = 0; + static final int EVENT_CONFIG = 1; + static final int EVENT_ANTENNA = 2; + static final int EVENT_TUNED = 3; + static final int EVENT_METADATA = 4; + static final int EVENT_TA = 5; + static final int EVENT_AF_SWITCH = 6; + static final int EVENT_CONTROL = 100; + static final int EVENT_SERVER_DIED = 101; + + private class NativeEventHandlerDelegate { + private final Handler mHandler; + + NativeEventHandlerDelegate(final RadioTuner.Callback callback, + Handler handler) { + // find the looper for our new event handler + Looper looper; + if (handler != null) { + looper = handler.getLooper(); + } else { + looper = Looper.getMainLooper(); + } + + // construct the event handler with this looper + if (looper != null) { + // implement the event handler delegate + mHandler = new Handler(looper) { + @Override + public void handleMessage(Message msg) { + switch (msg.what) { + case EVENT_HW_FAILURE: + if (callback != null) { + callback.onError(RadioTuner.ERROR_HARDWARE_FAILURE); + } + break; + case EVENT_CONFIG: { + RadioManager.BandConfig config = (RadioManager.BandConfig)msg.obj; + switch(msg.arg1) { + case RadioManager.STATUS_OK: + if (callback != null) { + callback.onConfigurationChanged(config); + } + break; + default: + if (callback != null) { + callback.onError(RadioTuner.ERROR_CONFIG); + } + break; + } + } break; + case EVENT_ANTENNA: + if (callback != null) { + callback.onAntennaState(msg.arg2 == 1); + } + break; + case EVENT_AF_SWITCH: + case EVENT_TUNED: { + RadioManager.ProgramInfo info = (RadioManager.ProgramInfo)msg.obj; + switch (msg.arg1) { + case RadioManager.STATUS_OK: + if (callback != null) { + callback.onProgramInfoChanged(info); + } + break; + case RadioManager.STATUS_TIMED_OUT: + if (callback != null) { + callback.onError(RadioTuner.ERROR_SCAN_TIMEOUT); + } + break; + case RadioManager.STATUS_INVALID_OPERATION: + default: + if (callback != null) { + callback.onError(RadioTuner.ERROR_CANCELLED); + } + break; + } + } break; + case EVENT_METADATA: { + RadioMetadata metadata = (RadioMetadata)msg.obj; + if (callback != null) { + callback.onMetadataChanged(metadata); + } + } break; + case EVENT_TA: + if (callback != null) { + callback.onTrafficAnnouncement(msg.arg2 == 1); + } + break; + case EVENT_CONTROL: + if (callback != null) { + callback.onControlChanged(msg.arg2 == 1); + } + break; + case EVENT_SERVER_DIED: + if (callback != null) { + callback.onError(RadioTuner.ERROR_SERVER_DIED); + } + break; + default: + // Should not happen + break; + } + } + }; + } else { + mHandler = null; + } + } + + Handler handler() { + return mHandler; + } + } + + + @SuppressWarnings("unused") + private static void postEventFromNative(Object module_ref, + int what, int arg1, int arg2, Object obj) { + RadioModule module = (RadioModule)((WeakReference)module_ref).get(); + if (module == null) { + return; + } + + NativeEventHandlerDelegate delegate = module.mEventHandlerDelegate; + if (delegate != null) { + Handler handler = delegate.handler(); + if (handler != null) { + Message m = handler.obtainMessage(what, arg1, arg2, obj); + handler.sendMessage(m); + } + } + } +} + diff --git a/core/java/android/hardware/radio/RadioTuner.java b/core/java/android/hardware/radio/RadioTuner.java new file mode 100644 index 0000000..376900a --- /dev/null +++ b/core/java/android/hardware/radio/RadioTuner.java @@ -0,0 +1,302 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.hardware.radio; + +import android.annotation.SystemApi; +import android.content.Context; +import android.content.Intent; +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import java.lang.ref.WeakReference; +import java.util.UUID; + +/** + * RadioTuner interface provides methods to control a radio tuner on the device: selecting and + * configuring the active band, muting/unmuting, scanning and tuning, etc... + * + * Obtain a RadioTuner interface by calling {@link RadioManager#openTuner(int, + * RadioManager.BandConfig, boolean, RadioTuner.Callback, Handler)}. + * @hide + */ +@SystemApi +public abstract class RadioTuner { + + /** Scanning direction UP for {@link #step(int, boolean)}, {@link #scan(int, boolean)} */ + public static final int DIRECTION_UP = 0; + + /** Scanning directions DOWN for {@link #step(int, boolean)}, {@link #scan(int, boolean)} */ + public static final int DIRECTION_DOWN = 1; + + /** + * Close the tuner interface. The {@link Callback} callback will not be called + * anymore and associated resources will be released. + * Must be called when the tuner is not needed to make hardware resources available to others. + * */ + public abstract void close(); + + /** + * Set the active band configuration for this module. + * Must be a valid configuration obtained via buildConfig() from a valid BandDescriptor listed + * in the ModuleProperties of the module with the specified ID. + * @param config The desired band configuration (FmBandConfig or AmBandConfig). + * @return + * <ul> + * <li>{@link RadioManager#STATUS_OK} in case of success, </li> + * <li>{@link RadioManager#STATUS_ERROR} in case of unspecified error, </li> + * <li>{@link RadioManager#STATUS_NO_INIT} if the native service cannot be reached, </li> + * <li>{@link RadioManager#STATUS_BAD_VALUE} if parameters are invalid, </li> + * <li>{@link RadioManager#STATUS_INVALID_OPERATION} if the call is out of sequence, </li> + * <li>{@link RadioManager#STATUS_DEAD_OBJECT} if the binder transaction to the native + * service fails, </li> + * </ul> + */ + public abstract int setConfiguration(RadioManager.BandConfig config); + + /** + * Get current configuration. + * @param config a BandConfig array of lengh 1 where the configuration is returned. + * @return + * <ul> + * <li>{@link RadioManager#STATUS_OK} in case of success, </li> + * <li>{@link RadioManager#STATUS_ERROR} in case of unspecified error, </li> + * <li>{@link RadioManager#STATUS_NO_INIT} if the native service cannot be reached, </li> + * <li>{@link RadioManager#STATUS_BAD_VALUE} if parameters are invalid, </li> + * <li>{@link RadioManager#STATUS_INVALID_OPERATION} if the call is out of sequence, </li> + * <li>{@link RadioManager#STATUS_DEAD_OBJECT} if the binder transaction to the native + * service fails, </li> + * </ul> + */ + public abstract int getConfiguration(RadioManager.BandConfig[] config); + + + /** + * Set mute state. When muted, the radio tuner audio source is not available for playback on + * any audio device. when unmuted, the radio tuner audio source is output as a media source + * and renderd over the audio device selected for media use case. + * The radio tuner audio source is muted by default when the tuner is first attached. + * Only effective if the tuner is attached with audio enabled. + * + * @param mute the requested mute state. + * @return + * <ul> + * <li>{@link RadioManager#STATUS_OK} in case of success, </li> + * <li>{@link RadioManager#STATUS_ERROR} in case of unspecified error, </li> + * <li>{@link RadioManager#STATUS_NO_INIT} if the native service cannot be reached, </li> + * <li>{@link RadioManager#STATUS_INVALID_OPERATION} if the call is out of sequence, </li> + * <li>{@link RadioManager#STATUS_DEAD_OBJECT} if the binder transaction to the native + * service fails, </li> + * </ul> + */ + public abstract int setMute(boolean mute); + + /** + * Get mute state. + * + * @return {@code true} if the radio tuner audio source is muted or a problem occured + * retrieving the mute state, {@code false} otherwise. + */ + public abstract boolean getMute(); + + /** + * Step up or down by one channel spacing. + * The operation is asynchronous and {@link Callback} + * onProgramInfoChanged() will be called when step completes or + * onError() when cancelled or timeout. + * @param direction {@link #DIRECTION_UP} or {@link #DIRECTION_DOWN}. + * @param skipSubChannel indicates to skip sub channels when the configuration currently + * selected supports sub channel (e.g HD Radio). N/A otherwise. + * @return + * <ul> + * <li>{@link RadioManager#STATUS_OK} in case of success, </li> + * <li>{@link RadioManager#STATUS_ERROR} in case of unspecified error, </li> + * <li>{@link RadioManager#STATUS_NO_INIT} if the native service cannot be reached, </li> + * <li>{@link RadioManager#STATUS_BAD_VALUE} if parameters are invalid, </li> + * <li>{@link RadioManager#STATUS_INVALID_OPERATION} if the call is out of sequence, </li> + * <li>{@link RadioManager#STATUS_DEAD_OBJECT} if the binder transaction to the native + * service fails, </li> + * </ul> + */ + public abstract int step(int direction, boolean skipSubChannel); + + /** + * Scan up or down to next valid station. + * The operation is asynchronous and {@link Callback} + * onProgramInfoChanged() will be called when scan completes or + * onError() when cancelled or timeout. + * @param direction {@link #DIRECTION_UP} or {@link #DIRECTION_DOWN}. + * @param skipSubChannel indicates to skip sub channels when the configuration currently + * selected supports sub channel (e.g HD Radio). N/A otherwise. + * @return + * <ul> + * <li>{@link RadioManager#STATUS_OK} in case of success, </li> + * <li>{@link RadioManager#STATUS_ERROR} in case of unspecified error, </li> + * <li>{@link RadioManager#STATUS_NO_INIT} if the native service cannot be reached, </li> + * <li>{@link RadioManager#STATUS_BAD_VALUE} if parameters are invalid, </li> + * <li>{@link RadioManager#STATUS_INVALID_OPERATION} if the call is out of sequence, </li> + * <li>{@link RadioManager#STATUS_DEAD_OBJECT} if the binder transaction to the native + * service fails, </li> + * </ul> + */ + public abstract int scan(int direction, boolean skipSubChannel); + + /** + * Tune to a specific frequency. + * The operation is asynchronous and {@link Callback} + * onProgramInfoChanged() will be called when tune completes or + * onError() when cancelled or timeout. + * @param channel the specific channel or frequency to tune to. + * @param subChannel the specific sub-channel to tune to. N/A if the selected configuration + * does not support cub channels. + * @return + * <ul> + * <li>{@link RadioManager#STATUS_OK} in case of success, </li> + * <li>{@link RadioManager#STATUS_ERROR} in case of unspecified error, </li> + * <li>{@link RadioManager#STATUS_NO_INIT} if the native service cannot be reached, </li> + * <li>{@link RadioManager#STATUS_BAD_VALUE} if parameters are invalid, </li> + * <li>{@link RadioManager#STATUS_INVALID_OPERATION} if the call is out of sequence, </li> + * <li>{@link RadioManager#STATUS_DEAD_OBJECT} if the binder transaction to the native + * service fails, </li> + * </ul> + */ + public abstract int tune(int channel, int subChannel); + + /** + * Cancel a pending scan or tune operation. + * If an operation is pending, {@link Callback} onError() will be called with + * {@link #ERROR_CANCELLED}. + * @return + * <ul> + * <li>{@link RadioManager#STATUS_OK} in case of success, </li> + * <li>{@link RadioManager#STATUS_ERROR} in case of unspecified error, </li> + * <li>{@link RadioManager#STATUS_NO_INIT} if the native service cannot be reached, </li> + * <li>{@link RadioManager#STATUS_BAD_VALUE} if parameters are invalid, </li> + * <li>{@link RadioManager#STATUS_INVALID_OPERATION} if the call is out of sequence, </li> + * <li>{@link RadioManager#STATUS_DEAD_OBJECT} if the binder transaction to the native + * service fails, </li> + * </ul> + */ + public abstract int cancel(); + + /** + * Get current station information. + * @param info a ProgramInfo array of lengh 1 where the information is returned. + * @return + * <ul> + * <li>{@link RadioManager#STATUS_OK} in case of success, </li> + * <li>{@link RadioManager#STATUS_ERROR} in case of unspecified error, </li> + * <li>{@link RadioManager#STATUS_NO_INIT} if the native service cannot be reached, </li> + * <li>{@link RadioManager#STATUS_BAD_VALUE} if parameters are invalid, </li> + * <li>{@link RadioManager#STATUS_INVALID_OPERATION} if the call is out of sequence, </li> + * <li>{@link RadioManager#STATUS_DEAD_OBJECT} if the binder transaction to the native + * service fails, </li> + * </ul> + */ + public abstract int getProgramInformation(RadioManager.ProgramInfo[] info); + + /** + * Get current antenna connection state for current configuration. + * Only valid if a configuration has been applied. + * @return {@code true} if the antenna is connected, {@code false} otherwise. + */ + public abstract boolean isAntennaConnected(); + + /** + * Indicates if this client actually controls the tuner. + * Control is always granted after + * {@link RadioManager#openTuner(int, + * RadioManager.BandConfig, boolean, Callback, Handler)} + * returns a non null tuner interface. + * Control is lost when another client opens an interface on the same tuner. + * When this happens, {@link Callback#onControlChanged(boolean)} is received. + * The client can either wait for control to be returned (which is indicated by the same + * callback) or close and reopen the tuner interface. + * @return {@code true} if this interface controls the tuner, + * {@code false} otherwise or if a problem occured retrieving the state. + */ + public abstract boolean hasControl(); + + /** Indicates a failure of radio IC or driver. + * The application must close and re open the tuner */ + public static final int ERROR_HARDWARE_FAILURE = 0; + /** Indicates a failure of the radio service. + * The application must close and re open the tuner */ + public static final int ERROR_SERVER_DIED = 1; + /** A pending seek or tune operation was cancelled */ + public static final int ERROR_CANCELLED = 2; + /** A pending seek or tune operation timed out */ + public static final int ERROR_SCAN_TIMEOUT = 3; + /** The requested configuration could not be applied */ + public static final int ERROR_CONFIG = 4; + + /** + * Callback provided by the client application when opening a {@link RadioTuner} + * to receive asynchronous operation results, updates and error notifications. + */ + public static abstract class Callback { + /** + * onError() is called when an error occured while performing an asynchronous + * operation of when the hardware or system service experiences a problem. + * status is one of {@link #ERROR_HARDWARE_FAILURE}, {@link #ERROR_SERVER_DIED}, + * {@link #ERROR_CANCELLED}, {@link #ERROR_SCAN_TIMEOUT}, + * {@link #ERROR_CONFIG} + */ + public void onError(int status) {} + /** + * onConfigurationChanged() is called upon successful completion of + * {@link RadioManager#openTuner(int, RadioManager.BandConfig, boolean, Callback, Handler)} + * or {@link RadioTuner#setConfiguration(RadioManager.BandConfig)} + */ + public void onConfigurationChanged(RadioManager.BandConfig config) {} + /** + * onProgramInfoChanged() is called upon successful completion of + * {@link RadioTuner#step(int, boolean)}, {@link RadioTuner#scan(int, boolean)}, + * {@link RadioTuner#tune(int, int)} or when a switching to alternate frequency occurs. + * Note that if metadata only are updated, {@link #onMetadataChanged(RadioMetadata)} will + * be called. + */ + public void onProgramInfoChanged(RadioManager.ProgramInfo info) {} + /** + * onMetadataChanged() is called when new meta data are received on current program. + * Meta data are also received in {@link RadioManager.ProgramInfo} when + * {@link #onProgramInfoChanged(RadioManager.ProgramInfo)} is called. + */ + public void onMetadataChanged(RadioMetadata metadata) {} + /** + * onTrafficAnnouncement() is called when a traffic announcement starts and stops. + */ + public void onTrafficAnnouncement(boolean active) {} + /** + * onAntennaState() is called when the antenna is connected or disconnected. + */ + public void onAntennaState(boolean connected) {} + /** + * onControlChanged() is called when the client loses or gains control of the radio tuner. + * The control is always granted after a successful call to + * {@link RadioManager#openTuner(int, RadioManager.BandConfig, boolean, Callback, Handler)}. + * If another client opens the same tuner, onControlChanged() will be called with + * control set to {@code false} to indicate loss of control. + * At this point, RadioTuner APIs other than getters will return + * {@link RadioManager#STATUS_INVALID_OPERATION}. + * When the other client releases the tuner, onControlChanged() will be called + * with control set to {@code true}. + */ + public void onControlChanged(boolean control) {} + } + +} + |