Skip to content
Browse files

FM Radio: Add support for FM Radio in Android

Creating interface and framework for using FM Radio
RX and TX from different vendors.

Signed-off-by: Christian Bejram <christian.bejram@stericsson.com>
Change-Id: I1a71aed01bfffdddfabf1cdfbfa3707cb1ed016b

Conflicts:
	core/java/android/app/ContextImpl.java

Conflicts:

	core/java/android/app/ContextImpl.java

Signed-off-by: ChampionSwimmer <android@championswimmer.tk>
  • Loading branch information...
1 parent f81b512 commit a6ab9925376fc275fc466c8585bbfe3e77ecd9b4 Anurag Gupta committed with Gerrit Code Review Jul 18, 2012
Showing with 11,100 additions and 5 deletions.
  1. +16 −5 Android.mk
  2. +21 −0 core/java/android/app/ContextImpl.java
  3. +16 −0 core/java/android/content/pm/PackageManager.java
  4. +16 −0 core/res/AndroidManifest.xml
  5. +12 −0 core/res/res/values/strings.xml
  6. +20 −0 data/etc/com.stericsson.hardware.fm.receiver.xml
  7. +20 −0 data/etc/com.stericsson.hardware.fm.transmitter.xml
  8. +203 −0 fmradio/include/android_fmradio.h
  9. +22 −0 fmradio/java/com/stericsson/hardware/fm/FmBand.aidl
  10. +258 −0 fmradio/java/com/stericsson/hardware/fm/FmBand.java
  11. +1,327 −0 fmradio/java/com/stericsson/hardware/fm/FmReceiver.java
  12. +1,126 −0 fmradio/java/com/stericsson/hardware/fm/FmReceiverImpl.java
  13. +1,347 −0 fmradio/java/com/stericsson/hardware/fm/FmReceiverService.java
  14. +807 −0 fmradio/java/com/stericsson/hardware/fm/FmTransmitter.java
  15. +703 −0 fmradio/java/com/stericsson/hardware/fm/FmTransmitterImpl.java
  16. +846 −0 fmradio/java/com/stericsson/hardware/fm/FmTransmitterService.java
  17. +84 −0 fmradio/java/com/stericsson/hardware/fm/IFmReceiver.aidl
  18. +64 −0 fmradio/java/com/stericsson/hardware/fm/IFmTransmitter.aidl
  19. +28 −0 fmradio/java/com/stericsson/hardware/fm/IOnAutomaticSwitchListener.aidl
  20. +28 −0 fmradio/java/com/stericsson/hardware/fm/IOnBlockScanListener.aidl
  21. +28 −0 fmradio/java/com/stericsson/hardware/fm/IOnErrorListener.aidl
  22. +30 −0 fmradio/java/com/stericsson/hardware/fm/IOnExtraCommandListener.aidl
  23. +28 −0 fmradio/java/com/stericsson/hardware/fm/IOnForcedPauseListener.aidl
  24. +28 −0 fmradio/java/com/stericsson/hardware/fm/IOnForcedResetListener.aidl
  25. +30 −0 fmradio/java/com/stericsson/hardware/fm/IOnRDSDataFoundListener.aidl
  26. +29 −0 fmradio/java/com/stericsson/hardware/fm/IOnScanListener.aidl
  27. +28 −0 fmradio/java/com/stericsson/hardware/fm/IOnSignalStrengthListener.aidl
  28. +28 −0 fmradio/java/com/stericsson/hardware/fm/IOnStartedListener.aidl
  29. +28 −0 fmradio/java/com/stericsson/hardware/fm/IOnStateChangedListener.aidl
  30. +28 −0 fmradio/java/com/stericsson/hardware/fm/IOnStereoListener.aidl
  31. +13 −0 fmradio/java/com/stericsson/hardware/fm/package.html
  32. +41 −0 fmradio/jni/Android.mk
  33. +1,188 −0 fmradio/jni/android_fmradio.cpp
  34. +1,494 −0 fmradio/jni/android_fmradio_Receiver.cpp
  35. +1,098 −0 fmradio/jni/android_fmradio_Transmitter.cpp
  36. +17 −0 services/java/com/android/server/SystemServer.java
View
21 Android.mk 100644 → 100755
@@ -26,8 +26,6 @@ LOCAL_PATH := $(call my-dir)
# TODO: find a more appropriate way to do this.
framework_res_source_path := APPS/framework-res_intermediates/src
-# the library
-# ============================================================
include $(CLEAR_VARS)
# FRAMEWORKS_BASE_SUBDIRS comes from build/core/pathmap.mk
@@ -223,7 +221,21 @@ LOCAL_SRC_FILES += \
wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \
voip/java/android/net/sip/ISipSession.aidl \
voip/java/android/net/sip/ISipSessionListener.aidl \
- voip/java/android/net/sip/ISipService.aidl
+ voip/java/android/net/sip/ISipService.aidl \
+ fmradio/java/com/stericsson/hardware/fm/IFmReceiver.aidl \
+ fmradio/java/com/stericsson/hardware/fm/IFmTransmitter.aidl \
+ fmradio/java/com/stericsson/hardware/fm/IOnStateChangedListener.aidl \
+ fmradio/java/com/stericsson/hardware/fm/IOnStartedListener.aidl \
+ fmradio/java/com/stericsson/hardware/fm/IOnErrorListener.aidl \
+ fmradio/java/com/stericsson/hardware/fm/IOnScanListener.aidl \
+ fmradio/java/com/stericsson/hardware/fm/IOnForcedPauseListener.aidl \
+ fmradio/java/com/stericsson/hardware/fm/IOnForcedResetListener.aidl \
+ fmradio/java/com/stericsson/hardware/fm/IOnBlockScanListener.aidl \
+ fmradio/java/com/stericsson/hardware/fm/IOnRDSDataFoundListener.aidl \
+ fmradio/java/com/stericsson/hardware/fm/IOnSignalStrengthListener.aidl \
+ fmradio/java/com/stericsson/hardware/fm/IOnStereoListener.aidl \
+ fmradio/java/com/stericsson/hardware/fm/IOnExtraCommandListener.aidl \
+ fmradio/java/com/stericsson/hardware/fm/IOnAutomaticSwitchListener.aidl
#
@@ -555,7 +567,7 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES:=$(framework_docs_LOCAL_API_CHECK_SRC_FILES)
LOCAL_INTERMEDIATE_SOURCES:=$(framework_docs_LOCAL_INTERMEDIATE_SOURCES)
-LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES)
+LOCAL_JAVA_LIBRARIES:=$(framework_docs_LOCAL_JAVA_LIBRARIES) framework
LOCAL_MODULE_CLASS:=$(framework_docs_LOCAL_MODULE_CLASS)
LOCAL_DROIDDOC_SOURCE_PATH:=$(framework_docs_LOCAL_DROIDDOC_SOURCE_PATH)
LOCAL_DROIDDOC_HTML_DIR:=$(framework_docs_LOCAL_DROIDDOC_HTML_DIR)
@@ -790,7 +802,6 @@ LOCAL_DX_FLAGS := --core-library
include $(BUILD_JAVA_LIBRARY)
-
# Include subdirectory makefiles
# ============================================================
View
21 core/java/android/app/ContextImpl.java
@@ -124,6 +124,13 @@
import java.util.ArrayList;
import java.util.HashMap;
+import com.stericsson.hardware.fm.IFmReceiver;
+import com.stericsson.hardware.fm.IFmTransmitter;
+import com.stericsson.hardware.fm.FmReceiver;
+import com.stericsson.hardware.fm.FmTransmitter;
+import com.stericsson.hardware.fm.FmReceiverImpl;
+import com.stericsson.hardware.fm.FmTransmitterImpl;
+
class ReceiverRestrictedContext extends ContextWrapper {
ReceiverRestrictedContext(Context base) {
super(base);
@@ -539,6 +546,20 @@ public Object getService(ContextImpl ctx) {
IUserManager service = IUserManager.Stub.asInterface(b);
return new UserManager(ctx, service);
}});
+
+ registerService("fm_receiver", new ServiceFetcher() {
+ public Object createService(ContextImpl ctx) {
+ IBinder b = ServiceManager.getService("fm_receiver");
+ IFmReceiver service = IFmReceiver.Stub.asInterface(b);
+ return new FmReceiverImpl(service);
+ }});
+
+ registerService("fm_transmitter", new ServiceFetcher() {
+ public Object createService(ContextImpl ctx) {
+ IBinder b = ServiceManager.getService("fm_transmitter");
+ IFmTransmitter service = IFmTransmitter.Stub.asInterface(b);
+ return new FmTransmitterImpl(service);
+ }});
}
static ContextImpl getImpl(Context context) {
View
16 core/java/android/content/pm/PackageManager.java
@@ -858,6 +858,22 @@ public NameNotFoundException(String name) {
/**
* Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device is able to receive FM radio.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_RADIO_FM_RECEIVER = "com.stericsson.hardware.fm.receiver";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
+ * {@link #hasSystemFeature}: The device is able to transmit FM radio.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.FEATURE)
+ public static final String FEATURE_RADIO_FM_TRANSMITTER = "com.stericsson.hardware.fm.transmitter";
+
+ /**
+ * Feature for {@link #getSystemAvailableFeatures} and
* {@link #hasSystemFeature}: The device supports one or more methods of
* reporting current location.
*/
View
16 core/res/AndroidManifest.xml
@@ -803,6 +803,22 @@
android:label="@string/permlab_vibrate"
android:description="@string/permdesc_vibrate" />
+ <!-- Allows access to the FM Radio receiver
+ @hide Pending API council approval -->
+ <permission android:name="com.stericsson.permission.FM_RADIO_RECEIVER"
+ android:permissionGroup="android.permission-group.HARDWARE_CONTROLS"
+ android:protectionLevel="normal"
+ android:label="@string/permlab_fm_radio_receiver"
+ android:description="@string/permdesc_fm_radio_receiver" />
+
+ <!-- Allows access to the FM Radio transmitter
+ @hide Pending API council approval -->
+ <permission android:name="com.stericsson.permission.FM_RADIO_TRANSMITTER"
+ android:permissionGroup="android.permission-group.HARDWARE_CONTROLS"
+ android:protectionLevel="dangerous"
+ android:label="@string/permlab_fm_radio_transmitter"
+ android:description="@string/permdesc_fm_radio_transmitter" />
+
<!-- Allows access to the flashlight -->
<permission android:name="android.permission.FLASHLIGHT"
android:permissionGroup="android.permission-group.AFFECTS_BATTERY"
View
12 core/res/res/values/strings.xml
@@ -1410,6 +1410,18 @@
<string name="permdesc_vibrate">Allows the app to control the vibrator.</string>
<!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_fm_radio_receiver">control FM receiver</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_fm_radio_receiver">Allows the application to control
+ the FM receiver.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permlab_fm_radio_transmitter">control FM transmitter</string>
+ <!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
+ <string name="permdesc_fm_radio_transmitter">Allows the application to control
+ the FM transmitter.</string>
+
+ <!-- Title of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permlab_flashlight">control flashlight</string>
<!-- Description of an application permission, listed so the user can choose whether they want to allow the application to do this. -->
<string name="permdesc_flashlight">Allows the app to control the flashlight.</string>
View
20 data/etc/com.stericsson.hardware.fm.receiver.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<!-- This is the standard feature indicating that the device includes FM receiver. -->
+<permissions>
+ <feature name="com.stericsson.hardware.fm.receiver" />
+</permissions>
View
20 data/etc/com.stericsson.hardware.fm.transmitter.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<!-- This is the standard feature indicating that the device includes FM transmitter. -->
+<permissions>
+ <feature name="com.stericsson.hardware.fm.transmitter" />
+</permissions>
View
203 fmradio/include/android_fmradio.h
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Copyright (C) 2010 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.
+ *
+ * Author: johan.xj.palmaeus@stericsson.com for ST-Ericsson
+ */
+
+/*
+ * Internal stuff for android_fmradio(_Receiver/_Transmitter).cpp
+ */
+
+#ifndef ANDROID_FMRADIO_H
+#define ANDROID_FMRADIO_H
+
+#include "jni.h"
+#include "fmradio.h"
+
+enum FmRadioState_t {
+ FMRADIO_STATE_IDLE,
+ FMRADIO_STATE_STARTING,
+ FMRADIO_STATE_STARTED,
+ FMRADIO_STATE_PAUSED,
+ FMRADIO_STATE_SCANNING,
+ FMRADIO_STATE_EXTRA_COMMAND,
+ /* sum up */
+ FMRADIO_NUMBER_OF_STATES
+};
+
+enum FmRadioCommand_t {
+ FMRADIO_EVENT_START,
+ FMRADIO_EVENT_START_ASYNC,
+ FMRADIO_EVENT_PAUSE,
+ FMRADIO_EVENT_RESUME,
+ FMRADIO_EVENT_RESET,
+ FMRADIO_EVENT_GET_FREQUENCY,
+ FMRADIO_EVENT_SET_FREQUENCY,
+ FMRADIO_EVENT_SET_PARAMETER,
+ FMRADIO_EVENT_STOP_SCAN,
+ FMRADIO_EVENT_EXTRA_COMMAND,
+ /* RX Only */
+ FMRADIO_EVENT_GET_PARAMETER,
+ FMRADIO_EVENT_GET_SIGNAL_STRENGTH,
+ FMRADIO_EVENT_SCAN,
+ FMRADIO_EVENT_FULL_SCAN,
+ /* TX Only */
+ FMRADIO_EVENT_BLOCK_SCAN,
+ /* sum up */
+ FMRADIO_NUMBER_OF_EVENTS
+};
+
+enum RadioMode_t {
+ FMRADIO_RX,
+ FMRADIO_TX
+};
+
+typedef bool ValidEventsForStates_t[FMRADIO_NUMBER_OF_EVENTS]
+ [FMRADIO_NUMBER_OF_STATES];
+
+struct FmRadioCallbacks_t {
+ void (*onStateChanged) (int, int);
+ void (*onError) (void);
+ void (*onStarted) (void);
+ void (*onScan) (int, int, int, bool); /* RX only */
+ void (*onFullScan) (int, int *, int *, bool); /* RX only */
+ void (*onBlockScan) (int, int *, int *, bool); /* TX only */
+ void (*onForcedReset) (enum fmradio_reset_reason_t reason);
+ void (*onSendExtraCommand) (char*, struct fmradio_extra_command_ret_item_t *);
+};
+
+struct bundle_descriptor_offsets_t {
+ jclass mClass;
+ jmethodID mConstructor;
+ jmethodID mGetInt;
+ jmethodID mGetIntArray;
+ jmethodID mGetShort;
+ jmethodID mGetShortArray;
+ jmethodID mGetString;
+ jmethodID mContainsKey;
+ jmethodID mSize;
+ jmethodID mKeySet;
+ jmethodID mPutInt;
+ jmethodID mPutShort;
+ jmethodID mPutIntArray;
+ jmethodID mPutShortArray;
+ jmethodID mPutString;
+};
+
+struct FmSession_t {
+ // vendor specific data, we do not know about this type
+ void *vendorData_p;
+ void *fmLibrary_p;
+ bool isRegistered;
+ enum FmRadioState_t state;
+ struct fmradio_vendor_methods_t *vendorMethods_p;
+ const ValidEventsForStates_t *validEventsForStates_p;
+ const struct FmRadioCallbacks_t *callbacks_p;
+ JavaVM *jvm_p;
+ jobject jobj;
+ struct FmSession_t *partnerSession_p;
+ struct bundle_descriptor_offsets_t *bundleOffsets_p;
+ enum FmRadioState_t oldState; /* used when scanning */
+ bool lastScanAborted; /* used when scanning */
+ bool pendingPause; /* used when scanning & asyncStarting */
+ bool ongoingReset; /* used during reset while waiting */
+ pthread_mutex_t *dataMutex_p; /* data access to this struct */
+ pthread_cond_t sync_cond;
+ struct ThreadCtrl_t *signalStrengthThreadCtrl_p; /* RX Only */
+};
+
+#define FMRADIO_SET_STATE(_session_p,_newState) {int _oldState = (_session_p)->state; (_session_p)->state = _newState;(_session_p)->callbacks_p->onStateChanged(_oldState, _newState);}
+
+/* exceptions */
+
+#define THROW_ILLEGAL_ARGUMENT(_session_p) \
+ androidFmRadioThrowException(_session_p,\
+ "java/lang/IllegalArgumentException",\
+ "Illegal argument", __FILE__, __LINE__,\
+ __FUNCTION__)
+#define THROW_UNSUPPORTED_OPERATION(_session_p) \
+ androidFmRadioThrowException(_session_p,\
+ "java/lang/UnsupportedOperationException",\
+ "Unsupported operation", __FILE__, __LINE__,\
+ __FUNCTION__)
+#define THROW_INVALID_STATE(_session_p) \
+ androidFmRadioThrowException(_session_p,\
+ "java/lang/IllegalStateException",\
+ "State is invalid", __FILE__, __LINE__,\
+ __FUNCTION__)
+#define THROW_IO_ERROR(_session_p) \
+ androidFmRadioThrowException(_session_p,\
+ "java/io/IOException",\
+ "IO Exception", __FILE__, __LINE__,\
+ __FUNCTION__)
+
+
+#define FM_LIBRARY_NAME_MAX_LENGTH 128
+
+#define THREAD_WAIT_TIMEOUT_S 2
+
+#define SIGNAL_STRENGTH_MAX 1000
+#define SIGNAL_STRENGTH_UNKNOWN -1
+
+extern pthread_mutex_t rx_tx_common_mutex;
+
+jobject extraCommandRetList2Bundle(JNIEnv * env_p, struct bundle_descriptor_offsets_t
+ *bundleOffsets_p,
+ struct fmradio_extra_command_ret_item_t *itemList);
+
+void freeExtraCommandRetList(struct extra_command_ret_item_t *itemList);
+
+void androidFmRadioTempResumeIfPaused(struct FmSession_t *session_p);
+
+void androidFmRadioPauseIfTempResumed(struct FmSession_t *session_p);
+
+bool androidFmRadioIsValidEventForState(struct FmSession_t *session_p,
+ enum FmRadioCommand_t event);
+
+void androidFmRadioThrowException(struct FmSession_t *session_p,
+ const char *exception,
+ const char *message, const char *file,
+ int line, const char *function);
+
+bool androidFmRadioLoadFmLibrary(struct FmSession_t *session_p,
+ enum RadioMode_t mode);
+
+void androidFmRadioUnLoadFmLibrary(struct FmSession_t *session_p);
+
+int
+androidFmRadioStart(struct FmSession_t *session_p, enum RadioMode_t mode,
+ const struct fmradio_vendor_callbacks_t *callbacks,
+ bool async, int lowFreq, int highFreq, int defaultFreq,
+ int grid);
+
+int androidFmRadioPause(struct FmSession_t *session_p);
+
+int androidFmRadioResume(struct FmSession_t *session_p);
+
+int androidFmRadioReset(struct FmSession_t *session_p);
+
+void androidFmRadioSetFrequency(struct FmSession_t *session_p,
+ int frequency);
+
+int androidFmRadioGetFrequency(struct FmSession_t *session_p);
+
+void androidFmRadioStopScan(struct FmSession_t *session_p);
+
+void
+androidFmRadioSendExtraCommand(struct FmSession_t *session_p, JNIEnv * env,
+ jstring command, jobjectArray parameter);
+
+#endif
View
22 fmradio/java/com/stericsson/hardware/fm/FmBand.aidl
@@ -0,0 +1,22 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Copyright (C) 2010 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.
+ *
+ * Author: Markus Grape (markus.grape@stericsson.com) for ST-Ericsson
+ */
+
+package com.stericsson.hardware.fm;
+
+parcelable FmBand;
View
258 fmradio/java/com/stericsson/hardware/fm/FmBand.java
@@ -0,0 +1,258 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Copyright (C) 2010 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.
+ *
+ * Author: Bjorn Pileryd (bjorn.pileryd@sonyericsson.com)
+ * Author: Markus Grape (markus.grape@stericsson.com) for ST-Ericsson
+ */
+
+package com.stericsson.hardware.fm;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Describes the properties of the FM frequency band. The frequency band range
+ * and the channel offset vary in different regions. The unit for all
+ * frequencies in this class is kHz.
+ */
+public class FmBand implements Parcelable {
+
+ /**
+ * Default band for US 87.9MHz - 107.9MHz, 200kHz channel offset.
+ */
+ public static final int BAND_US = 0;
+
+ /**
+ * Default band for EU 87.5MHz - 108MHz, 100kHz channel offset.
+ */
+ public static final int BAND_EU = 1;
+
+ /**
+ * Default band for Japan 76MHz - 90MHz, 100kHz channel offset.
+ */
+ public static final int BAND_JAPAN = 2;
+
+ /**
+ * Default band for China 70MHz - 108MHz, 50kHz channel offset.
+ */
+ public static final int BAND_CHINA = 3;
+
+ /**
+ * Default band for EU 87.5MHz - 108MHz, 50kHz channel offset.
+ */
+ public static final int BAND_EU_50K_OFFSET = 4;
+
+ /**
+ * Unknown frequency.
+ */
+ public static final int FM_FREQUENCY_UNKNOWN = -1;
+
+ /**
+ * The lowest frequency of the band.
+ */
+ private int mMinFrequency;
+
+ /**
+ * The highest frequency of the band.
+ */
+ private int mMaxFrequency;
+
+ /**
+ * The default frequency of the band.
+ */
+ private int mDefaultFrequency;
+
+ /**
+ * The offset between the channels in the band.
+ */
+ private int mChannelOffset;
+
+ /**
+ * Creates a band representation.
+ *
+ * @param minFrequency
+ * the lowest frequency of the band in kHz
+ * @param maxFrequency
+ * the highest frequency of the band in kHz
+ * @param channelOffset
+ * the offset between the channels in the band in kHz
+ * @param defaultFrequency
+ * the default frequency that the hardware will tune to at
+ * startup
+ * @throws IllegalArgumentException
+ * if the minFrequency is equal or higher then maxFrequency
+ * @throws IllegalArgumentException
+ * if the defaultFrequency is not within the limits of
+ * minFrequency and maxFrequency
+ * @throws IllegalArgumentException
+ * if the minFrequency or maxFrequency is not a multiplier of channelOffset
+ */
+ public FmBand(int minFrequency, int maxFrequency, int channelOffset, int defaultFrequency) {
+ if (minFrequency >= maxFrequency) {
+ throw new IllegalArgumentException(
+ "Minimum frequency can not be equal or higher than maximum frequency");
+ }
+ if (defaultFrequency < minFrequency) {
+ throw new IllegalArgumentException(
+ "Default frequency can not be less than minFrequency");
+ }
+ if (defaultFrequency > maxFrequency) {
+ throw new IllegalArgumentException(
+ "Default frequency can not be higher than maxFrequency");
+ }
+ if ((maxFrequency - minFrequency) % channelOffset != 0
+ || (defaultFrequency - minFrequency) % channelOffset != 0) {
+ throw new IllegalArgumentException(
+ "Frequency has invalid offset");
+ }
+ this.mMinFrequency = minFrequency;
+ this.mMaxFrequency = maxFrequency;
+ this.mDefaultFrequency = defaultFrequency;
+ this.mChannelOffset = channelOffset;
+ }
+
+ /**
+ * Creates a standard band representation. The default frequency will be the
+ * lowest frequency for the specified band.
+ *
+ * @param band
+ * one of {@link #BAND_US}, {@link #BAND_EU}, {@link #BAND_JAPAN}
+ * , {@link #BAND_CHINA}, {@link #BAND_EU_50K_OFFSET}
+ * @throws IllegalArgumentException
+ * if the band is not one of {@link #BAND_US}, {@link #BAND_EU},
+ * {@link #BAND_JAPAN}, {@link #BAND_CHINA}, {@link #BAND_EU_50K_OFFSET}
+ */
+ public FmBand(int band) {
+ switch (band) {
+ case BAND_US:
+ this.mMinFrequency = 87900;
+ this.mMaxFrequency = 107900;
+ this.mDefaultFrequency = 87900;
+ this.mChannelOffset = 200;
+ break;
+ case BAND_EU:
+ this.mMinFrequency = 87500;
+ this.mMaxFrequency = 108000;
+ this.mDefaultFrequency = 87500;
+ this.mChannelOffset = 100;
+ break;
+ case BAND_JAPAN:
+ this.mMinFrequency = 76000;
+ this.mMaxFrequency = 90000;
+ this.mDefaultFrequency = 76000;
+ this.mChannelOffset = 100;
+ break;
+ case BAND_CHINA:
+ this.mMinFrequency = 70000;
+ this.mMaxFrequency = 108000;
+ this.mDefaultFrequency = 70000;
+ this.mChannelOffset = 50;
+ break;
+ case BAND_EU_50K_OFFSET:
+ this.mMinFrequency = 87500;
+ this.mMaxFrequency = 108000;
+ this.mDefaultFrequency = 87500;
+ this.mChannelOffset = 50;
+ break;
+ default:
+ throw new IllegalArgumentException("Wrong band identifier");
+ }
+ }
+
+ /**
+ * Checks if a frequency is valid to the band. To be valid it must be within
+ * the frequency range and on a frequency with correct channel offset.
+ *
+ * @param frequency
+ * the frequency to validate
+ * @return true if the frequency is valid for this band
+ */
+ public boolean isFrequencyValid(int frequency) {
+ if (frequency < mMinFrequency || frequency > mMaxFrequency) {
+ return false;
+ }
+ if ((frequency - mMinFrequency) % mChannelOffset != 0) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Return the lowest frequency of the band.
+ *
+ * @return the lowest frequency of the band in kHz
+ */
+ public int getMinFrequency() {
+ return mMinFrequency;
+ }
+
+ /**
+ * Returns the highest frequency of the band.
+ *
+ * @return the highest frequency of the band in kHz
+ */
+ public int getMaxFrequency() {
+ return mMaxFrequency;
+ }
+
+ /**
+ * Returns the default frequency of the band that the hardware will tune to
+ * at startup.
+ *
+ * @return the default frequency of the band in kHz
+ */
+ public int getDefaultFrequency() {
+ return mDefaultFrequency;
+ }
+
+ /**
+ * Returns the offset between the channels in the band.
+ *
+ * @return the offset between the channels in the band in kHz
+ */
+ public int getChannelOffset() {
+ return mChannelOffset;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public int describeContents() {
+ return 0;
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public void writeToParcel(Parcel dest, int flags) {
+ dest.writeInt(mMinFrequency);
+ dest.writeInt(mMaxFrequency);
+ dest.writeInt(mChannelOffset);
+ dest.writeInt(mDefaultFrequency);
+ }
+
+ /** Implement the Parcelable interface {@hide} */
+ public static final Creator<FmBand> CREATOR = new Creator<FmBand>() {
+ public FmBand createFromParcel(Parcel in) {
+ int minfreq = in.readInt();
+ int maxfreq = in.readInt();
+ int offset = in.readInt();
+ int defaultFreq = in.readInt();
+ FmBand band = new FmBand(minfreq, maxfreq, offset, defaultFreq);
+ return band;
+ }
+
+ public FmBand[] newArray(int size) {
+ return new FmBand[size];
+ }
+ };
+}
View
1,327 fmradio/java/com/stericsson/hardware/fm/FmReceiver.java
@@ -0,0 +1,1327 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Copyright (C) 2010 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.
+ *
+ * Author: Bjorn Pileryd (bjorn.pileryd@sonyericsson.com)
+ * Author: Markus Grape (markus.grape@stericsson.com) for ST-Ericsson
+ */
+
+package com.stericsson.hardware.fm;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+
+import java.io.IOException;
+
+/**
+ * The FmReceiver controls reception of FM radio. This API enables an
+ * application to tune/scan for channels, receive RDS data, etc. The unit for
+ * all frequencies in this class is kHz. Note that this API only controls the
+ * reception of FM radio, to play FM radio the MediaPlayer interfaces should be
+ * used, see code example below the state diagram.
+ * <p>
+ * Get an instance of this class by calling
+ * {@link android.content.Context#getSystemService(String)
+ * Context.getSystemService("fm_receiver")}.
+ * </p>
+ * <a name="StateDiagram"></a> <h3>State Diagram</h3>
+ * <p>
+ * The state machine is designed to take into account that some hardware may
+ * need time to prepare, and that it is likely to consume more power when paused
+ * and started than it does in the idle state. The hardware implementation of
+ * this interface should do the time consuming preparation procedures in the
+ * starting state. The switching between paused and started states should be
+ * fast to give a good user experience.
+ * </p>
+ * <p>
+ * <img src="../../../../images/FmReceiver_states.gif"
+ * alt="FmReceiver State diagram" border="0" />
+ * </p>
+ * <table border="1">
+ * <tr>
+ * <th>Method Name</th>
+ * <th>Valid States</th>
+ * <th>Invalid States</th>
+ * <th>Comments</th>
+ * </tr>
+ * <tr>
+ * <td>{@link #startAsync(FmBand)}</td>
+ * <td>{idle}</td>
+ * <td>{starting, paused, started, scanning}</td>
+ * <td>Successful invocation of this method in a valid state transfers the
+ * object to the starting state. Calling this method in an invalid state throws
+ * an IllegalStateException.</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #start(FmBand)}</td>
+ * <td>{idle}</td>
+ * <td>{starting, paused, started, scanning}</td>
+ * <td>Successful invocation of this method in a valid state transfers the
+ * object to the started state. Calling this method in an invalid state throws
+ * an IllegalStateException.</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #resume()}</td>
+ * <td>{paused, started}</td>
+ * <td>{idle, starting, scanning}</td>
+ * <td>Successful invocation of this method in a valid state transfers the
+ * object to the started state. Calling this method in an invalid state throws
+ * an IllegalStateException.</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #pause()}</td>
+ * <td>{started, paused}</td>
+ * <td>{idle, starting, scanning}</td>
+ * <td>Successful invocation of this method in a valid state transfers the
+ * object to the paused state. Calling this method in an invalid state throws an
+ * IllegalStateException.</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #reset()}</td>
+ * <td>any</td>
+ * <td>{}</td>
+ * <td>Successful invocation of this method transfers the object to the idle
+ * state, the object is like being just created.</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #getState()}</td>
+ * <td>any</td>
+ * <td>{}</td>
+ * <td>This method can be called in any state and calling it does not change the
+ * object state.</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #isApiSupported(Context)}</td>
+ * <td>any</td>
+ * <td>{}</td>
+ * <td>This method can be called in any state and calling it does not change the
+ * object state.</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #isRDSDataSupported()}</td>
+ * <td>any</td>
+ * <td>{}</td>
+ * <td>This method can be called in any state and calling it does not change the
+ * object state.</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #isTunedToValidChannel()}</td>
+ * <td>any</td>
+ * <td>{}</td>
+ * <td>This method can be called in any state and calling it does not change the
+ * object state.</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #setThreshold(int)}</td>
+ * <td>{started, paused, scanning}</td>
+ * <td>{idle, starting}</td>
+ * <td>Calling this method in an invalid state throws an IllegalStateException.
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>{@link #getThreshold()}</td>
+ * <td>{started, paused, scanning}</td>
+ * <td>{idle, starting}</td>
+ * <td>Calling this method in an invalid state throws an IllegalStateException.
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>{@link #getFrequency()}</td>
+ * <td>{paused, started}</td>
+ * <td>{idle, starting, scanning}</td>
+ * <td>Successful invocation of this method in a valid state does not change the
+ * object state. Calling this method in an invalid state throws an
+ * IllegalStateException.</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #getSignalStrength()}</td>
+ * <td>any</td>
+ * <td>{}</td>
+ * <td>This method can be called in any state and calling it does not change the
+ * object state.</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #isPlayingInStereo()}</td>
+ * <td>any</td>
+ * <td>{}</td>
+ * <td>This method can be called in any state and calling it does not change the
+ * object state.</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #setForceMono(boolean)}</td>
+ * <td>{started, paused, scanning}</td>
+ * <td>{idle, starting}</td>
+ * <td>Calling this method in an invalid state throws an IllegalStateException.
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>{@link #setAutomaticAFSwitching(boolean)}</td>
+ * <td>{started, paused, scanning}</td>
+ * <td>{idle, starting}</td>
+ * <td>Calling this method in an invalid state throws an IllegalStateException.
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>{@link #setAutomaticTASwitching(boolean)}</td>
+ * <td>{started, paused, scanning}</td>
+ * <td>{idle, starting}</td>
+ * <td>Calling this method in an invalid state throws an IllegalStateException.
+ * </td>
+ * </tr>
+ * <tr>
+ * <td>{@link #setFrequency(int)}</td>
+ * <td>{started, paused}</td>
+ * <td>{idle, starting, scanning}</td>
+ * <td>Successful invocation of this method in a valid state does not change the
+ * object state. Calling this method in an invalid state throws an
+ * IllegalStateException.</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #startFullScan()}</td>
+ * <td>{started, paused}</td>
+ * <td>{idle, starting, scanning}</td>
+ * <td>Successful invocation of this method in a valid state transfers the
+ * object to the scanning state. Calling this method in an invalid state throws
+ * an IllegalStateException.</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #scanUp()}</td>
+ * <td>{started, paused}</td>
+ * <td>{idle, starting, scanning}</td>
+ * <td>Successful invocation of this method in a valid state transfers the
+ * object to the scanning state. Calling this method in an invalid state throws
+ * an IllegalStateException.</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #scanDown()}</td>
+ * <td>{started, paused}</td>
+ * <td>{idle, starting, scanning}</td>
+ * <td>Successful invocation of this method in a valid state transfers the
+ * object to the scanning state. Calling this method in an invalid state throws
+ * an IllegalStateException.</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #stopScan()}</td>
+ * <td>any</td>
+ * <td>{}</td>
+ * <td>Successful invocation of this method in a valid state tries to stop
+ * performing a scan operation. The hardware might continue the scan for an
+ * unspecified amount of time after this method is called. Once the scan has
+ * stopped, it will be notified via {@link OnScanListener}</td>
+ * </tr>
+ * <tr>
+ * <td>{@link #sendExtraCommand(String, String[])}</td>
+ * <td>vendor specific</td>
+ * <td>vendor specific</td>
+ * <td>vendor specific</td>
+ * </tr>
+ * </table>
+ * <a name="Examples"></a> <h3>Example code</h3>
+ * <pre>
+ * // start receiving FM radio
+ * FmReceiver fmr = (FmReceiver) getSystemService("fm_receiver");
+ * fmr.start(new FmBand(FmBand.BAND_EU));
+ *
+ * // prepare and start playback
+ * MediaPlayer mp = new MediaPlayer();
+ * mp.setDataSource(&quot;fmradio://rx&quot;);
+ * mp.prepare();
+ * mp.start();
+ * </pre>
+ * <a name="FMHandling"></a> <h3>FM receiving/transmission handling</h3>
+ * <p>
+ * In this API, FM radio cannot be received and transmitted at the same time,
+ * therefore the state machine is designed to prevent incorrect usage. The
+ * FmReceiver and FmTransmitter has a separate state machine and only one can be
+ * <i>active</i> (state other than idle).
+ * <ul>
+ * <li>If start is called on FmReceiver and the FmTransmitter is <i>active</i>,
+ * the FmTransmitter MUST release resources and change state to idle.</li>
+ * <li>The FmTransmitter will in that case be notified by
+ * {@link com.stericsson.hardware.fm.FmTransmitter.OnForcedResetListener#onForcedReset(int)}.</li>
+ * </ul>
+ * </p>
+ * <a name="RDSHandling"></a> <h3>Receiving/transmitting RDS data</h3>
+ * <p>
+ * RDS data can be received by setting the
+ * {@link #addOnRDSDataFoundListener(OnRDSDataFoundListener)}. When RDS data is
+ * available the data can be extracted from the Bundle object in
+ * {@link OnRDSDataFoundListener#onRDSDataFound(Bundle, int)} according to the
+ * table below. This table can also be used when transmitting RDS data with the
+ * FmTransmitter.
+ * </p>
+ * <table border="1">
+ * <tr>
+ * <th>RDS description</th>
+ * <th>key name</th>
+ * <th>value type</th>
+ * <th>value description</th>
+ * </tr>
+ * <tr>
+ * <td>Program Identification code</td>
+ * <td>PI</td>
+ * <td>short</td>
+ * <td>N/A</td>
+ * </tr>
+ * <tr>
+ * <td>Traffic Program Identification code</td>
+ * <td>TP</td>
+ * <td>short</td>
+ * <td>1 bit</td>
+ * </tr>
+ * <tr>
+ * <td>Program Type code</td>
+ * <td>PTY</td>
+ * <td>short</td>
+ * <td>5 bits</td>
+ * </tr>
+ * <tr>
+ * <td>Traffic Announcement code</td>
+ * <td>TA</td>
+ * <td>short</td>
+ * <td>1 bit</td>
+ * </tr>
+ * <tr>
+ * <td>Music/Speech switch code</td>
+ * <td>M/S</td>
+ * <td>short</td>
+ * <td>1 bit</td>
+ * </tr>
+ * <tr>
+ * <td>Alternative Frequency</td>
+ * <td>AF</td>
+ * <td>int[]</td>
+ * <td>kHz</td>
+ * </tr>
+ * <tr>
+ * <td>Program service name</td>
+ * <td>PSN</td>
+ * <td>string</td>
+ * <td>8 chars</td>
+ * </tr>
+ * <tr>
+ * <td>Radio text</td>
+ * <td>RT</td>
+ * <td>string</td>
+ * <td>64 chars</td>
+ * </tr>
+ * <tr>
+ * <td>Clock-time and date</td>
+ * <td>CT</td>
+ * <td>string</td>
+ * <td>Yr:mo:dy:hr:min</td>
+ * </tr>
+ * <tr>
+ * <td>Program Type name</td>
+ * <td>PTYN</td>
+ * <td>string</td>
+ * <td>8 chars</td>
+ * </tr>
+ * <tr>
+ * <td>Traffic Message Channel</td>
+ * <td>TMC</td>
+ * <td>short[]</td>
+ * <td>X:Y:Z -> 5+16+16 bits</td>
+ * </tr>
+ * <tr>
+ * <td>TA Frequency</td>
+ * <td>TAF</td>
+ * <td>int</td>
+ * <td>kHz</td>
+ * </tr>
+ * </table>
+ * <p>
+ * The RDS specification can be found <a
+ * href="http://www.rds.org.uk/rds98/pdf/IEC%2062106-E_no%20print.pdf">here</a>
+ * </p>
+ * <a name="ErrorHandling"></a> <h3>Error handling</h3>
+ * <p>
+ * In general, it is up to the application that uses this API to keep track of
+ * events that could affect the FM radio user experience. The hardware
+ * implementation side of this API should only take actions when it is really
+ * necessary, e.g. if the hardware is forced to pause or reset, and notify the
+ * application by using the {@link OnForcedPauseListener},
+ * {@link OnForcedResetListener} or {@link OnErrorListener}.
+ * </p>
+ */
+public abstract class FmReceiver {
+
+ /**
+ * The FmReceiver had to be shut down due to a non-critical error, meaning
+ * that it is OK to attempt a restart immediately after this. For example,
+ * if the hardware was shut down in order to save power after being in the
+ * paused state for too long.
+ */
+ public static final int RESET_NON_CRITICAL = 0;
+
+ /**
+ * The FmReceiver had to be shut down due to a critical error. The FM
+ * hardware it not guaranteed to work as expected after receiving this
+ * error.
+ */
+ public static final int RESET_CRITICAL = 1;
+
+ /**
+ * The FmTransmitter was activated and therefore the FmReceiver must be put
+ * in idle.
+ *
+ * @see FmTransmitter#startAsync(FmBand)
+ */
+ public static final int RESET_TX_IN_USE = 2;
+
+ /**
+ * The radio is not allowed to be used, typically when flight mode is
+ * enabled.
+ */
+ public static final int RESET_RADIO_FORBIDDEN = 3;
+
+ /**
+ * Indicates that the FmReceiver is in an idle state. No resources are
+ * allocated and power consumption is kept to a minimum.
+ */
+ public static final int STATE_IDLE = 0;
+
+ /**
+ * Indicates that the FmReceiver is allocating resources and preparing to
+ * receive FM radio.
+ */
+ public static final int STATE_STARTING = 1;
+
+ /**
+ * Indicates that the FmReceiver is receiving FM radio. Note that the
+ * FmReceiver is considered to be started even if it is receiving noise or
+ * gets a signal with not good enough quality to consider a valid channel.
+ */
+ public static final int STATE_STARTED = 2;
+
+ /**
+ * Indicates that the FmReceiver has allocated resources and is ready to
+ * instantly receive FM radio.
+ */
+ public static final int STATE_PAUSED = 3;
+
+ /**
+ * Indicates that the FmReceiver is scanning. FM radio will not be received
+ * in this state.
+ */
+ public static final int STATE_SCANNING = 4;
+
+ /**
+ * Unknown signal strength.
+ */
+ public static final int SIGNAL_STRENGTH_UNKNOWN = -1;
+
+ /**
+ * The frequency switch occurred as a stronger alternate frequency was
+ * found.
+ */
+ public static final int SWITCH_AF = 0;
+
+ /**
+ * The frequency switch occurred as there is a traffic announcement
+ * in progress.
+ */
+ public static final int SWITCH_TA = 1;
+
+ /**
+ * The frequency switch occurred at the cessation of a traffic
+ * announcement.
+ */
+ public static final int SWITCH_TA_END = 2;
+
+ /**
+ * Scan direction down towards lower frequencies.
+ */
+ public static final int SCAN_DOWN = 0;
+
+ /**
+ * Scan direction up towards higher frequencies.
+ */
+ public static final int SCAN_UP = 1;
+
+ /**
+ * Returns true if the FM receiver API is supported by the system.
+ */
+ public static boolean isApiSupported(Context context) {
+ return context.getPackageManager().hasSystemFeature(
+ PackageManager.FEATURE_RADIO_FM_RECEIVER);
+ }
+
+ /**
+ * Starts reception of the FM hardware. This is an asynchronous method since
+ * different hardware can have varying startup times. When the reception is
+ * started a callback to {@link OnStartedListener#onStarted()} is made.
+ * <p>
+ * When calling this method, an FmBand parameter must be passed that
+ * describes the properties of the band that the FmReceiver should prepare
+ * for. If the band is null, invalid or not supported, an exception will be
+ * thrown.
+ * </p>
+ * <p>
+ * If the FmTransmitter is active it will be forced to reset. See
+ * {@link FmTransmitter#RESET_RX_IN_USE}.
+ * </p>
+ *
+ * @param band
+ * the band to use
+ * @throws IllegalArgumentException
+ * if the band is null
+ * @throws UnsupportedOperationException
+ * if the band is not supported by the hardware
+ * @throws IllegalStateException
+ * if it is called in an invalid state
+ * @throws IOException
+ * if the FM hardware failed to start
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ * @see FmBand
+ */
+ public abstract void startAsync(FmBand band) throws IOException;
+
+ /**
+ * Starts reception of the FM hardware. This is a synchronous method and the
+ * method call will block until the hardware is started.
+ * <p>
+ * When calling this method, an FmBand parameter must be passed that
+ * describes the properties of the band that the FmReceiver should prepare
+ * for. If the band is null, invalid or not supported, an exception will be
+ * thrown.
+ * </p>
+ * <p>
+ * If the FmTransmitter is active it will be forced to reset. See
+ * {@link FmTransmitter#RESET_RX_IN_USE}.
+ * </p>
+ *
+ * @param band
+ * the band to use
+ * @throws IllegalArgumentException
+ * if the band is null
+ * @throws UnsupportedOperationException
+ * if the band is not supported by the hardware
+ * @throws IllegalStateException
+ * if it is called in an invalid state
+ * @throws IOException
+ * if the FM hardware failed to start
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ * @see FmBand
+ */
+ public abstract void start(FmBand band) throws IOException;
+
+ /**
+ * Resumes FM reception.
+ * <p>
+ * Calling this method when the FmReceiver is in started state has no
+ * affect.
+ * </p>
+ *
+ * @throws IllegalStateException
+ * if it is called in an invalid state
+ * @throws IOException
+ * if the FM hardware failed to resume
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void resume() throws IOException;
+
+ /**
+ * Pauses FM reception. No FM radio is received as long as the FmReceiver is
+ * paused. Call {@link #resume()} to resume reception. The hardware should
+ * be able to resume reception quickly from the paused state to give a good
+ * user experience.
+ * <p>
+ * Note that the hardware provider may choose to turn off the hardware after
+ * being paused a certain amount of time to save power. This will be
+ * reported in {@link OnForcedResetListener#onForcedReset(int)} with reason
+ * {@link #RESET_NON_CRITICAL} and the FmReceiver will be set to the idle
+ * state.
+ * </p>
+ * <p>
+ * Calling this method when the FmReceiver is in paused state has no affect.
+ * </p>
+ *
+ * @throws IllegalStateException
+ * if it is called in an invalid state
+ * @throws IOException
+ * if the FM hardware failed to pause
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void pause() throws IOException;
+
+ /**
+ * Resets the FmReceiver to its idle state.
+ *
+ * @throws IOException
+ * if the FM hardware failed to reset
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void reset() throws IOException;
+
+ /**
+ * Returns the state of the FmReceiver.
+ *
+ * @return One of {@link #STATE_IDLE}, {@link #STATE_STARTING},
+ * {@link #STATE_STARTED}, {@link #STATE_PAUSED},
+ * {@link #STATE_SCANNING}
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract int getState();
+
+ /**
+ * Returns true if the hardware/implementation supports RDS data. If true
+ * the {@link OnRDSDataFoundListener} will work. If not it will never report
+ * any data.
+ * <p>
+ * The motivation for having this function is that an application can take
+ * this capability into account when laying out its UI.
+ * </p>
+ *
+ * @return true if RDS data is supported by the FmReceiver, false otherwise
+ *
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract boolean isRDSDataSupported();
+
+ /**
+ * Checks if the tuned frequency is considered to contain a channel.
+ *
+ * @return true if the FmReceiver is tuned to a valid channel
+ *
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract boolean isTunedToValidChannel();
+
+ /**
+ * Sets the threshold for the tuner. The threshold can be 0-1000. A low
+ * threshold indicates that the tuner will find stations with a weak signal
+ * and a high threshold will find stations with a strong signal.
+ * <p>
+ * This is used then calling {@link FmReceiver#scanUp()},
+ * {@link FmReceiver#scanDown()} or {@link FmReceiver#startFullScan()}.
+ * </p>
+ *
+ * @param threshold
+ * a value between 0-1000
+ * @throws IllegalArgumentException
+ * if the value is not between 0-1000
+ * @throws IllegalStateException
+ * if it is called in an invalid state
+ * @throws IOException
+ * if the FM hardware failed to set threshold
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void setThreshold(int threshold) throws IOException;
+
+ /**
+ * Returns the threshold for the tuner.
+ *
+ * @return the threshold for the tuner
+ *
+ * @throws IllegalStateException
+ * if it is called in an invalid state
+ * @throws IOException
+ * if the FM hardware failed to get the threshold
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract int getThreshold() throws IOException;
+
+ /**
+ * Returns the tuned frequency.
+ *
+ * @return the tuned frequency in kHz
+ *
+ * @throws IllegalStateException
+ * if it is called in an invalid state
+ * @throws IOException
+ * if the FM hardware failed to get the frequency
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract int getFrequency() throws IOException;
+
+ /**
+ * Returns the signal strength of the tuned frequency. The signal strength
+ * is a value from 0 to 1000. A high value indicates a strong signal and a
+ * low value indicates a weak signal.
+ *
+ * @return the signal strength or {@link #SIGNAL_STRENGTH_UNKNOWN}
+ *
+ * @throws IOException
+ * if the FM hardware failed to get the signal strength
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract int getSignalStrength() throws IOException;
+
+ /**
+ * Checks if the tuned frequency is played in stereo. If
+ * {@link #setForceMono(boolean)} is set, this method will always return
+ * false.
+ *
+ * @return true if the tuned frequency is playing in stereo
+ *
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract boolean isPlayingInStereo();
+
+ /**
+ * Force the playback to always be in mono.
+ *
+ * @param forceMono
+ * if true, the hardware will only output mono audio. If false,
+ * stereo is allowed if supported by hardware and signal.
+ * @throws IllegalStateException
+ * if it is called in an invalid state
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void setForceMono(boolean forceMono);
+
+ /**
+ * Sets the automatic switching of the FmReceiver in the case of a stronger
+ * transmitter with the same Programme Identification (PI) presence. The
+ * application should register for callbacks using
+ * {@link #addOnAutomaticSwitchListener(OnAutomaticSwitchListener)}
+ * to receive a callback when channels are found. The reason stated in
+ * the callback will be {@link FmReceiver#SWITCH_AF}.
+ *
+ * @param automatic
+ * enable or disable automatic switching
+ * @throws IllegalStateException
+ * if it is called in an invalid state
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void setAutomaticAFSwitching(boolean automatic);
+
+ /**
+ * Sets the automatic switching of the program in case of the presence of
+ * traffic announcement in another program. The application should register
+ * for callbacks using {@link #addOnAutomaticSwitchListener(OnAutomaticSwitchListener)}
+ * to receive a callback when channels are found. The reason stated in
+ * the callback will be {@link FmReceiver#SWITCH_TA} when switching to
+ * traffic announcement and {@link FmReceiver#SWITCH_TA_END} when switching
+ * back after the announcement.
+ *
+ * @param automatic
+ * enable or disable automatic switching
+ * @throws IllegalStateException
+ * if it is called in an invalid state
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void setAutomaticTASwitching(boolean automatic);
+
+ /**
+ * Sets the frequency. Unlike {@link #scanUp()} and {@link #scanDown()},
+ * this method will directly jump to the specified frequency instead of
+ * trying to find a channel while scanning.
+ * <p>
+ * The frequency must be within the band that the FmReceiver prepared for.
+ * </p>
+ *
+ * @param frequency
+ * the frequency to tune to in kHz
+ * @throws IllegalArgumentException
+ * if the frequency is not supported
+ * @throws IllegalStateException
+ * if it is called in an invalid state
+ * @throws IOException
+ * if the FM hardware failed to set frequency
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ * @see FmBand
+ */
+ public abstract void setFrequency(int frequency) throws IOException;
+
+ /**
+ * Starts a full scan. The tuner will scan the entire frequency band for
+ * channels. The application should register for callbacks using
+ * {@link #addOnScanListener(OnScanListener)} to receive a callback when
+ * channels are found.
+ * <p>
+ * If the application wants to stop the full scan, a call to
+ * {@link #stopScan()} should be made.
+ * </p>
+ *
+ * @throws IllegalStateException
+ * if it is called in an invalid state
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void startFullScan();
+
+ /**
+ * Starts seeking for a channel downwards in the frequency band from the
+ * currently tuned frequency. When a channel with enough signal strength is
+ * found the scanning will stop.
+ * <p>
+ * The seek will always stop if it reaches back to the frequency it started
+ * from, meaning that in the worst case scenario, when no channel can be
+ * found, the seek will run through one full cycle of the frequency band
+ * and stop at the frequency it started from.
+ * </p>
+ * The application should register for callbacks using
+ * {@link #addOnScanListener(OnScanListener)} to receive a callback when the
+ * scan is complete.
+ * <p>
+ * If the application wants to stop the scan, a call to {@link #stopScan()}
+ * should be made.
+ * </p>
+ *
+ * @throws IllegalStateException
+ * if it is called in an invalid state
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ * @see FmReceiver#scanUp()
+ */
+ public abstract void scanDown();
+
+ /**
+ * Same as {@link #scanDown()} but seeks upwards in the frequency band.
+ *
+ * @throws IllegalStateException
+ * if it is called in an invalid state
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ * @see FmReceiver#scanDown()
+ */
+ public abstract void scanUp();
+
+ /**
+ * Stops performing a scan operation. The hardware might continue the scan
+ * for an unspecified amount of time after this method is called. Once the
+ * scan has stopped, it will be notified via {@link OnScanListener}.
+ * <p>
+ * Note that this method has no affect if called in other states than the
+ * scanning state.
+ * </p>
+ *
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void stopScan();
+
+ /**
+ * This method can be used to send vendor specific commands. These commands
+ * must not follow any common design for all vendors, and information about
+ * the commands that a vendor implements is out of scope in this API.
+ * <p>
+ * However, one command must be supported by all vendors that implements
+ * vendor specific commands, the <i>vendor_information</i> command. In the
+ * Bundle parameter in
+ * {@link OnExtraCommandListener#onExtraCommand(String, Bundle)} the FM
+ * radio device name and version can be extracted according to the table
+ * below.
+ * </p>
+ * <table border="1">
+ * <tr>
+ * <th>key name</th>
+ * <th>value type</th>
+ * </tr>
+ * <tr>
+ * <td>device_name</td>
+ * <td>string</td>
+ * </tr>
+ * <tr>
+ * <td>device_version</td>
+ * <td>string</td>
+ * </tr>
+ * </table>
+ *
+ * @param command
+ * the command to send
+ * @param extras
+ * extra parameters to the command
+ * @return true if the command was accepted, otherwise false
+ *
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract boolean sendExtraCommand(String command, String[] extras);
+
+ /**
+ * Register a callback to be invoked when the FmReceiver is started.
+ *
+ * @param listener
+ * the callback that will be run
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void addOnStartedListener(OnStartedListener listener);
+
+ /**
+ * Unregister a callback to be invoked when the FmReceiver is started.
+ *
+ * @param listener
+ * the callback to remove
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void removeOnStartedListener(OnStartedListener listener);
+
+ /**
+ * Register a callback to be invoked during a scan.
+ *
+ * @param listener
+ * the callback that will be run
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void addOnScanListener(OnScanListener listener);
+
+ /**
+ * Unregister a callback to be invoked during a scan.
+ *
+ * @param listener
+ * the callback to remove
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void removeOnScanListener(OnScanListener listener);
+
+ /**
+ * Register a callback to be invoked when RDS data is found. Having a
+ * listener registered for this might cause continuous callbacks, so it is
+ * considered good practice to set this listener to null whenever the
+ * application is not interested in these updates, e.g. when the application
+ * UI is not visible.
+ *
+ * @param listener
+ * the callback that will be run
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void addOnRDSDataFoundListener(OnRDSDataFoundListener listener);
+
+ /**
+ * Unregister a callback to be invoked when RDS data is found.
+ *
+ * @param listener
+ * the callback to remove
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void removeOnRDSDataFoundListener(OnRDSDataFoundListener listener);
+
+ /**
+ * Register a callback to be invoked when an error has happened during an
+ * asynchronous operation.
+ *
+ * @param listener
+ * the callback that will be run
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void addOnErrorListener(OnErrorListener listener);
+
+ /**
+ * Unregister a callback to be invoked when an error has happened during an
+ * asynchronous operation.
+ *
+ * @param listener
+ * the callback to remove
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void removeOnErrorListener(OnErrorListener listener);
+
+ /**
+ * Register a callback to be invoked when the signal strength of the
+ * currently tuned frequency changes. Having a listener registered to this
+ * method may cause frequent callbacks, hence it is good practice to only
+ * have a listener registered for this when necessary.
+ * <p>
+ * Example: If the application uses this information to visualize the signal
+ * strength on the UI, it should unregister the listener whenever the UI is
+ * not visible.
+ * </p>
+ * <p>
+ * The listener will only receive callbacks when the signal strength
+ * changes.
+ * </p>
+ *
+ * @param listener
+ * the callback that will be run
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void addOnSignalStrengthChangedListener(OnSignalStrengthChangedListener listener);
+
+ /**
+ * Unregister a callback to be invoked when the signal strength of the
+ * currently tuned frequency changes.
+ *
+ * @param listener
+ * the callback to remove
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void removeOnSignalStrengthChangedListener(OnSignalStrengthChangedListener listener);
+
+ /**
+ * Register a callback to be invoked when playback of the tuned frequency
+ * changes between mono and stereo. Having a listener registered to this
+ * method may cause frequent callbacks, hence it is good practice to only
+ * have a listener registered for this when necessary.
+ *
+ * @param listener
+ * the callback that will be run
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void addOnPlayingInStereoListener(OnPlayingInStereoListener listener);
+
+ /**
+ * Unregister a callback to be invoked when playback of the tuned frequency
+ * changes between mono and stereo.
+ *
+ * @param listener
+ * the callback to remove
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void removeOnPlayingInStereoListener(OnPlayingInStereoListener listener);
+
+ /**
+ * Register a callback to be invoked when the FmReceiver is forced to pause
+ * due to external reasons.
+ *
+ * @param listener
+ * the callback that will be run
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void addOnForcedPauseListener(OnForcedPauseListener listener);
+
+ /**
+ * Unregister a callback to be invoked when the FmReceiver is forced to
+ * pause due to external reasons.
+ *
+ * @param listener
+ * the callback to remove
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void removeOnForcedPauseListener(OnForcedPauseListener listener);
+
+ /**
+ * Register a callback to be invoked when the FmReceiver is forced to reset
+ * due to external reasons.
+ *
+ * @param listener
+ * the callback that will be run
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void addOnForcedResetListener(OnForcedResetListener listener);
+
+ /**
+ * Unregister a callback to be invoked when the FmReceiver is forced to
+ * reset due to external reasons.
+ *
+ * @param listener
+ * the callback to remove
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void removeOnForcedResetListener(OnForcedResetListener listener);
+
+ /**
+ * Register a callback to be invoked when the FmReceiver changes state.
+ * Having a listener registered to this method may cause frequent callbacks,
+ * hence it is good practice to only have a listener registered for this
+ * when necessary.
+ *
+ * @param listener
+ * the callback that will be run
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void addOnStateChangedListener(OnStateChangedListener listener);
+
+ /**
+ * Unregister a callback to be invoked when the FmReceiver changes state.
+ *
+ * @param listener
+ * the callback to remove
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void removeOnStateChangedListener(OnStateChangedListener listener);
+
+ /**
+ * Register a callback to be invoked when the FmReceiver want's to invoke a
+ * vendor specific callback.
+ *
+ * @param listener
+ * the callback that will be run
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void addOnExtraCommandListener(OnExtraCommandListener listener);
+
+ /**
+ * Unregister a callback to be invoked when the FmReceiver want's to invoke
+ * a vendor specific callback.
+ *
+ * @param listener
+ * the callback to remove
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void removeOnExtraCommandListener(OnExtraCommandListener listener);
+
+ /**
+ * Register a callback to be invoked when the FmReceiver has triggered
+ * a changed frequency.
+ *
+ * @param listener
+ * the callback that will be run
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void addOnAutomaticSwitchListener(OnAutomaticSwitchListener listener);
+
+ /**
+ * Unregister a callback to be invoked when the FmReceiver has triggered
+ * a changed frequency.
+ *
+ * @param listener
+ * the callback to remove
+ * @throws SecurityException
+ * if the FM_RADIO_RECEIVER permission is not present
+ */
+ public abstract void removeOnAutomaticSwitchListener(OnAutomaticSwitchListener listener);
+
+ /**
+ * Interface definition of a callback to be invoked when the FmReceiver is
+ * started.
+ */
+ public interface OnStartedListener {
+ /**
+ * Called when the FmReceiver is started. The FmReceiver is now
+ * receiving FM radio.
+ */
+ void onStarted();
+ }
+
+ /**
+ * Interface definition of a callback to be invoked when a scan operation is
+ * complete.
+ */
+ public interface OnScanListener {
+ /**
+ * Called when the full scan is completed.
+ * <p>
+ * If the full scan is aborted with stopScan, this will be indicated
+ * with the aborted argument.
+ * <p>
+ * If an error occurs during a full scan, it will be reported via
+ * {@link OnErrorListener#onError()} and this method callback will not
+ * be invoked.
+ * </p>
+ *
+ * @param frequency
+ * the frequency in kHz where the channel was found
+ * @param signalStrength
+ * the signal strength, 0-1000
+ * @param aborted
+ * true if the full scan was aborted, false otherwise
+ */
+ void onFullScan(int[] frequency, int[] signalStrength, boolean aborted);
+
+ /**
+ * Called when {@link FmReceiver#scanDown()} or
+ * {@link FmReceiver#scanUp()} has successfully completed a scan
+ * operation. Note that failing to find a channel during a scan
+ * operation does not mean that it is an error, and it will still result
+ * in a call to this interface.
+ * <p>
+ * If the scan is aborted with stopScan, this will be indicated with the
+ * aborted argument.
+ * <p>
+ *
+ * @param tunedFrequency
+ * the current frequency in kHz of the tuner after the scan
+ * operation was completed
+ * @param signalStrength
+ * the signal strength, 0-1000
+ * @param scanDirection
+ * direction of scan, SCAN_DOWN or SCAN_UP
+ * @param aborted
+ * true if the scan was aborted, false otherwise
+ */
+ void onScan(int tunedFrequency, int signalStrength, int scanDirection, boolean aborted);
+ }
+
+ /**
+ * Interface definition of a callback to be invoked when RDS data has been
+ * found. Note that there is not necessarily a relation between the
+ * frequency that the RDS data is found at and the currently tuned
+ * frequency.
+ */
+ public interface OnRDSDataFoundListener {
+ /**
+ * Called when RDS data has been found or updated.
+ *
+ * @param rdsData
+ * the RDS data that was found
+ * @param frequency
+ * the frequency where the RDS data was found
+ */
+ void onRDSDataFound(Bundle rdsData, int frequency);
+ };
+
+ /**
+ * Interface definition of a callback to be invoked when there has been an
+ * error during an asynchronous operation.
+ */
+ public interface OnErrorListener {
+ /**
+ * Called to indicate an error.
+ */
+ void onError();
+ }
+
+ /**
+ * Interface definition of a callback to be invoked when the signal strength
+ * of the currently tuned frequency changes.
+ */
+ public interface OnSignalStrengthChangedListener {
+ /**
+ * Called to indicate that the signal strength has changed.
+ *
+ * @param signalStrength
+ * the signal strength, 0-1000
+ */
+ void onSignalStrengthChanged(int signalStrength);
+ }
+
+ /**
+ * Interface definition of a callback to be invoked when playback of the
+ * tuned frequency changes between mono and stereo. This is useful if the
+ * application wants to display some icon that shows if playing in stereo or
+ * not.
+ */
+ public interface OnPlayingInStereoListener {
+ /**
+ * Called when switching between mono and stereo.
+ *
+ * @param inStereo
+ * true if playback is in stereo, false if in mono
+ */
+ void onPlayingInStereo(boolean inStereo);
+ }
+
+ /**
+ * Interface definition of a callback to be invoked when the FmReceiver was
+ * forced to pause due to external reasons.
+ */
+ public interface OnForcedPauseListener {
+ /**
+ * Called when an external reason caused the FmReceiver to pause. When
+ * this callback is received, the FmReceiver is still able to resume
+ * reception by calling {@link FmReceiver#resume()}.
+ */
+ void onForcedPause();
+ }
+
+ /**
+ * Interface definition of a callback to be invoked when the FmReceiver was
+ * forced to reset due to external reasons.
+ */
+ public interface OnForcedResetListener {
+ /**
+ * Called when an external reason caused the FmReceiver to reset. The
+ * application that uses the FmReceiver should take action according to
+ * the reason for resetting.
+ *
+ * @param reason
+ * reason why the FmReceiver reset:
+ * <ul>
+ * <li>{@link FmReceiver#RESET_NON_CRITICAL}
+ * <li>{@link FmReceiver#RESET_CRITICAL}
+ * <li>{@link FmReceiver#RESET_TX_IN_USE}
+ * <li>{@link FmReceiver#RESET_RADIO_FORBIDDEN}
+ * </ul>
+ */
+ void onForcedReset(int reason);
+ }
+
+ /**
+ * Interface definition of a callback to be invoked when the FmReceiver
+ * changes state.
+ */
+ public interface OnStateChangedListener {
+ /**
+ * Called when the state is changed in the FmReceiver. This is useful if
+ * an application want's to monitor the FmReceiver state.
+ *
+ * @param oldState
+ * the old state of the FmReceiver
+ * @param newState
+ * the new state of the FmReceiver
+ */
+ void onStateChanged(int oldState, int newState);
+ }
+
+ /**
+ * Interface definition of a callback to be invoked when the FmReceiver
+ * responds to a vendor specific command.
+ */
+ public interface OnExtraCommandListener {
+ /**
+ * Called when the FmReceiver responds to a vendor specific command.
+ *
+ * @param response
+ * the command the FmReceiver responds to
+ * @param extras
+ * extra parameters to the command
+ */
+ void onExtraCommand(String response, Bundle extras);
+ }
+
+ /**
+ * Interface definition of a callback to be invoked when the FmReceiver
+ * changes frequency either due to AF switch or TA event.
+ */
+ public interface OnAutomaticSwitchListener {
+ /**
+ * Called when the FmReceiver changes frequency either due to AF
+ * switch or TA event.
+ *
+ * @param newFrequency
+ * the frequency switched to
+ * @param reason
+ * the reason for the switch:
+ * <ul>
+ * <li>{@link FmReceiver#SWITCH_AF}
+ * <li>{@link FmReceiver#SWITCH_TA}
+ * <li>{@link FmReceiver#SWITCH_TA_END}
+ * </ul>
+ */
+ void onAutomaticSwitch(int newFrequency, int reason);
+ }
+}
View
1,126 fmradio/java/com/stericsson/hardware/fm/FmReceiverImpl.java
@@ -0,0 +1,1126 @@
+/*
+ * Copyright (C) ST-Ericsson SA 2010
+ * Copyright (C) 2010 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.
+ *
+ * Author: Bjorn Pileryd (bjorn.pileryd@sonyericsson.com)
+ * Author: Markus Grape (markus.grape@stericsson.com) for ST-Ericsson
+ * Author: Andreas Gustafsson (andreas.a.gustafsson@stericsson.com) for ST-Ericsson
+ */
+
+package com.stericsson.hardware.fm;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.HashMap;
+
+/**
+ * The implementation of the FmReceiver.
+ *
+ * @hide
+ */
+public class FmReceiverImpl extends FmReceiver {
+
+ private static final String TAG = "FmReceiver";
+
+ private IFmReceiver mService;
+
+ /**
+ * Save the FmBand used to be able to validate frequencies.
+ */
+ private FmBand mBand;
+
+ /**
+ * Map from OnStateChanged to their associated ListenerTransport objects.
+ */
+ private HashMap<OnStateChangedListener, OnStateChangedListenerTransport> mOnStateChanged =
+ new HashMap<OnStateChangedListener, OnStateChangedListenerTransport>();
+
+ /**
+ * Map from OnStarted to their associated ListenerTransport objects.
+ */
+ private HashMap<OnStartedListener, OnStartedListenerTransport> mOnStarted =
+ new HashMap<OnStartedListener, OnStartedListenerTransport>();
+
+ /**
+ * Map from OnError to their associated ListenerTransport objects.
+ */
+ private HashMap<OnErrorListener, OnErrorListenerTransport> mOnError =
+ new HashMap<OnErrorListener, OnErrorListenerTransport>();
+
+ /**
+ * Map from OnScan to their associated ListenerTransport objects.
+ */
+ private HashMap<OnScanListener, OnScanListenerTransport> mOnScan =
+ new HashMap<OnScanListener, OnScanListenerTransport>();
+
+ /**
+ * Map from OnForcedPause to their associated ListenerTransport objects.
+ */
+ private HashMap<OnForcedPauseListener, OnForcedPauseListenerTransport> mOnForcedPause =
+ new HashMap<OnForcedPauseListener, OnForcedPauseListenerTransport>();
+
+ /**
+ * Map from OnForcedReset to their associated ListenerTransport objects.
+ */
+ private HashMap<OnForcedResetListener, OnForcedResetListenerTransport> mOnForcedReset =
+ new HashMap<OnForcedResetListener, OnForcedResetListenerTransport>();
+
+ /**
+ * Map from OnRDSDataFound to their associated ListenerTransport objects.
+ */
+ private HashMap<OnRDSDataFoundListener, OnRDSDataListenerTransport> mOnRDSData =
+ new HashMap<OnRDSDataFoundListener, OnRDSDataListenerTransport>();
+
+ /**
+ * Map from OnSignalStrength to their associated ListenerTransport objects.
+ */
+ private HashMap<OnSignalStrengthChangedListener, OnSignalStrengthListenerTransport> mOnSignalStrength =
+ new HashMap<OnSignalStrengthChangedListener, OnSignalStrengthListenerTransport>();
+
+ /**
+ * Map from OnStereo to their associated ListenerTransport objects.
+ */
+ private HashMap<OnPlayingInStereoListener, OnStereoListenerTransport> mOnStereo =
+ new HashMap<OnPlayingInStereoListener, OnStereoListenerTransport>();
+
+ /**
+ * Map from OnExtraCommand to their associated ListenerTransport objects.
+ */
+ private HashMap<OnExtraCommandListener, OnExtraCommandListenerTransport> mOnExtraCommand =
+ new HashMap<OnExtraCommandListener, OnExtraCommandListenerTransport>();
+
+ /**
+ * Map from OnAutomaticSwitch to their associated ListenerTransport objects.
+ */
+ private HashMap<OnAutomaticSwitchListener, OnAutomaticSwitchListenerTransport> mOnAutomaticSwitch =
+ new HashMap<OnAutomaticSwitchListener, OnAutomaticSwitchListenerTransport>();
+
+ private static class OnStateChangedListenerTransport extends IOnStateChangedListener.Stub {
+ private static final int TYPE_ON_STATE_CHANGED = 1;
+
+ private OnStateChangedListener mListener;
+ private final Handler mListenerHandler;
+
+ OnStateChangedListenerTransport(OnStateChangedListener listener) {
+ mListener = listener;
+
+ mListenerHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ _handleMessage(msg);
+ }
+ };
+ }
+
+
+ public void onStateChanged(int oldState, int newState) {
+ Message msg = Message.obtain();
+ msg.what = TYPE_ON_STATE_CHANGED;
+ Bundle b = new Bundle();
+ b.putInt("oldState", oldState);
+ b.putInt("newState", newState);
+ msg.obj = b;
+ mListenerHandler.sendMessage(msg);
+ }
+
+ private void _handleMessage(Message msg) {
+ switch (msg.what) {
+ case TYPE_ON_STATE_CHANGED:
+ Bundle b = (Bundle) msg.obj;
+ int oldState = b.getInt("oldState");
+ int newState = b.getInt("newState");
+ mListener.onStateChanged(oldState, newState);
+ break;
+ }
+ }
+ }
+
+ private static class OnStartedListenerTransport extends IOnStartedListener.Stub {
+ private static final int TYPE_ON_STARTED = 1;
+
+ private OnStartedListener mListener;
+ private final Handler mListenerHandler;
+
+ OnStartedListenerTransport(OnStartedListener listener) {
+ mListener = listener;
+
+ mListenerHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ _handleMessage(msg);
+ }
+ };
+ }
+
+ public void onStarted() {
+ Message msg = Message.obtain();
+ msg.what = TYPE_ON_STARTED;
+ mListenerHandler.sendMessage(msg);
+ }
+
+ private void _handleMessage(Message msg) {
+ switch (msg.what) {
+ case TYPE_ON_STARTED:
+ mListener.onStarted();
+ break;
+ }
+ }
+ }
+
+ private static class OnErrorListenerTransport extends IOnErrorListener.Stub {
+ private static final int TYPE_ON_ERROR = 1;
+
+ private OnErrorListener mListener;
+ private final Handler mListenerHandler;
+
+ OnErrorListenerTransport(OnErrorListener listener) {
+ mListener = listener;
+
+ mListenerHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ _handleMessage(msg);
+ }
+ };
+ }
+
+ public void onError() {
+ Message msg = Message.obtain();
+ msg.what = TYPE_ON_ERROR;
+ mListenerHandler.sendMessage(msg);
+ }
+
+ private void _handleMessage(Message msg) {
+ switch (msg.what) {
+ case TYPE_ON_ERROR:
+ mListener.onError();
+ break;
+ }
+ }
+ }
+
+ private static class OnScanListenerTransport extends IOnScanListener.Stub {
+ private static final int TYPE_ON_SCAN = 1;
+ private static final int TYPE_ON_FULLSCAN = 2;
+
+ private OnScanListener mListener;
+ private final Handler mListenerHandler;
+
+ OnScanListenerTransport(OnScanListener listener) {
+ mListener = listener;
+
+ mListenerHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ _handleMessage(msg);
+ }
+ };
+ }
+
+ public void onScan(int tunedFrequency, int signalStrength,
+ int scanDirection, boolean aborted) {
+ Message msg = Message.obtain();
+ msg.what = TYPE_ON_SCAN;
+ Bundle b = new Bundle();
+ b.putInt("tunedFrequency", tunedFrequency);
+ b.putInt("signalStrength", signalStrength);
+ b.putInt("scanDirection", scanDirection);
+ b.putBoolean("aborted", aborted);
+ msg.obj = b;
+ mListenerHandler.sendMessage(msg);
+ }
+
+ public void onFullScan(int[] frequency, int[] signalStrength, boolean aborted) {
+ Message msg = Message.obtain();
+ msg.what = TYPE_ON_FULLSCAN;
+ Bundle b = new Bundle();
+ b.putIntArray("frequency", frequency);
+ b.putIntArray("signalStrength", signalStrength);
+ b.putBoolean("aborted", aborted);
+ msg.obj = b;
+ mListenerHandler.sendMessage(msg);
+ }
+
+ private void _handleMessage(Message msg) {
+ Bundle b;
+ boolean aborted;
+
+ switch (msg.what) {
+ case TYPE_ON_SCAN:
+ b = (Bundle) msg.obj;
+ int tunedFrequency = b.getInt("tunedFrequency");
+ int signalStrength = b.getInt("signalStrength");
+ int scanDirection = b.getInt("scanDirection");
+ aborted = b.getBoolean("aborted");
+ mListener.onScan(tunedFrequency, signalStrength, scanDirection, aborted);
+ break;
+ case TYPE_ON_FULLSCAN:
+ b = (Bundle) msg.obj;
+ int[] frequency = b.getIntArray("frequency");
+ int[] signalStrengths = b.getIntArray("signalStrength");
+ aborted = b.getBoolean("aborted");
+ mListener.onFullScan(frequency, signalStrengths, aborted);
+ break;
+ }
+ }
+ }
+
+ private static class OnForcedPauseListenerTransport extends IOnForcedPauseListener.Stub {
+ private static final int TYPE_ON_FORCEDPAUSE = 1;
+
+ private OnForcedPauseListener mListener;
+ private final Handler mListenerHandler;
+
+ OnForcedPauseListenerTransport(OnForcedPauseListener listener) {
+ mListener = listener;
+
+ mListenerHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ _handleMessage(msg);
+ }
+ };
+ }
+
+ public void onForcedPause() {
+ Message msg = Message.obtain();
+ msg.what = TYPE_ON_FORCEDPAUSE;
+ Bundle b = new Bundle();
+ // Need more here? Or remove?
+ msg.obj = b;
+ mListenerHandler.sendMessage(msg);
+ }
+
+ private void _handleMessage(Message msg) {
+ switch (msg.what) {
+ case TYPE_ON_FORCEDPAUSE:
+ Bundle b = (Bundle) msg.obj;
+ mListener.onForcedPause();
+ break;
+ }
+ }
+ }
+
+ private static class OnForcedResetListenerTransport extends IOnForcedResetListener.Stub {
+ private static final int TYPE_ON_FORCEDRESET = 1;
+
+ private OnForcedResetListener mListener;
+ private final Handler mListenerHandler;
+
+ OnForcedResetListenerTransport(OnForcedResetListener listener) {
+ mListener = listener;
+
+ mListenerHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ _handleMessage(msg);
+ }
+ };
+ }
+
+ public void onForcedReset(int reason) {
+ Message msg = Message.obtain();
+ msg.what = TYPE_ON_FORCEDRESET;
+ Bundle b = new Bundle();
+ b.putInt("reason", reason);
+ msg.obj = b;
+ mListenerHandler.sendMessage(msg);
+ }
+
+ private void _handleMessage(Message msg) {
+ switch (msg.what) {
+ case TYPE_ON_FORCEDRESET:
+ Bundle b = (Bundle) msg.obj;
+ int reason = b.getInt("reason");
+ mListener.onForcedReset(reason);
+ break;
+ }
+ }
+ }
+
+ private static class OnRDSDataListenerTransport extends IOnRDSDataFoundListener.Stub {
+ private static final int TYPE_ON_RDS_DATA = 1;
+
+ private OnRDSDataFoundListener mListener;
+ private final Handler mListenerHandler;
+
+ OnRDSDataListenerTransport(OnRDSDataFoundListener listener) {
+ mListener = listener;
+
+ mListenerHandler = new Handler() {
+ @Override
+ public