Skip to content

Instantly share code, notes, and snippets.

@ngoquang2708
Created October 4, 2016 16:27
Show Gist options
  • Save ngoquang2708/ed5e4b71dae1db2b381a6675e1e46539 to your computer and use it in GitHub Desktop.
Save ngoquang2708/ed5e4b71dae1db2b381a6675e1e46539 to your computer and use it in GitHub Desktop.
diff --git a/Android.mk b/Android.mk
index 430edcd..e5f31ee 100755
--- a/Android.mk
+++ b/Android.mk
@@ -30,6 +30,13 @@ LOCAL_MULTILIB := 32
LOCAL_PROGUARD_ENABLED := disabled
+ifeq ($(strip $(BOARD_HAVE_FMRADIO_BCM)),true)
+LOCAL_SRC_FILES += \
+ $(call all-java-files-under, fm/app/src)
+LOCAL_STATIC_JAVA_LIBRARIES += com.broadcom.fm
+LOCAL_FULL_MANIFEST_FILE := $(LOCAL_PATH)/fm/app/AndroidManifest.xml
+endif
+
include $(BUILD_PACKAGE)
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/fm/Android.mk b/fm/Android.mk
new file mode 100644
index 0000000..12db2f7
--- /dev/null
+++ b/fm/Android.mk
@@ -0,0 +1,19 @@
+# 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.
+
+LOCAL_PATH := $(call my-dir)
+
+ifeq ($(TARGET_BUILD_APPS),)
+include $(call all-makefiles-under, $(LOCAL_PATH))
+endif
diff --git a/fm/app/AndroidManifest.xml b/fm/app/AndroidManifest.xml
new file mode 100644
index 0000000..0e9d6b0
--- /dev/null
+++ b/fm/app/AndroidManifest.xml
@@ -0,0 +1,376 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.bluetooth"
+ android:sharedUserId="android.uid.bluetooth">
+
+ <original-package android:name="com.android.bluetooth" />
+
+ <!-- Allows access to the Bluetooth Share Manager -->
+ <permission android:name="android.permission.ACCESS_BLUETOOTH_SHARE"
+ android:label="@string/permlab_bluetoothShareManager"
+ android:description="@string/permdesc_bluetoothShareManager"
+ android:protectionLevel="signature" />
+
+ <permission android:name="android.permission.ACCESS_BLUETOOTH_AVRCP_CT_DATA"
+ android:label="@string/permlab_bluetoothAvrcpDataManager"
+ android:description="@string/permdesc_bluetoothAvrcpDataManager"
+ android:protectionLevel="signature" />
+
+ <!-- Allows temporarily whitelisting Bluetooth addresses for sharing -->
+ <permission android:name="com.android.permission.WHITELIST_BLUETOOTH_DEVICE"
+ android:label="@string/permlab_bluetoothWhitelist"
+ android:description="@string/permdesc_bluetoothWhitelist"
+ android:protectionLevel="signature" />
+
+ <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
+ <uses-permission android:name="android.permission.ACCESS_BLUETOOTH_SHARE" />
+ <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
+ <uses-permission android:name="android.permission.ACCESS_BLUETOOTH_AVRCP_CT_DATA" />
+ <uses-permission android:name="android.permission.INTERNET" />
+ <uses-permission android:name="android.permission.BLUETOOTH" />
+ <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
+ <uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" />
+ <uses-permission android:name="android.permission.BLUETOOTH_MAP" />
+ <uses-permission android:name="android.permission.WAKE_LOCK" />
+ <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+ <uses-permission android:name="android.permission.READ_CONTACTS" />
+ <!-- WRITE_CONTACTS is used for test cases only -->
+ <uses-permission android:name="android.permission.WRITE_CONTACTS" />
+ <uses-permission android:name="android.permission.READ_CALL_LOG" />
+ <uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
+ <uses-permission android:name="android.permission.WRITE_SETTINGS" />
+ <uses-permission android:name="android.permission.NFC_HANDOVER_STATUS" />
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+ <uses-permission android:name="android.permission.WRITE_APN_SETTINGS" />
+ <uses-permission android:name="android.permission.NET_ADMIN" />
+ <uses-permission android:name="android.permission.CALL_PRIVILEGED" />
+ <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+ <uses-permission android:name="android.permission.NET_TUNNELING" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.CHANGE_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.CONNECTIVITY_INTERNAL" />
+ <uses-permission android:name="android.permission.MODIFY_PHONE_STATE" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+ <uses-permission android:name="android.permission.BLUETOOTH_STACK" />
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS"/>
+ <uses-permission android:name="android.permission.MANAGE_USERS"/>
+ <uses-permission android:name="com.google.android.gallery3d.permission.GALLERY_PROVIDER"/>
+ <uses-permission android:name="com.android.gallery3d.permission.GALLERY_PROVIDER"/>
+ <uses-permission android:name="android.permission.RECEIVE_SMS" />
+ <uses-permission android:name="android.permission.SEND_SMS" />
+ <uses-permission android:name="android.permission.READ_SMS" />
+ <uses-permission android:name="android.permission.WRITE_SMS" />
+ <uses-permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
+ <uses-permission android:name="android.permission.MANAGE_DOCUMENTS" />
+ <uses-permission android:name="android.permission.UPDATE_APP_OPS_STATS" />
+ <uses-permission android:name="android.permission.VIBRATE" />
+ <uses-permission android:name="android.permission.DEVICE_POWER" />
+ <uses-permission android:name="android.permission.REAL_GET_TASKS" />
+ <uses-permission android:name="android.permission.MODIFY_AUDIO_ROUTING" />
+ <uses-permission android:name="com.android.email.permission.ACCESS_PROVIDER"/>
+ <uses-permission android:name="com.android.email.permission.READ_ATTACHMENT"/>
+
+ <!-- For PBAP Owner Vcard Info -->
+ <uses-permission android:name="android.permission.READ_PROFILE"/>
+ <application
+ android:name=".btservice.AdapterApp"
+ android:icon="@mipmap/bt_share"
+ android:persistent="false"
+ android:label="@string/app_name"
+ android:supportsRtl="true"
+ android:usesCleartextTraffic="false">
+ <uses-library android:name="javax.obex" />
+ <provider android:name=".opp.BluetoothOppProvider"
+ android:authorities="com.android.bluetooth.opp"
+ android:exported="true"
+ android:process="@string/process">
+ <path-permission
+ android:pathPrefix="/btopp"
+ android:permission="android.permission.ACCESS_BLUETOOTH_SHARE" />
+ </provider>
+ <provider android:name=".avrcp.BluetoothAvrcpDataProvider"
+ android:authorities="com.android.bluetooth.avrcp"
+ android:exported="true"
+ android:process="@string/process">
+ <path-permission
+ android:pathPrefix="/btavrcp_ct"
+ android:permission="android.permission.ACCESS_BLUETOOTH_AVRCP_CT_DATA" />
+ </provider>
+ <provider android:name="android.support.v4.content.FileProvider"
+ android:authorities="com.google.android.bluetooth.fileprovider"
+ android:grantUriPermissions="true"
+ android:exported="false">
+ <meta-data
+ android:name="android.support.FILE_PROVIDER_PATHS"
+ android:resource="@xml/file_paths" />
+ </provider>
+ <service
+ android:process="@string/process"
+ android:name = ".btservice.AdapterService">
+ <intent-filter>
+ <action android:name="android.bluetooth.IBluetooth" />
+ </intent-filter>
+ </service>
+ <service
+ android:process="@string/process"
+ android:name=".opp.BluetoothOppService"
+ android:permission="android.permission.ACCESS_BLUETOOTH_SHARE"
+ android:enabled="@bool/profile_supported_opp"/>
+ <receiver
+ android:process="@string/process"
+ android:exported="true"
+ android:name=".opp.BluetoothOppReceiver"
+ android:enabled="@bool/profile_supported_opp">
+ <intent-filter>
+ <action android:name="android.bluetooth.adapter.action.STATE_CHANGED" />
+ <!--action android:name="android.intent.action.BOOT_COMPLETED" /-->
+ <action android:name="android.btopp.intent.action.OPEN_RECEIVED_FILES" />
+ </intent-filter>
+ </receiver>
+ <receiver
+ android:process="@string/process"
+ android:name=".opp.BluetoothOppHandoverReceiver"
+ android:permission="com.android.permission.WHITELIST_BLUETOOTH_DEVICE">
+ <intent-filter>
+ <action android:name="android.btopp.intent.action.WHITELIST_DEVICE" />
+ <action android:name="android.btopp.intent.action.STOP_HANDOVER_TRANSFER" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.nfc.handover.intent.action.HANDOVER_SEND" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:mimeType="*/*" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.nfc.handover.intent.action.HANDOVER_SEND_MULTIPLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:mimeType="*/*" />
+ </intent-filter>
+ </receiver>
+ <activity android:name=".opp.BluetoothOppLauncherActivity"
+ android:process="@string/process"
+ android:theme="@android:style/Theme.Material.Light.Dialog"
+ android:label="@string/bt_share_picker_label"
+ android:enabled="@bool/profile_supported_opp">
+ <intent-filter>
+ <action android:name="android.intent.action.SEND" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:mimeType="image/*" />
+ <data android:mimeType="video/*" />
+ <data android:mimeType="audio/*" />
+ <data android:mimeType="text/x-vcard" />
+ <data android:mimeType="text/plain" />
+ <data android:mimeType="text/html" />
+ <data android:mimeType="text/xml" />
+ <data android:mimeType="application/zip" />
+ <data android:mimeType="application/vnd.ms-excel" />
+ <data android:mimeType="application/msword" />
+ <data android:mimeType="application/vnd.android.package-archive" />
+ <data android:mimeType="application/vnd.ms-powerpoint" />
+ <data android:mimeType="application/pdf" />
+ <data android:mimeType="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" />
+ <data android:mimeType="application/vnd.openxmlformats-officedocument.wordprocessingml.document" />
+ <data android:mimeType="application/vnd.openxmlformats-officedocument.presentationml.presentation" />
+ <data android:mimeType="application/x-hwp" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.intent.action.SEND_MULTIPLE" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:mimeType="image/*" />
+ <data android:mimeType="video/*" />
+ <data android:mimeType="audio/*" />
+ <data android:mimeType="x-mixmedia/*" />
+ <data android:mimeType="text/x-vcard" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="android.btopp.intent.action.OPEN" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:mimeType="vnd.android.cursor.item/vnd.android.btopp" />
+ </intent-filter>
+ </activity>
+ <activity android:name=".opp.BluetoothOppBtEnableActivity"
+ android:process="@string/process"
+ android:excludeFromRecents="true"
+ android:theme="@android:style/Theme.Material.Light.Dialog.Alert"
+ android:enabled="@bool/profile_supported_opp">
+ </activity>
+ <activity android:name=".opp.BluetoothOppBtErrorActivity"
+ android:process="@string/process"
+ android:excludeFromRecents="true"
+ android:theme="@android:style/Theme.Material.Light.Dialog.Alert">
+ </activity>
+ <activity android:name=".opp.BluetoothOppBtEnablingActivity"
+ android:process="@string/process"
+ android:excludeFromRecents="true"
+ android:theme="@android:style/Theme.Material.Light.Dialog.Alert"
+ android:enabled="@bool/profile_supported_opp">
+ </activity>
+ <activity android:name=".opp.BluetoothOppIncomingFileConfirmActivity"
+ android:process="@string/process"
+ android:excludeFromRecents="true"
+ android:theme="@android:style/Theme.Material.Light.Dialog.Alert"
+ android:enabled="@bool/profile_supported_opp">
+ </activity>
+ <activity android:name=".opp.BluetoothOppTransferActivity"
+ android:process="@string/process"
+ android:excludeFromRecents="true"
+ android:theme="@android:style/Theme.Material.Light.Dialog.Alert"
+ android:enabled="@bool/profile_supported_opp">
+ </activity>
+ <activity android:name=".opp.BluetoothOppTransferHistory"
+ android:process="@string/process"
+ android:label=""
+ android:excludeFromRecents="true"
+ android:configChanges="orientation|keyboardHidden|screenSize"
+ android:enabled="@bool/profile_supported_opp">
+ </activity>
+ <activity android:name=".pbap.BluetoothPbapActivity"
+ android:process="@string/process"
+ android:excludeFromRecents="true"
+ android:theme="@android:style/Theme.Material.Light.Dialog.Alert"
+ android:enabled="@bool/profile_supported_pbap">
+ <intent-filter>
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ <service
+ android:process="@string/process"
+ android:name=".pbap.BluetoothPbapService"
+ android:enabled="@bool/profile_supported_pbap" >
+ <intent-filter>
+ <action android:name="android.bluetooth.IBluetoothPbap" />
+ </intent-filter>
+ </service>
+ <receiver
+ android:process="@string/process"
+ android:exported="true"
+ android:name=".pbap.BluetoothPbapReceiver"
+ android:enabled="@bool/profile_supported_pbap">
+ <intent-filter>
+ <action android:name="android.bluetooth.adapter.action.STATE_CHANGED"/>
+ <action android:name="android.bluetooth.device.action.CONNECTION_ACCESS_REPLY" />
+ <action android:name="android.bluetooth.device.action.ACL_DISCONNECTED" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </receiver>
+ <service
+ android:process="@string/process"
+ android:name=".map.BluetoothMapService"
+ android:enabled="@bool/profile_supported_map" >
+ <intent-filter>
+ <action android:name="android.bluetooth.IBluetoothMap" />
+ <action android:name="android.btmap.intent.action.SHOW_MAPS_SETTINGS" />
+ <action android:name="com.android.bluetooth.map.USER_CONFIRM_TIMEOUT"/>
+ </intent-filter>
+ </service>
+ <activity android:name=".map.BluetoothMapSettings"
+ android:process="@string/process"
+ android:label="@string/bluetooth_map_settings_title"
+ android:excludeFromRecents="true"
+ android:configChanges="orientation|keyboardHidden"
+ android:enabled="@bool/profile_supported_map">
+ </activity>
+ <provider android:name=".map.MmsFileProvider"
+ android:authorities="com.android.bluetooth.map.MmsFileProvider"
+ android:enabled="true"
+ android:grantUriPermissions="true"
+ android:exported="false">
+ </provider>
+ <service
+ android:process="@string/process"
+ android:name=".sap.SapService"
+ android:enabled="@bool/profile_supported_sap" >
+ <intent-filter>
+ <action android:name="android.bluetooth.IBluetoothSap" />
+ </intent-filter>
+ </service>
+ <service
+ android:process="@string/process"
+ android:name = ".gatt.GattService"
+ android:enabled="@bool/profile_supported_gatt">
+ <intent-filter>
+ <action android:name="android.bluetooth.IBluetoothGatt" />
+ </intent-filter>
+ </service>
+ <service
+ android:process="@string/process"
+ android:name = ".hfp.HeadsetService"
+ android:enabled="@bool/profile_supported_hs_hfp">
+ <intent-filter>
+ <action android:name="android.bluetooth.IBluetoothHeadset" />
+ </intent-filter>
+ </service>
+ <service
+ android:process="@string/process"
+ android:name = ".a2dp.A2dpService"
+ android:enabled="@bool/profile_supported_a2dp">
+ <intent-filter>
+ <action android:name="android.bluetooth.IBluetoothA2dp" />
+ </intent-filter>
+ </service>
+ <service
+ android:process="@string/process"
+ android:name = ".a2dp.A2dpSinkService"
+ android:enabled="@bool/profile_supported_a2dp_sink">
+ <intent-filter>
+ <action android:name="android.bluetooth.IBluetoothA2dpSink" />
+ </intent-filter>
+ </service>
+ <service
+ android:process="@string/process"
+ android:name = ".avrcp.AvrcpControllerService"
+ android:permission="android.permission.ACCESS_BLUETOOTH_AVRCP_CT_DATA"
+ android:enabled="@bool/profile_supported_avrcp_controller">
+ <intent-filter>
+ <action android:name="android.bluetooth.IBluetoothAvrcpController" />
+ </intent-filter>
+ </service>
+ <service
+ android:process="@string/process"
+ android:name = ".hid.HidService"
+ android:enabled="@bool/profile_supported_hid">
+ <intent-filter>
+ <action android:name="android.bluetooth.IBluetoothInputDevice" />
+ </intent-filter>
+ </service>
+ <service
+ android:process="@string/process"
+ android:name = ".hdp.HealthService"
+ android:enabled="@bool/profile_supported_hdp">
+ <intent-filter>
+ <action android:name="android.bluetooth.IBluetoothHealth" />
+ </intent-filter>
+ </service>
+ <service
+ android:process="@string/process"
+ android:name = ".pan.PanService"
+ android:enabled="@bool/profile_supported_pan">
+ <intent-filter>
+ <action android:name="android.bluetooth.IBluetoothPan" />
+ </intent-filter>
+ </service>
+ <service
+ android:process="@string/process"
+ android:name = ".hfpclient.HeadsetClientService"
+ android:enabled="@bool/profile_supported_hfpclient">
+ <intent-filter>
+ <action android:name="android.bluetooth.IBluetoothHeadsetClient" />
+ </intent-filter>
+ </service>
+ <service
+ android:process="@string/process"
+ android:name = ".hid.HidDevService"
+ android:enabled="@bool/profile_supported_hidd">
+ <intent-filter>
+ <action android:name="android.bluetooth.IBluetoothHidDevice" />
+ </intent-filter>
+ </service>
+ <service
+ android:process="@string/process"
+ android:name = "com.broadcom.fm.fmreceiver.FmService">
+ <intent-filter>
+ <action android:name="com.broadcom.fm.fmreceiver.IFmReceiverService" />
+ </intent-filter>
+ </service>
+ </application>
+</manifest>
diff --git a/fm/app/jni/com_broadcom_fm_service.cpp b/fm/app/jni/com_broadcom_fm_service.cpp
new file mode 100644
index 0000000..3e62ee8
--- /dev/null
+++ b/fm/app/jni/com_broadcom_fm_service.cpp
@@ -0,0 +1,955 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+
+#define LOG_TAG "FmServiceJni"
+
+//#define LOG_NDEBUG 0
+
+
+
+
+#define CHECK_CALLBACK_ENV \
+ if (!checkCallbackThread()) { \
+ ALOGE("Callback: '%s' is not called on the correct thread", __FUNCTION__);\
+ return; \
+ }
+
+/* I2C_FM_SEARCH_METHOD */
+#define I2C_FM_SEARCH_NORMAL 0x00
+#define I2C_FM_SEARCH_PRESET 0x01 /* default: preset scan with CO */
+#define I2C_FM_SEARCH_RSSI 0x02
+#define I2C_FM_SEARCH_PRESET_SNR 0x03 /* preset scan with SNR */
+
+/* scan mode */
+#define BTA_FM_PRESET_SCAN I2C_FM_SEARCH_PRESET /* preset scan : bit0 = 1 */
+#define BTA_FM_NORMAL_SCAN I2C_FM_SEARCH_NORMAL /* normal scan : bit0 = 0 */
+typedef unsigned char tBTA_FM_SCAN_METHOD;
+
+/* frequency scanning direction */
+#define BTA_FM_SCAN_DOWN 0x00 /* bit7 = 0 scanning toward lower frequency */
+#define BTA_FM_SCAN_UP 0x80 /* bit7 = 1 scanning toward higher frequency */
+typedef unsigned char tBTA_FM_SCAN_DIR;
+
+#define BTA_FM_SCAN_FULL (BTA_FM_SCAN_UP | BTA_FM_NORMAL_SCAN|0x02) /* full band scan */
+#define BTA_FM_FAST_SCAN (BTA_FM_SCAN_UP | BTA_FM_PRESET_SCAN) /* use preset scan */
+#define BTA_FM_SCAN_NONE 0xff
+
+typedef unsigned char tBTA_FM_SCAN_MODE;
+
+#define BTA_FM_SCAN_FULL (BTA_FM_SCAN_UP | BTA_FM_NORMAL_SCAN|0x02) /* full band scan */
+#define BTA_FM_FAST_SCAN (BTA_FM_SCAN_UP | BTA_FM_PRESET_SCAN) /* use preset scan */
+#define BTA_FM_SCAN_NONE 0xff
+
+
+
+
+/*Fm App audio config value*/
+#define FM_AUDIO_PATH_NONE 0
+#define FM_AUDIO_PATH_ANALOG 1
+#define FM_AUDIO_PATH_DIGITAL 2
+
+#define FM_ROUTE_NONE 0x00 /* No Audio output */
+#define FM_ROUTE_DAC 0x01 /* routing over analog output */
+#define FM_ROUTE_I2S 0x02 /* routing over digital (I2S) output */
+
+
+#include "com_android_bluetooth.h"
+#include "hardware/bt_fm.h"
+#include "utils/Log.h"
+#include "android_runtime/AndroidRuntime.h"
+
+#include <string.h>
+
+
+
+namespace android {
+
+static btfm_rds_update_t previous_rds_update;
+static jint rds_type_save;
+static jboolean af_on_save;
+static jboolean rds_on_save;
+static int processing_scan = 0;
+
+/* Local references to java callback event functions. */
+static jmethodID method_onRadioOnEvent;
+static jmethodID method_onRadioOffEvent;
+static jmethodID method_onRadioMuteEvent;
+static jmethodID method_onRadioTuneEvent;
+static jmethodID method_onRadioSearchEvent;
+static jmethodID method_onRadioSearchCompleteEvent;
+static jmethodID method_onRadioAfJumpEvent;
+static jmethodID method_onRadioAudioModeEvent;
+static jmethodID method_onRadioAudioPathEvent;
+static jmethodID method_onRadioAudioDataEvent;
+static jmethodID method_onRadioRdsModeEvent;
+static jmethodID method_onRadioRdsTypeEvent;
+static jmethodID method_onRadioRdsUpdateEvent;
+static jmethodID method_onRadioDeemphEvent;
+static jmethodID method_onRadioScanStepEvent;
+static jmethodID method_onRadioRegionEvent;
+static jmethodID method_onRadioNflEstimationEvent;
+static jmethodID method_onRadioVolumeEvent;
+
+static const btfm_interface_t *sFmIf = NULL;
+static jobject mCallbacksObj = NULL;
+static JNIEnv *sCallbackEnv = NULL;
+
+static int sFmAppAudioPath = -1;
+static int sCurrentBtaPath = -1;
+
+static jboolean setRdsRdbsNative(jint rdsType);
+
+static void resetFmData()
+{
+ sFmAppAudioPath = -1;
+ sCurrentBtaPath = -1;
+}
+
+static bool checkCallbackThread() {
+ // Always fetch the latest callbackEnv from AdapterService.
+ // Caching this could cause this sCallbackEnv to go out-of-sync
+ // with the AdapterService's ENV if an ASSOCIATE/DISASSOCIATE event
+ // is received
+ //if (sCallbackEnv == NULL) {
+ sCallbackEnv = getCallbackEnv();
+ //}
+
+ JNIEnv* env = AndroidRuntime::getJNIEnv();
+ if (sCallbackEnv != env || sCallbackEnv == NULL) return false;
+ return true;
+}
+
+
+/**
+ * Enable fm callback
+ */
+static void enable_callback(int status)
+{
+ CHECK_CALLBACK_ENV
+
+ memset(&previous_rds_update, 0, sizeof(btfm_rds_update_t));
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onRadioOnEvent, (jint)status);
+
+}
+
+/**
+ * Disable fm callback
+ */
+static void disable_callback(int status)
+{
+ CHECK_CALLBACK_ENV
+
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onRadioOffEvent, (jint)status);
+}
+
+/**
+ * Fm tune event callback
+ */
+static void tune_callback(int status, int rssi, int snr, int freq)
+{
+ CHECK_CALLBACK_ENV
+
+ memset(&previous_rds_update, 0, sizeof(btfm_rds_update_t));
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onRadioTuneEvent,
+ (jint)status, (jint)rssi,(jint)snr, (jint)freq);
+
+}
+
+
+/**
+ * Fm mute event callback
+ */
+static void mute_callback(int status, bool isMute)
+{
+ CHECK_CALLBACK_ENV
+
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onRadioMuteEvent,
+ (jint)status, (jboolean)isMute);
+}
+
+/**
+ * Fm search event callback
+ */
+static void search_callback(int status, int rssi, int snr, int freq)
+{
+ CHECK_CALLBACK_ENV
+ if (processing_scan) {
+ ALOGI("[JNI] - BTA_FM_SEARCH_EVT: rssi = %i, freq = %i snr = %i", rssi,
+ freq, snr);
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onRadioSearchEvent,
+ (jint)rssi, (jint)snr, (jint)freq);
+ }
+
+}
+
+
+/**
+ * Fm search complete event callback
+ */
+static void search_complete_callback(int status, int rssi, int snr, int freq)
+{
+ CHECK_CALLBACK_ENV
+
+ ALOGI("[JNI] - BTA_FM_SEARCH_CMPL_EVT: status = %i rssi = %i freq = %i snr = %i",
+ status, rssi, freq, snr);
+ memset(&previous_rds_update, 0, sizeof(btfm_rds_update_t));
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onRadioSearchCompleteEvent,
+ (jint)status, (jint)rssi, (jint)snr, (jint)freq);
+ if (processing_scan)
+ processing_scan = 0;
+
+}
+
+
+/**
+ * Fm af jump event callback
+ */
+static void af_jump_callback(int status, int rssi, int snr, int freq)
+{
+ CHECK_CALLBACK_ENV
+
+ memset(&previous_rds_update, 0, sizeof(btfm_rds_update_t));
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onRadioAfJumpEvent,
+ (jint)status, (jint)rssi, (jint)freq);
+ ALOGI("[JNI] - TRANSMITTING EVENT BTA_FM_AF_JMP_EVT : status = %i rssi = %i freq = %i",
+ status, rssi, freq);
+ if (processing_scan)
+ processing_scan = 0;
+
+}
+
+
+/**
+ * Fm audio mode callback
+ */
+static void audio_mode_callback(int status, int audioMode)
+{
+ CHECK_CALLBACK_ENV
+
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onRadioAudioModeEvent,
+ (jint)status, (jint)audioMode);
+
+}
+
+
+/**
+ * Fm audio path callback
+ */
+static void audio_path_callback(int status, int audioPath)
+{
+ CHECK_CALLBACK_ENV
+
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onRadioAudioPathEvent,
+ (jint)status, (jint)sFmAppAudioPath);
+
+}
+
+
+/**
+ * Fm audio data callback
+ */
+static void audio_data_callback(int status, int rssi, int snr, int audioMode)
+{
+ CHECK_CALLBACK_ENV
+
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onRadioAudioDataEvent,
+ (jint)status, (jint)rssi, (jint)audioMode, (jint)snr);
+
+}
+
+
+/**
+ * Fm rds mode callback
+ */
+static void rds_mode_callback(int status, bool rdsOn, bool afOn)
+{
+ CHECK_CALLBACK_ENV
+
+ ALOGI("%s: BTA_FM_RDS_MODE_EVT", __FUNCTION__);
+ if(status == BT_STATUS_SUCCESS)
+ {
+ af_on_save = (jboolean)afOn;
+ rds_on_save = (jboolean)rdsOn;
+ setRdsRdbsNative(rds_type_save);
+ }
+ else{
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onRadioRdsModeEvent,
+ (jint)status, (jboolean)rdsOn,(jboolean)afOn,(jint)rds_type_save);
+ }
+
+}
+
+
+/**
+ * Fm rds type callback
+ */
+static void rds_type_callback(int status, int rdsType)
+{
+ CHECK_CALLBACK_ENV
+
+ ALOGI("%s: BTA_FM_RDS_TYPE_EVT", __FUNCTION__);
+
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onRadioRdsModeEvent,
+ (jint)status, rds_on_save, af_on_save,(jint)rdsType);
+
+}
+
+
+/**
+ * Fm deempasis param callback
+ */
+static void deemphasis_callback(int status, int timeConst)
+{
+ CHECK_CALLBACK_ENV
+
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onRadioDeemphEvent,
+ (jint)status, (jint)timeConst);
+
+}
+
+
+/**
+ * Fm scan step callback
+ */
+static void scan_step_callback(int status, int scanStep)
+{
+ CHECK_CALLBACK_ENV
+
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onRadioScanStepEvent,
+ (jint)scanStep);
+
+}
+
+
+/**
+ * Fm region callback
+ */
+static void region_callback(int status, int region)
+{
+ CHECK_CALLBACK_ENV
+
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onRadioRegionEvent,
+ (jint)status, (jint)region);
+
+}
+
+
+/**
+ * Fm noise floor level callback
+ */
+static void nfl_callback(int status, int noiseFloor)
+ {
+ CHECK_CALLBACK_ENV
+
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onRadioNflEstimationEvent,
+ (jint)noiseFloor);
+
+ }
+
+
+/**
+ * Fm volume callback
+ */
+static void volume_callback(int status, int volume)
+{
+ CHECK_CALLBACK_ENV
+
+ ALOGI("volume_callback : status = %d", status);
+
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onRadioVolumeEvent,
+ (jint)status, (jint)volume);
+
+}
+
+
+/* Assumes data exists. Screens the text field for illegal (non ASCII) characters. */
+static void screenData(char* text) {
+ int i = 0;
+ char c = text[i];
+
+ while ((i < 65) && (0 != c)) {
+ /* Check legality of all characters. */
+ if ((c < 32) || (c > 126)) {
+ text[i] = '*';
+ }
+ /* Look at next character. */
+ c = text[++i];
+ }
+ /* Cap for safety at end of 64 bytes. */
+ text[64] = 0;
+}
+
+/**
+ * Fm rds data callback
+ */
+static void rds_data_callback(int status, int dataType, int index,
+ char *radioText)
+{
+ CHECK_CALLBACK_ENV
+
+ ALOGI("%s: BTA_FM_RDS_UPD_EVT", __FUNCTION__);
+ if (NULL != radioText) {
+ /* Pre-process data. */
+ screenData(radioText);
+ //not to overload system with useless events check if data has changed
+ ALOGI("%s: BTA_FM_RDS_UPD_EVT, previous_rds%s", __FUNCTION__,previous_rds_update.text);
+ ALOGI("%s: BTA_FM_RDS_UPD_EVT, new_rds%s", __FUNCTION__,radioText);
+ ALOGI("%s: BTA_FM_RDS_UPD_EVT, memcmp 0x%8x", __FUNCTION__,
+ memcmp(&previous_rds_update.text, radioText, 65));
+ if(memcmp(&previous_rds_update.text, radioText, 65)) {
+ if (memcpy(&previous_rds_update.text, radioText, 65)) {
+ /* Transmit upwards. */
+ sCallbackEnv->CallVoidMethod(mCallbacksObj, method_onRadioRdsUpdateEvent,
+ (jint)status, (jint)dataType,(jint)index,sCallbackEnv->NewStringUTF(radioText));
+ }
+ }
+
+ }
+}
+
+/**
+ * Fm rtp data callback
+ */
+static void rtp_data_callback(btfm_rds_rtp_info_t *rtpInfo)
+{
+ CHECK_CALLBACK_ENV
+
+ ALOGI("%s: BTA_RDS_RTP_EVT", __FUNCTION__);
+ if (NULL != rtpInfo) {
+ int k = 0;
+
+ ALOGI("%s: bRunning: %d bToggle = %d tag_num - %d",__FUNCTION__,
+ rtpInfo->running,rtpInfo->tag_toggle,
+ rtpInfo->tag_num);
+ k = rtpInfo->tag_num;
+ while(k){
+
+ ALOGI("%s: content_type: %d start: %d len: %d",__FUNCTION__,
+ rtpInfo->tag[k].content_type,rtpInfo->tag[k].start,
+ rtpInfo->tag[k].len);
+ k--;
+ }
+ //TODO: this event is not sent to java layer as of now. If requirement arises
+ // this event as to passed to app.
+ }
+}
+
+
+static btfm_callbacks_t sFmCallbacks = {
+ sizeof(btfm_callbacks_t),
+ enable_callback,
+ disable_callback,
+ tune_callback,
+ mute_callback,
+ search_callback,
+ search_complete_callback,
+ af_jump_callback,
+ audio_mode_callback,
+ audio_path_callback,
+ audio_data_callback,
+ rds_mode_callback,
+ rds_type_callback,
+ deemphasis_callback,
+ scan_step_callback,
+ region_callback,
+ nfl_callback,
+ volume_callback,
+ rds_data_callback,
+ rtp_data_callback
+};
+
+
+static void classInitNative(JNIEnv* env, jclass clazz) {
+
+
+ method_onRadioOnEvent =
+ env->GetMethodID(clazz, "onRadioOnEvent", "(I)V");
+ method_onRadioOffEvent =
+ env->GetMethodID(clazz, "onRadioOffEvent", "(I)V");
+ method_onRadioMuteEvent =
+ env->GetMethodID(clazz, "onRadioMuteEvent", "(IZ)V");
+ method_onRadioTuneEvent =
+ env->GetMethodID(clazz, "onRadioTuneEvent", "(IIII)V");
+ method_onRadioSearchEvent =
+ env->GetMethodID(clazz, "onRadioSearchEvent", "(III)V");
+ method_onRadioSearchCompleteEvent =
+ env->GetMethodID(clazz, "onRadioSearchCompleteEvent", "(IIII)V");
+ method_onRadioAfJumpEvent =
+ env->GetMethodID(clazz, "onRadioAfJumpEvent", "(III)V");
+ method_onRadioAudioPathEvent =
+ env->GetMethodID(clazz, "onRadioAudioPathEvent", "(II)V");
+ method_onRadioAudioModeEvent =
+ env->GetMethodID(clazz, "onRadioAudioModeEvent", "(II)V");
+ method_onRadioAudioDataEvent =
+ env->GetMethodID(clazz, "onRadioAudioDataEvent", "(IIII)V");
+ method_onRadioRdsModeEvent =
+ env->GetMethodID(clazz, "onRadioRdsModeEvent", "(IZZI)V");
+ method_onRadioRdsTypeEvent =
+ env->GetMethodID(clazz, "onRadioRdsTypeEvent", "(II)V");
+ method_onRadioRdsUpdateEvent =
+ env->GetMethodID(clazz, "onRadioRdsUpdateEvent", "(IIILjava/lang/String;)V");
+ method_onRadioDeemphEvent =
+ env->GetMethodID(clazz, "onRadioDeemphEvent", "(II)V");
+ method_onRadioScanStepEvent =
+ env->GetMethodID(clazz, "onRadioScanStepEvent", "(I)V");
+ method_onRadioRegionEvent =
+ env->GetMethodID(clazz, "onRadioRegionEvent", "(II)V");
+ method_onRadioNflEstimationEvent =
+ env->GetMethodID(clazz, "onRadioNflEstimationEvent", "(I)V");
+ method_onRadioVolumeEvent =
+ env->GetMethodID(clazz, "onRadioVolumeEvent", "(II)V");
+
+ ALOGI("%s: succeeds", __FUNCTION__);
+}
+
+static const bt_interface_t* btIf;
+
+static void initializeNative(JNIEnv *env, jobject object) {
+ ALOGD("fm");
+
+ if ( (btIf = getBluetoothInterface()) == NULL) {
+ ALOGE("Fm module is not loaded");
+ return;
+ }
+
+ if (sFmIf !=NULL) {
+ ALOGE("Cleaning up Fm Interface before initializing...");
+ sFmIf->cleanup();
+ sFmIf = NULL;
+ }
+
+ if (mCallbacksObj != NULL) {
+ ALOGE("Cleaning up Fm callback object");
+ env->DeleteGlobalRef(mCallbacksObj);
+ mCallbacksObj = NULL;
+ }
+
+ if ( (sFmIf = (btfm_interface_t*)btIf->get_profile_interface(BT_PROFILE_FM_ID)) == NULL) {
+ ALOGE("Failed to get Fm Interface");
+ return;
+ }
+
+ int status;
+ if ( (status = sFmIf->init(&sFmCallbacks)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed to initialize Fm, status: %d", status);
+ sFmIf = NULL;
+ return;
+ }
+
+ mCallbacksObj = env->NewGlobalRef(object);
+ processing_scan = 0;
+}
+
+static void cleanupNative(JNIEnv *env, jobject object) {
+ const bt_interface_t* btInf;
+ int status;
+
+ ALOGE("Fm cleanupNative");
+
+ if ( (btInf = getBluetoothInterface()) == NULL) {
+ ALOGE("Bluetooth module is not loaded");
+ return;
+ }
+
+ if (sFmIf !=NULL) {
+ sFmIf->cleanup();
+ sFmIf = NULL;
+ resetFmData();
+ }
+
+ if (mCallbacksObj != NULL) {
+ env->DeleteGlobalRef(mCallbacksObj);
+ mCallbacksObj = NULL;
+ }
+}
+
+
+static jboolean enableFmNative(JNIEnv *env, jobject obj, jint functionalityMask)
+{
+ ALOGI("[JNI] - enableFmNative : functionalityMask = %i", functionalityMask);
+ jboolean ret = JNI_TRUE;
+ int status;
+ if (!sFmIf) return JNI_FALSE;
+ if ((status = sFmIf->enable((int)functionalityMask)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed FM enable, status: %d", status);
+ ret = JNI_FALSE;
+ }
+ return ret;
+}
+
+static jboolean disableFmNative(JNIEnv *env, jobject obj, jboolean bForcing)
+{
+ ALOGI("[JNI] - disableNative :");
+ jboolean ret = JNI_TRUE;
+ int status;
+
+ if (!sFmIf) return JNI_FALSE;
+
+ if ((status = sFmIf->disable()) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed FM disable, status: %d", status);
+ ret = JNI_FALSE;
+ }
+ return ret;
+
+}
+
+static jboolean tuneNative(JNIEnv *env, jobject obj, jint freq)
+{
+ ALOGI("[JNI] - tuneNative : freq = %i", freq);
+ jboolean ret = JNI_TRUE;
+ int status;
+
+ if (!sFmIf) return JNI_FALSE;
+
+ if ((status = sFmIf->tune((int)freq)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed FM tune, status: %d", status);
+ ret = JNI_FALSE;
+ }
+ return ret;
+
+}
+
+static jboolean muteNative(JNIEnv *env, jobject obj, jboolean toggle)
+{
+ ALOGI("[JNI] - muteNative : toggle = %i", toggle);
+ jboolean ret = JNI_TRUE;
+ int status;
+
+ if (!sFmIf) return JNI_FALSE;
+
+ if ((status = sFmIf->mute((bool)toggle)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed FM Mute, status: %d", status);
+ ret = JNI_FALSE;
+ }
+ return ret;
+}
+
+static jboolean searchNative(JNIEnv *env, jobject obj, jint scanMode, jint rssiThreshold, jint condVal, jint condType)
+{
+ ALOGI("[JNI] - searchNative : scanMode = %i rssiThreshold = %i condVal = %i condType = %i",
+ scanMode, rssiThreshold, condVal, condType);
+ jboolean ret = JNI_TRUE;
+ int status;
+
+ if (!sFmIf) return JNI_FALSE;
+
+ if (scanMode == BTA_FM_SCAN_FULL || scanMode == BTA_FM_FAST_SCAN)
+ processing_scan = 1;
+
+ if ((status = sFmIf->search((int)scanMode, (int)rssiThreshold, (int)condVal,
+ (int)condType)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed FM Tune, status: %d", status);
+ ret = JNI_FALSE;
+ }
+ return ret;
+
+}
+
+static jboolean comboSearchNative(JNIEnv *env, jobject obj, jint startFreq,
+ jint endFreq, jint rssiThreshold, jint direction, jint scanMethod,
+ jboolean multiChannel, jint rdsType, jint rdsTypeValue)
+{
+ ALOGI("[JNI] - comboSearchNative");
+ jboolean ret = JNI_TRUE;
+ int status;
+ if (!sFmIf) return JNI_FALSE;
+
+ if ((status = sFmIf->combo_search((int)startFreq, (int)endFreq,
+ (int)rssiThreshold, (int)direction, (int)scanMethod,
+ (int)multiChannel, (int)rdsType, (int)rdsTypeValue)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed FM Tune, status: %d", status);
+ ret = JNI_FALSE;
+ }
+ return ret;
+
+}
+
+static jboolean searchAbortNative(JNIEnv *env, jobject obj)
+{
+ ALOGI("[JNI] - searchAbortNative :");
+ jboolean ret = JNI_TRUE;
+ int status;
+
+ if (!sFmIf) return JNI_FALSE;
+
+ if (processing_scan)
+ processing_scan = 0;
+
+ if ((status = sFmIf->search_abort()) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed FM Search abort, status: %d", status);
+ ret = JNI_FALSE;
+ }
+ return ret;
+}
+
+static jboolean setRdsModeNative(JNIEnv *env, jobject obj, jboolean rdsOn,
+ jboolean afOn, jint rdsType)
+{
+ ALOGI("[JNI] - setRdsModeNative :");
+ jboolean ret = JNI_TRUE;
+ int status;
+
+ if (!sFmIf) return JNI_FALSE;
+
+ if ((status = sFmIf->set_rds_mode((bool)rdsOn, (bool)afOn)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed FM set_rds_mode, status: %d", status);
+ ret = JNI_FALSE;
+ } else {
+ rds_type_save = (int) rdsType;
+ }
+ return ret;
+}
+
+static jboolean setRdsRdbsNative(jint rdsType)
+{
+ jboolean ret = JNI_TRUE;
+ int status;
+
+ if (!sFmIf) return JNI_FALSE;
+
+ ALOGI("[JNI] - setRdsRdbsNative :");
+ if ((status = sFmIf->set_rds_type((int)rdsType)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed FM setRdsRdbsNative, status: %d", status);
+ ret = JNI_FALSE;
+ }
+ return ret;
+
+}
+
+static jboolean setAudioModeNative(JNIEnv *env, jobject obj, jint audioMode)
+{
+ jboolean ret = JNI_TRUE;
+ int status;
+
+ if (!sFmIf) return JNI_FALSE;
+
+ ALOGI("[JNI] - setAudioModeNative :");
+ if ((status = sFmIf->set_audio_mode((int)audioMode)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed FM setAudioModeNative, status: %d", status);
+ ret = JNI_FALSE;
+ }
+ return ret;
+
+}
+
+
+static jboolean setAudioPathNative(JNIEnv *env, jobject obj, jint audioPath)
+{
+ jboolean ret = JNI_TRUE;
+ int status;
+ int desiredBtaAudioPath = -1;
+
+ if (!sFmIf) return JNI_FALSE;
+
+ ALOGI("[JNI] - setAudioPathNative : audioPath = %i", audioPath);
+ sFmAppAudioPath = audioPath;
+ switch (audioPath)
+ {
+ case FM_AUDIO_PATH_NONE:
+ desiredBtaAudioPath = FM_ROUTE_NONE;
+ break;
+ case FM_AUDIO_PATH_ANALOG:
+ desiredBtaAudioPath = FM_ROUTE_DAC;
+ break;
+ case FM_AUDIO_PATH_DIGITAL:
+ desiredBtaAudioPath = FM_ROUTE_I2S;
+ break;
+ default:
+ ALOGE( "Unsupported Audio path: %d!",
+ audioPath );
+ break;
+ }
+
+ if ( -1 != desiredBtaAudioPath) {
+ if ((status = sFmIf->set_audio_path((int)desiredBtaAudioPath)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed FM setAudioPathNative, status: %d", status);
+ ret = JNI_FALSE;
+ }
+ sCurrentBtaPath = desiredBtaAudioPath;
+ }
+
+ return ret;
+
+}
+
+
+static jboolean setScanStepNative(JNIEnv *env, jobject obj, jint stepSize)
+{
+ jboolean ret = JNI_TRUE;
+ int status;
+
+ if (!sFmIf) return JNI_FALSE;
+
+ ALOGI("[JNI] - setScanStepNative : stepSize = %i", stepSize);
+ if ((status = sFmIf->set_scan_step((int)stepSize)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed FM setScanStepNative, status: %d", status);
+ ret = JNI_FALSE;
+ }
+ return ret;
+
+}
+
+static jboolean setRegionNative(JNIEnv *env, jobject obj, jint region)
+{
+ jboolean ret = JNI_TRUE;
+ int status;
+ if (!sFmIf) return JNI_FALSE;
+
+ ALOGI("[JNI] - setRegionNative : stepSize = %i", region);
+ if ((status = sFmIf->set_region((int)region)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed FM setRegionNative, status: %d", status);
+ ret = JNI_FALSE;
+ }
+ return ret;
+
+}
+
+static jboolean configureDeemphasisNative(JNIEnv *env, jobject obj, jint time)
+{
+ jboolean ret = JNI_TRUE;
+ int status;
+
+ if (!sFmIf) return JNI_FALSE;
+
+ ALOGI("[JNI] - configureDeemphasisNative : time = %i", time);
+ if ((status = sFmIf->config_deemphasis((int)time)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed FM configureDeemphasisNative, status: %d", status);
+ ret = JNI_FALSE;
+ }
+ return ret;
+
+}
+
+static jboolean estimateNoiseFloorNative(JNIEnv *env, jobject obj, jint level)
+{
+ jboolean ret = JNI_TRUE;
+ int status;
+
+ if (!sFmIf) return JNI_FALSE;
+
+ ALOGI("[JNI] - estimateNoiseFloorNative : level = %i", level);
+ if ((status = sFmIf->estimate_noise_floor((int)time)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed FM estimateNoiseFloorNative, status: %d", status);
+ ret = JNI_FALSE;
+ }
+ return ret;
+
+}
+
+static jboolean getAudioQualityNative(JNIEnv *env, jobject obj, jboolean enable)
+{
+ jboolean ret = JNI_TRUE;
+ int status;
+
+ if (!sFmIf) return JNI_FALSE;
+
+ ALOGI("[JNI] - estimateNoiseFloorNative : enable = %i", enable);
+ if ((status = sFmIf->read_audio_quality((bool)enable)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed FM estimateNoiseFloorNative, status: %d", status);
+ ret = JNI_FALSE;
+ }
+ return ret;
+
+}
+
+static jboolean configureSignalNotificationNative(JNIEnv *env, jobject obj,
+ jint pollInterval)
+{
+ jboolean ret = JNI_TRUE;
+ int status;
+
+ if (!sFmIf) return JNI_FALSE;
+
+ ALOGI("[JNI] - configureSignalNotificationNative : pollInterval = %i", pollInterval);
+ if ((status = sFmIf->config_signal_notification((int)pollInterval)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed FM configureSignalNotificationNative, status: %d", status);
+ ret = JNI_FALSE;
+ }
+ return ret;
+
+}
+
+
+static jboolean setFMVolumeNative(JNIEnv *env, jobject obj, jint volume)
+{
+ jboolean ret = JNI_TRUE;
+ int status;
+
+ if (!sFmIf) return JNI_FALSE;
+
+ ALOGI("[JNI] - setFMVolumeNative : volume = %i,bta_path = %d ",
+ volume, sCurrentBtaPath);
+
+ if ((status = sFmIf->set_volume((int)volume)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed FM setFMVolumeNative, status: %d", status);
+ ret = JNI_FALSE;
+ }
+
+ return ret;
+
+}
+
+static jboolean setSnrThresholdNative(JNIEnv *env, jobject obj, jint snr_thres)
+{
+
+ jboolean ret = JNI_TRUE;
+ int status;
+
+ if (!sFmIf) return JNI_FALSE;
+
+ ALOGI("[JNI] - setSnrThresholdNative : snr_thres = %i", snr_thres);
+ if ((status = sFmIf->set_search_criteria((int)snr_thres)) != BT_STATUS_SUCCESS) {
+ ALOGE("Failed FM setSnrThresholdNative, status: %d", status);
+ ret = JNI_FALSE;
+ }
+ return ret;
+
+}
+
+static JNINativeMethod sMethods[] = {
+ {"classInitNative", "()V", (void *) classInitNative},
+ {"initializeNative", "()V", (void *) initializeNative},
+ {"cleanupNative", "()V", (void *) cleanupNative},
+ {"enableFmNative", "(I)Z", (void *)enableFmNative},
+ {"disableFmNative", "(Z)Z", (void *)disableFmNative},
+ {"tuneNative", "(I)Z", (void *)tuneNative},
+ {"muteNative", "(Z)Z", (void *)muteNative},
+ {"searchNative", "(IIII)Z", (void *)searchNative},
+ {"comboSearchNative", "(IIIIIZII)Z", (void *)comboSearchNative},
+ {"searchAbortNative", "()Z", (void *)searchAbortNative},
+ {"setRdsModeNative", "(ZZI)Z", (void *) setRdsModeNative},
+ {"setAudioModeNative", "(I)Z", (void *)setAudioModeNative},
+ {"setAudioPathNative", "(I)Z", (void *)setAudioPathNative},
+ {"setScanStepNative", "(I)Z", (void *) setScanStepNative},
+ {"setRegionNative", "(I)Z", (void *) setRegionNative},
+ {"configureDeemphasisNative", "(I)Z", (void *) configureDeemphasisNative},
+ {"estimateNoiseFloorNative", "(I)Z", (void *) estimateNoiseFloorNative},
+ {"getAudioQualityNative", "(Z)Z", (void *) getAudioQualityNative},
+ {"configureSignalNotificationNative", "(I)Z",
+ (void *) configureSignalNotificationNative},
+ {"setFMVolumeNative", "(I)Z", (void *) setFMVolumeNative},
+ {"setSnrThresholdNative", "(I)Z", (void *) setSnrThresholdNative},
+};
+
+int register_com_broadcom_fm_service(JNIEnv* env)
+{
+ return jniRegisterNativeMethods(env, "com/broadcom/fm/fmreceiver/FmNativehandler",
+ sMethods, NELEM(sMethods));
+}
+
+
+}
diff --git a/fm/app/src/com/broadcom/fm/fmreceiver/FmNativehandler.java b/fm/app/src/com/broadcom/fm/fmreceiver/FmNativehandler.java
new file mode 100644
index 0000000..7f28e1c
--- /dev/null
+++ b/fm/app/src/com/broadcom/fm/fmreceiver/FmNativehandler.java
@@ -0,0 +1,2616 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2009-2013 Broadcom Corporation
+ *
+ * This program is the proprietary software of Broadcom Corporation and/or its
+ * licensors, and may only be used, duplicated, modified or distributed
+ * pursuant to the terms and conditions of a separate, written license
+ * agreement executed between you and Broadcom (an "Authorized License").
+ * Except as set forth in an Authorized License, Broadcom grants no license
+ * (express or implied), right to use, or waiver of any kind with respect to
+ * the Software, and Broadcom expressly reserves all rights in and to the
+ * Software and all intellectual property rights therein.
+ * IF YOU HAVE NO AUTHORIZED LICENSE, THEN YOU HAVE NO RIGHT TO USE THIS
+ * SOFTWARE IN ANY WAY, AND SHOULD IMMEDIATELY NOTIFY BROADCOM AND DISCONTINUE
+ * ALL USE OF THE SOFTWARE.
+ *
+ * Except as expressly set forth in the Authorized License,
+ *
+ * 1. This program, including its structure, sequence and organization,
+ * constitutes the valuable trade secrets of Broadcom, and you shall
+ * use all reasonable efforts to protect the confidentiality thereof,
+ * and to use this information only in connection with your use of
+ * Broadcom integrated circuit products.
+ *
+ * 2. TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED
+ * "AS IS" AND WITH ALL FAULTS AND BROADCOM MAKES NO PROMISES,
+ * REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY,
+ * OR OTHERWISE, WITH RESPECT TO THE SOFTWARE. BROADCOM SPECIFICALLY
+ * DISCLAIMS ANY AND ALL IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY,
+ * NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES,
+ * ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
+ * CORRESPONDENCE TO DESCRIPTION. YOU ASSUME THE ENTIRE RISK ARISING
+ * OUT OF USE OR PERFORMANCE OF THE SOFTWARE.
+ *
+ * 3. TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL BROADCOM
+ * OR ITS LICENSORS BE LIABLE FOR
+ * (i) CONSEQUENTIAL, INCIDENTAL, SPECIAL, INDIRECT, OR EXEMPLARY
+ * DAMAGES WHATSOEVER ARISING OUT OF OR IN ANY WAY RELATING TO
+ * YOUR USE OF OR INABILITY TO USE THE SOFTWARE EVEN IF BROADCOM
+ * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES; OR
+ * (ii) ANY AMOUNT IN EXCESS OF THE AMOUNT ACTUALLY PAID FOR THE
+ * SOFTWARE ITSELF OR U.S. $1, WHICHEVER IS GREATER. THESE
+ * LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF
+ * ESSENTIAL PURPOSE OF ANY LIMITED REMEDY.
+ *
+ *****************************************************************************/
+
+package com.broadcom.fm.fmreceiver;
+
+import android.bluetooth.IBluetooth;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.ServiceConnection;
+import android.media.AudioSystem;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteCallbackList;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.bluetooth.btservice.AdapterService;
+import com.broadcom.fm.fmreceiver.IFmReceiverCallback;
+
+import java.text.DateFormat;
+import java.util.Date;
+import java.util.Iterator;
+import java.util.LinkedList;
+
+/**
+ * Provides a service for accessing and altering FM receiver hardware
+ * settings. Requests should be made via proxy to this server and once the
+ * requests have been processed, event(s) will be issued with the result of the
+ * request execution. Also provides the callback event loop.
+ *
+ * @hide
+ */
+public final class FmNativehandler {
+ private static final boolean V = true;
+ private static final String TAG = "FmNativehandler";
+
+ /* The list of all registered client callbacks. */
+ private final RemoteCallbackList<IFmReceiverCallback> mCallbacks =
+ new RemoteCallbackList<IFmReceiverCallback>();
+
+ /* The current active fm app client name */
+ private String mClientName;
+
+ private boolean mIsStarted = false;
+ private boolean mIsFinish = false;
+ private Context mContext;
+ protected IBluetooth mService;
+
+ private int mFunctionalityMask;
+
+
+ // ******************************************************
+ // FM Queue mechanism - BEGIN
+ private static final int FM_BIND_SVC = 0;
+ private static final int FM_ON = 1;
+ private static final int FM_OFF = 2;
+ private static final int FM_TUNE_RADIO = 3;
+ private static final int FM_GET_STATUS = 4;
+ private static final int FM_MUTE_AUDIO = 5;
+ private static final int FM_SEEK_STATION = 6;
+ private static final int FM_SEEK_STATION_COMBO = 7;
+ private static final int FM_SEEK_RDS_STATION = 8;
+ private static final int FM_SEEK_STATION_ABORT = 9;
+ private static final int FM_SET_RDS_MODE = 10;
+ private static final int FM_SET_AUDIO_MODE = 11;
+ private static final int FM_SET_AUDIO_PATH = 12;
+ private static final int FM_SET_STEP_SIZE = 13;
+ private static final int FM_SET_WORLD_REGION = 14;
+ private static final int FM_ESTIMATE_NOISE_FLOOR_LEVEL = 15;
+ private static final int FM_SET_LIVE_AUDIO_POLLING = 16;
+ private static final int FM_SET_VOLUME = 17;
+ private static final int FM_CMD_ANY = 20;
+
+ private static String fmCmdToString(int what) {
+ switch (what) {
+ case FM_BIND_SVC: return "FM_BIND_SVC";
+ case FM_ON: return "FM_ON";
+ case FM_OFF: return "FM_OFF";
+ case FM_TUNE_RADIO: return "FM_TUNE_RADIO";
+ case FM_GET_STATUS: return "FM_GET_STATUS";
+ case FM_MUTE_AUDIO: return "FM_MUTE_AUDIo";
+ case FM_SEEK_STATION: return "FM_SEEK_STATION";
+ case FM_SEEK_STATION_COMBO: return "FM_SEEK_STATION_COMBO";
+ case FM_SEEK_RDS_STATION: return "FM_SEEK_RDS_STATION";
+ case FM_SET_RDS_MODE: return "FM_SET_RDS_MODE";
+ case FM_SET_AUDIO_MODE: return "FM_SET_AUDIO_MODE";
+ case FM_SET_AUDIO_PATH: return "FM_SET_AUDIO_PATH";
+ case FM_SET_STEP_SIZE: return "FM_SET_STEP_SIZE";
+ case FM_SET_WORLD_REGION: return "FM_SET_WORLD_REGION";
+ case FM_ESTIMATE_NOISE_FLOOR_LEVEL: return "FM_ESTIMATE_NOISE_FLOOR_LEVEL";
+ case FM_SET_LIVE_AUDIO_POLLING: return "FM_SET_LIVE_AUDIO_POLLING";
+ case FM_SET_VOLUME: return "FM_SET_VOLUME";
+ default: return "UNKNOWN COMMAND: " + what;
+ }
+ }
+
+ private class FM_Status_Params {
+ private int mStFreq;
+ private int mStRssi;
+ private int mStSnr;
+ private boolean mStRadioIsOn;
+ private int mStRdsProgramType;
+ private String mStRdsProgramService;
+ private String mStRdsRadioText;
+ private String mStRdsProgramTypeName;
+ private boolean mStIsMute;
+
+ public FM_Status_Params(int freq, int rssi, int snr, boolean radioIsOn,
+ int rdsProgramType, String rdsProgramService, String rdsRadioText,
+ String rdsProgramTypeName, boolean isMute)
+ {
+ mStFreq = freq;
+ mStRssi = rssi;
+ mStSnr = snr;
+ mStRadioIsOn = radioIsOn;
+ mStRdsProgramType = rdsProgramType;
+ mStRdsProgramService = rdsProgramService;
+ mStRdsRadioText = rdsRadioText;
+ mStRdsProgramTypeName = rdsProgramTypeName;
+ mStIsMute = isMute;
+ }
+ }
+
+ private class FM_Search_Params {
+ private int mStFreq;
+ private int mStRssi;
+ private int mStSnr;
+ private boolean mStSeekSuccess;
+
+ public FM_Search_Params(int freq, int rssi, int snr, boolean seekSuccess) {
+ mStFreq = freq;
+ mStRssi = rssi;
+ mStSnr = snr;
+ mStSeekSuccess = seekSuccess;
+ }
+ }
+
+ static class FMJob {
+ final int command;
+ // 0 means this command was not been sent to the bt framework.
+ long timeSent;
+ boolean b_arg1;
+ int arg1, arg2, arg3, arg4, arg5, arg6, arg7;
+
+ // Job without arg
+ public FMJob(int command) {
+ this.command = command;
+ this.timeSent = 0;
+ }
+
+ // Job with 1 arg
+ public FMJob(int command, int arg1) {
+ this.command = command;
+ this.timeSent = 0;
+ this.arg1 = arg1;
+ }
+
+ // Job with 2 args
+ public FMJob(int command, int arg1, int arg2) {
+ this.command = command;
+ this.timeSent = 0;
+ this.arg1 = arg1;
+ this.arg2 = arg2;
+ }
+
+ // Job with 3 args
+ public FMJob(int command, int arg1, int arg2, int arg3) {
+ this.command = command;
+ this.timeSent = 0;
+ this.arg1 = arg1;
+ this.arg2 = arg2;
+ this.arg3 = arg3;
+ }
+
+ // Job with 4 arg
+ public FMJob(int command, int arg1, int arg2, int arg3, int arg4) {
+ this.command = command;
+ this.timeSent = 0;
+ this.arg1 = arg1;
+ this.arg2 = arg2;
+ this.arg3 = arg3;
+ this.arg4 = arg4;
+ }
+
+ // Job with 1 boolean arg and 7 int arg, for the seekStationCombo function
+ public FMJob(int command, boolean b_arg1, int arg1, int arg2, int arg3,
+ int arg4, int arg5, int arg6, int arg7) {
+ this.command = command;
+ this.timeSent = 0;
+ this.b_arg1 = b_arg1;
+ this.arg1 = arg1;
+ this.arg2 = arg2;
+ this.arg3 = arg3;
+ this.arg4 = arg4;
+ this.arg5 = arg5;
+ this.arg6 = arg6;
+ this.arg7 = arg7;
+ }
+
+ // Job with 1 boolean arg
+ public FMJob(int command, boolean b_arg1) {
+ this.command = command;
+ this.timeSent = 0;
+ this.b_arg1 = b_arg1;
+ }
+
+ // Job with 2 arg, with arg1 is a boolean
+ public FMJob(int command, boolean b_arg1, int arg2) {
+ this.command = command;
+ this.timeSent = 0;
+ this.b_arg1 = b_arg1;
+ this.arg2 = arg2;
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(fmCmdToString(command));
+ sb.append(" TimeSent:");
+ if (timeSent == 0) {
+ sb.append("not yet");
+ } else {
+ sb.append(DateFormat.getTimeInstance().format(new Date(timeSent)));
+ }
+ return sb.toString();
+ }
+ }
+
+ private LinkedList<FMJob> mFmQueue = new LinkedList<FMJob>();
+ private int mCurrCmd = FM_CMD_ANY;
+
+ private void queueFMCommand(FMJob job) {
+ if (job == null)
+ return;
+ Log.d(TAG, "queueFMCommand: add [" + job + "] to queue " + mFmQueue.toString());
+
+ synchronized (mFmQueue) {
+ if (job.command == FM_OFF) {
+ // First check whether the current job is FM_OFF
+ FMJob firstJob = mFmQueue.peek();
+ if (firstJob != null) {
+ // If it is duplicated FM_OFF command, do nothing...
+ if (firstJob.command == FM_OFF) {
+ Log.d(TAG, "queueFMCommand: Ignore duplicated FM_OFF command...");
+ return;
+ }
+ // Remove any pending job(s) when received FM_OFF command
+ Iterator<FMJob> it = mFmQueue.iterator();
+ while (it.hasNext()) {
+ FMJob existingJob = it.next();
+ if (existingJob.timeSent == 0) {
+ Log.d(TAG, "queueFMCommand: " + existingJob + " removed due to FM_OFF");
+ it.remove();
+ }
+ }
+ }
+ }
+
+ // TBD: handle SEEK_ABORT here later
+
+ // Add the new command to the queue
+ mFmQueue.add(job);
+
+ // if there's nothing pending from before, send the command immediately.
+ if (mFmQueue.size() == 1) {
+ processCommands();
+ }
+ }
+ }
+
+ private boolean processCommand(FMJob job) {
+ int successful = FmProxy.STATUS_OK;
+
+ if (job.timeSent == 0) {
+ mCurrCmd = job.command;
+ job.timeSent = System.currentTimeMillis();
+ Log.d(TAG, "processCommand: [" + job + "]");
+
+ switch (job.command) {
+ case FM_BIND_SVC:
+ successful = FmProxy.STATUS_OK;
+ break;
+ case FM_ON:
+ successful = process_turnOnRadio(job.arg1);
+ break;
+ case FM_OFF:
+ successful = process_turnOffRadio();
+ break;
+ case FM_TUNE_RADIO:
+ successful = process_tuneRadio(job.arg1);
+ break;
+ case FM_GET_STATUS:
+ successful = process_getStatus();
+ break;
+ case FM_MUTE_AUDIO:
+ successful = process_muteAudio(job.b_arg1);
+ break;
+ case FM_SEEK_STATION:
+ successful = process_seekStation(job.arg1, job.arg2);
+ break;
+ case FM_SEEK_STATION_COMBO:
+ successful = process_seekStationCombo(job.b_arg1, job.arg1, job.arg2,
+ job.arg3, job.arg4, job.arg5, job.arg6, job.arg7);
+ break;
+ case FM_SEEK_RDS_STATION:
+ successful = process_seekRdsStation(job.arg1, job.arg2, job.arg3, job.arg4);
+ break;
+ case FM_SET_RDS_MODE:
+ successful = process_setRdsMode(job.arg1, job.arg2, job.arg3, job.arg4);
+ break;
+ case FM_SET_AUDIO_MODE:
+ successful = process_setAudioMode(job.arg1);
+ break;
+ case FM_SET_AUDIO_PATH:
+ successful = process_setAudioPath(job.arg1);
+ break;
+ case FM_SET_STEP_SIZE:
+ successful = process_setStepSize(job.arg1);
+ break;
+ case FM_SET_WORLD_REGION:
+ successful = process_setWorldRegion(job.arg1, job.arg2);
+ break;
+ case FM_ESTIMATE_NOISE_FLOOR_LEVEL:
+ try {
+ successful = process_estimateNoiseFloorLevel(job.arg1);
+ } catch (RemoteException e) {
+ successful = FmProxy.STATUS_SERVER_FAIL;
+ }
+ break;
+ case FM_SET_LIVE_AUDIO_POLLING:
+ try {
+ successful = process_setLiveAudioPolling( job.b_arg1, job.arg2);
+ } catch (RemoteException e) {
+ successful = FmProxy.STATUS_SERVER_FAIL;
+ }
+ break;
+ case FM_SET_VOLUME:
+ successful = process_setFMVolume(job.arg1);
+ break;
+ }
+
+ if (successful != FmProxy.STATUS_OK)
+ mCurrCmd = FM_CMD_ANY;
+ }
+ return (successful == FmProxy.STATUS_OK);
+ }
+
+ /* This method is called in 2 places:
+ * 1) queueCommand() - when someone or something want to send the FM commands
+ * 2) on receiving the notification from BTAPP
+ */
+ private void processCommands() {
+ Log.d(TAG, "processCommands: " + mFmQueue.toString());
+
+ Iterator<FMJob> it = mFmQueue.iterator();
+ while (it.hasNext()) {
+ FMJob job = it.next();
+ if (!processCommand(job)) {
+ // If the command failed immediately, there will be no event callbacks.
+ // So delete the job immediately and move on to the next one
+ it.remove();
+ }
+ }
+ }
+
+ private void fetchNextJob(int currJobCmd) {
+ Log.d(TAG, "fetchNextJob: currJobCmd = " + fmCmdToString(currJobCmd));
+ synchronized (mFmQueue) {
+ FMJob job = mFmQueue.peek();
+ if (job == null) {
+ return;
+ }
+ if (currJobCmd == FM_CMD_ANY || currJobCmd == job.command) {
+ Log.d(TAG, "fetchNextJob: remove completed job [" + job + "]");
+ mFmQueue.poll();
+ }
+ else {
+ Log.w(TAG, "fetchNextJob: currJob = " + currJobCmd +
+ ", but the current job on the queue is [" + job + "]");
+ }
+ processCommands();
+ }
+ }
+
+ /**
+ * Method to clear up the mFmQueue object and release it during application close
+ */
+ public void clearAllQueue() {
+ synchronized (mFmQueue) {
+ if (mFmQueue != null) {
+ Log.d(TAG, "clearAllQueue: mFmQueue = " + mFmQueue.toString());
+ mFmQueue.clear();
+ mFmQueue = null;
+ }
+ }
+ }
+
+ // FM Queue mechanism - END
+ // ******************************************************
+
+
+ // some gets and sets
+ public boolean getRadioIsOn() {
+ return FmReceiverServiceState.mRadioIsOn;
+ }
+
+ /**
+ * Returns the FM Audio Mode state.
+ * @param none
+ * @return {@link FmProxy#AUDIO_MODE_AUTO},
+ * {@link FmProxy#AUDIO_MODE_STEREO},
+ * {@link FmProxy#AUDIO_MODE_MONO}, or
+ * {@link FmProxy#AUDIO_MODE_BLEND}.
+ */
+ public synchronized int getMonoStereoMode() {
+ return FmReceiverServiceState.mAudioMode;
+ }
+
+ /**
+ * Returns the present tuned FM Frequency
+ * @param none
+ * @return Tuned frequency
+ */
+ public synchronized int getTunedFrequency() {
+ return FmReceiverServiceState.mFreq;
+ }
+
+ /**
+ * Returns whether MUTE is turned ON or OFF
+ * @param none
+ * @return false if MUTE is OFF ; true otherwise
+ */
+ public synchronized boolean getIsMute() {
+ return FmReceiverServiceState.mIsMute;
+ }
+
+ public void registerCallback(IFmReceiverCallback cb) throws RemoteException {
+ if (cb != null) {
+ mCallbacks.register(cb);
+ }
+ }
+
+ public synchronized void unregisterCallback(IFmReceiverCallback cb) throws RemoteException {
+ if (cb != null) {
+ mCallbacks.unregister(cb);
+ }
+ }
+
+ static {
+ classInitNative();
+ }
+ private native static void classInitNative();
+
+ public FmNativehandler(Context context) {
+ mContext = context;
+ }
+
+ public synchronized void start() {
+ if (V) Log.d(TAG, "start");
+ if (mIsStarted) {
+ Log.w(TAG,"Service already started. Skipping...");
+ return;
+ }
+
+ if (!mContext.bindService(new Intent(mContext, AdapterService.class),
+ mConnection, Context.BIND_AUTO_CREATE)) {
+ Log.e(TAG, "Could not bind to Bluetooth Service");
+ return;
+ }
+ queueFMCommand(new FMJob(FM_BIND_SVC));
+
+ mIsStarted = true;
+ }
+
+ public synchronized void stop() {
+ if (V) Log.d(TAG, "stop");
+ if (!mIsStarted) {
+ Log.d(TAG,"Service already stopped. Skipping...");
+ return;
+ }
+
+ unRegisterIntent();
+ cleanupNative();
+ mContext.unbindService(mConnection);
+ mService = null;
+ mIsStarted = false;
+ }
+
+ private native void cleanupNative();
+
+ public void finish() {
+ if (V) Log.d(TAG, "finish - cleanup Service here");
+ if (operationHandler != null) {
+ //Manually remove all messages to clean the MessageQueue of operationHandler
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_STATUS_EVENT_CALLBACK);
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_SEARCH_EVENT_CALLBACK);
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_RDS_EVENT_CALLBACK);
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_RDS_DATA_EVENT_CALLBACK);
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_AUDIO_MODE_EVENT_CALLBACK);
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_AUDIO_PATH_EVENT_CALLBACK);
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_REGION_EVENT_CALLBACK);
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_NFE_EVENT_CALLBACK);
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_LIVE_AUDIO_EVENT_CALLBACK);
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_VOLUME_EVENT_CALLBACK);
+
+ operationHandler.removeCallbacksAndMessages(null);
+ operationHandler = null;
+ }
+ clearAllQueue();
+ mCallbacks.kill(); //Ensure to clear off the callback loop
+ }
+
+ private ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName name, IBinder service) {
+ if (V) Log.d(TAG, "onServiceConnected() name = " + name);
+ mService = (IBluetooth) IBluetooth.Stub.asInterface(service);
+ fetchNextJob(FM_BIND_SVC);
+ }
+
+ public void onServiceDisconnected(ComponentName name) {
+ if (V) Log.d(TAG, "onServiceDisconnected()");
+ }
+ };
+
+ private void initializeStateMachine() {
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_OFF;
+ FmReceiverServiceState.mFreq = 0;
+ FmReceiverServiceState.mRssi = 127;
+ FmReceiverServiceState.mSnr = 127;
+ FmReceiverServiceState.mRadioIsOn = false;
+ FmReceiverServiceState.mRdsProgramType = 0;
+ FmReceiverServiceState.mRdsProgramService = "";
+ FmReceiverServiceState.mRdsRadioText = "";
+ FmReceiverServiceState.mRdsProgramTypeName = "";
+ FmReceiverServiceState.mIsMute = false;
+ FmReceiverServiceState.mSeekSuccess = false;
+ FmReceiverServiceState.mRdsOn = false;
+ FmReceiverServiceState.mAfOn = false;
+ FmReceiverServiceState.mRdsType = 0; // RDS
+ FmReceiverServiceState.mAlternateFreqHopThreshold = 0;
+ FmReceiverServiceState.mAudioMode = FmProxy.AUDIO_MODE_AUTO;
+ FmReceiverServiceState.mAudioPath = FmProxy.AUDIO_PATH_DIGITAL;
+ FmReceiverServiceState.mWorldRegion = FmProxy.FUNC_REGION_DEFAULT;
+ FmReceiverServiceState.mStepSize = FmProxy.FREQ_STEP_DEFAULT;
+ FmReceiverServiceState.mLiveAudioQuality = FmProxy.LIVE_AUDIO_QUALITY_DEFAULT;
+ FmReceiverServiceState.mEstimatedNoiseFloorLevel = FmProxy.NFL_DEFAULT;
+ FmReceiverServiceState.mSignalPollInterval = FmProxy.SIGNAL_POLL_INTERVAL_DEFAULT;
+ FmReceiverServiceState.mDeemphasisTime = FmProxy.DEEMPHASIS_TIME_DEFAULT;
+ FmReceiverServiceState.radio_op_state = FmReceiverServiceState.RADIO_OP_STATE_NONE;
+ }
+
+ /**
+ * This class handles operation lock timeouts.
+ */
+ protected Handler operationHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ /* Check if the current operation can be rescued. */
+ switch (msg.what) {
+ case FmReceiverServiceState.OPERATION_TIMEOUT:
+ /* Currently assume that any timeout is catastrophic. */
+ Log.w(TAG, "handleMessage: OPERATION_TIMEOUT on " + fmCmdToString(msg.arg1));
+ switch (msg.arg1) {
+ case FM_ON:
+ /* Could not start radio. Reset state machine. */
+ initializeStateMachine();
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_OFF;
+ break;
+ case FM_OFF:
+ initializeStateMachine();
+ try {
+ disableFmNative(true);
+ } catch (Exception e) { }
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_OFF;
+ break;
+ case FM_SEEK_STATION:
+ case FM_SEEK_STATION_COMBO:
+ case FM_SEEK_RDS_STATION:
+ /* Send search complet update to client. */
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND;
+ FmReceiverServiceState.mSeekSuccess = true;
+ sendSeekCompleteEventCallback(FmReceiverServiceState.mFreq,
+ FmReceiverServiceState.mRssi,
+ FmReceiverServiceState.mSnr,
+ FmReceiverServiceState.mSeekSuccess,
+ msg.arg1, 1);
+ break;
+ default:
+ Log.w(TAG, "handleMessage: Unknown OPERATION_TIMEOUT " + fmCmdToString(msg.arg1));
+ /* Send status update to client. */
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND;
+ sendStatusEventCallback(FmReceiverServiceState.mFreq,
+ FmReceiverServiceState.mRssi,
+ FmReceiverServiceState.mSnr,
+ FmReceiverServiceState.mRadioIsOn,
+ FmReceiverServiceState.mRdsProgramType,
+ FmReceiverServiceState.mRdsProgramService,
+ FmReceiverServiceState.mRdsRadioText,
+ FmReceiverServiceState.mRdsProgramTypeName,
+ FmReceiverServiceState.mIsMute,
+ msg.arg1, 1);
+ break;
+ }
+ break;
+ case FmReceiverServiceState.OPERATION_STATUS_EVENT_CALLBACK:
+ Log.d(TAG, "handleMessage: OPERATION_STATUS_EVENT_CALLBACK");
+ {
+ FM_Status_Params st = (FM_Status_Params) msg.obj;
+ sendStatusEventCallback(st.mStFreq, st.mStRssi, st.mStSnr,
+ st.mStRadioIsOn, st.mStRdsProgramType,
+ st.mStRdsProgramService, st.mStRdsRadioText,
+ st.mStRdsProgramTypeName, st.mStIsMute,
+ msg.arg1, msg.arg2);
+ }
+ break;
+ case FmReceiverServiceState.OPERATION_SEARCH_EVENT_CALLBACK:
+ Log.d(TAG, "handleMessage: OPERATION_SEARCH_EVENT_CALLBACK");
+ {
+ FM_Search_Params st = (FM_Search_Params) msg.obj;
+ sendSeekCompleteEventCallback(st.mStFreq, st.mStRssi, st.mStSnr,
+ st.mStSeekSuccess, msg.arg1, msg.arg2);
+ }
+ break;
+ case FmReceiverServiceState.OPERATION_RDS_EVENT_CALLBACK:
+ Log.d(TAG, "handleMessage: OPERATION_RDS_EVENT_CALLBACK");
+ sendRdsModeEventCallback(msg.arg1, msg.arg2);
+ break;
+ case FmReceiverServiceState.OPERATION_RDS_DATA_EVENT_CALLBACK:
+ Log.d(TAG, "handleMessage: OPERATION_RDS_DATA_EVENT_CALLBACK");
+ sendRdsDataEventCallback(msg.arg1, msg.arg2, (String) msg.obj);
+ break;
+ case FmReceiverServiceState.OPERATION_AUDIO_MODE_EVENT_CALLBACK:
+ Log.d(TAG, "handleMessage: OPERATION_AUDIO_MODE_EVENT_CALLBACK");
+ sendAudioModeEventCallback(msg.arg1);
+ break;
+ case FmReceiverServiceState.OPERATION_AUDIO_PATH_EVENT_CALLBACK:
+ Log.d(TAG, "handleMessage: OPERATION_AUDIO_PATH_EVENT_CALLBACK");
+ sendAudioPathEventCallback(msg.arg1);
+ break;
+ case FmReceiverServiceState.OPERATION_REGION_EVENT_CALLBACK:
+ Log.d(TAG, "handleMessage: OPERATION_REGION_EVENT_CALLBACK");
+ sendWorldRegionEventCallback(msg.arg1);
+ break;
+ case FmReceiverServiceState.OPERATION_NFE_EVENT_CALLBACK:
+ Log.d(TAG, "handleMessage: OPERATION_NFE_EVENT_CALLBACK");
+ sendEstimateNflEventCallback(msg.arg1);
+ break;
+ case FmReceiverServiceState.OPERATION_LIVE_AUDIO_EVENT_CALLBACK:
+ Log.d(TAG, "handleMessage: OPERATION_LIVE_AUDIO_EVENT_CALLBACK");
+ sendLiveAudioQualityEventCallback(msg.arg1, msg.arg2);
+ break;
+ case FmReceiverServiceState.OPERATION_VOLUME_EVENT_CALLBACK:
+ Log.d(TAG, "handleMessage: OPERATION_VOLUME_EVENT_CALLBACK");
+ sendVolumeEventCallback(msg.arg1, msg.arg2);
+ break;
+ default:
+ Log.w(TAG, "handleMessage: Unknown message: " + msg.what);
+ break;
+ }
+ }
+ };
+
+ /**
+ * this registration for intents are to get notified when the client app process
+ * gets killed and for intents when radio state changed.
+ */
+ private void registerIntent() {
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
+ intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
+ intentFilter.addDataScheme("package");
+ mContext.registerReceiver(mIntentReceiver, intentFilter);
+ }
+
+ private void unRegisterIntent() {
+ try {
+ mContext.unregisterReceiver(mIntentReceiver);
+ } catch (IllegalArgumentException ei) {
+ ;
+ } catch (Exception e) {
+ Log.e(TAG, "unRegisterIntent failed", e);
+ }
+ mClientName = null;
+ }
+
+ private BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ String action = intent.getAction();
+ Log.d(TAG, action);
+ if (action.equals(Intent.ACTION_PACKAGE_REMOVED) ||
+ action.equals(Intent.ACTION_PACKAGE_RESTARTED)) {
+
+ Uri uri = intent.getData();
+ if (uri == null) {
+ return;
+ }
+ String pkgName = uri.getSchemeSpecificPart();
+ if (pkgName == null) {
+ return;
+ }
+ Log.d(TAG,pkgName);
+
+ // do the required clean up if it is our client killed
+ if (pkgName.equals(mClientName)) {
+ turnOffRadio();
+ }
+ }
+ }
+ };
+
+ /**
+ * Turns on the radio and plays audio using the specified functionality mask.
+ * <p>
+ * After executing this function, the application should wait for a confirmation
+ * status event callback before calling further API functions. Furthermore,
+ * applications should call the {@link #turnOffRadio()} function before shutting down.
+ *
+ * @param functionalityMask
+ * is a bitmask comprised of one or more of the following fields:
+ * {@link FmProxy#FUNC_REGION_NA},
+ * {@link FmProxy#FUNC_REGION_JP},
+ * {@link FmProxy#FUNC_REGION_JP_II},
+ * {@link FmProxy#FUNC_REGION_EUR},
+ * {@link FmProxy#FUNC_RDS}, {@link FmProxy#FUNC_RDBS} and
+ * {@link FmProxy#FUNC_AF}
+ *
+ * @param clientPackagename
+ * is the the client application package name , this is required for the
+ * fm service to clean up it state when the client process gets killed
+ * eg scenario: when client app dies without calling turnOffRadio()
+ *
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error code.
+ */
+ public synchronized int turnOnRadio(int functionalityMask, char[] clientPackagename) {
+ mClientName = String.copyValueOf(clientPackagename);
+
+ if (V) Log.d(TAG, "turnOnRadio: functionalityMask = " + functionalityMask +
+ ", clientPackagename = " + mClientName);
+
+ /* Sanity check of parameters. */
+ int requestedRegion = functionalityMask & FmReceiverServiceState.FUNC_REGION_BITMASK;
+ int requestedRdsFeatures = functionalityMask & FmReceiverServiceState.FUNC_RDS_BITMASK;
+ if (requestedRegion != FmProxy.FUNC_REGION_EUR &&
+ requestedRegion != FmProxy.FUNC_REGION_JP &&
+ requestedRegion != FmProxy.FUNC_REGION_JP_II &&
+ requestedRegion != FmProxy.FUNC_REGION_NA)
+ {
+ Log.e(TAG, "turnOnRadio: Illegal requestedRegion = " + requestedRegion);
+ return FmProxy.STATUS_ILLEGAL_PARAMETERS;
+ }
+ if ((requestedRdsFeatures & FmProxy.FUNC_RDS) != 0 &&
+ (requestedRdsFeatures & FmProxy.FUNC_RBDS) != 0)
+ {
+ Log.e(TAG, "turnOnRadio: Illegal requestedRdsFeatures = " + requestedRdsFeatures);
+ return FmProxy.STATUS_ILLEGAL_PARAMETERS;
+ }
+
+ // Register to get notified if client app process gets killed
+ registerIntent();
+
+ mFunctionalityMask = functionalityMask;
+ queueFMCommand(new FMJob(FM_ON, functionalityMask));
+ return FmProxy.STATUS_OK;
+ }
+
+ private int process_turnOnRadio(int functionalityMask) {
+ if (V) Log.d(TAG, "process_turnOnRadio: functionalityMask = " + functionalityMask);
+
+ if (FmReceiverServiceState.mRadioIsOn) {
+ // One possible scenario to get here is the user switches to another application
+ // when FM is being turn on by the FM service. When the user switches back to
+ // the FM application, the FM service is already on. Update the local cache.
+ sendStatusEventCallbackFromLocalStore(FM_ON, true);
+ return FmProxy.STATUS_OK;
+ }
+ if (FmReceiverServiceState.RADIO_STATE_OFF != FmReceiverServiceState.radio_state) {
+ Log.w(TAG, "process_turnOnRadio: STATE = " + FmReceiverServiceState.radio_state);
+ return FmProxy.STATUS_ILLEGAL_COMMAND;
+ }
+
+ initializeNative();
+
+ /* Set timer to check that this does not lock the state machine. */
+ Message msg = Message.obtain();
+ msg.what = FmReceiverServiceState.OPERATION_TIMEOUT;
+ msg.arg1 = FM_ON;
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+ operationHandler.sendMessageDelayed(msg,
+ FmReceiverServiceState.OPERATION_TIMEOUT_STARTUP);
+
+ int returnStatus = FmProxy.STATUS_OK;
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_STARTING;
+ functionalityMask &= FmReceiverServiceState.FUNC_BITMASK;
+ try {
+ if (!enableFmNative(functionalityMask))
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ } catch (Exception e) {
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ Log.e(TAG, "process_turnOnRadio: enableFmNative failed", e);
+ }
+ if (returnStatus != FmProxy.STATUS_OK)
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ return returnStatus;
+ }
+ private native void initializeNative();
+ private native boolean enableFmNative(int functionalityMask);
+
+ /**
+ * Turns off the radio.
+ * <p>
+ * After executing this function, the application should wait for a
+ * confirmatory status event callback before shutting down.
+ *
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error code.
+ */
+ public synchronized int turnOffRadio() {
+ if (V) Log.d(TAG, "turnOffRadio:");
+ queueFMCommand(new FMJob(FM_OFF));
+ return FmProxy.STATUS_OK;
+ }
+
+ private int process_turnOffRadio() {
+ if (V) Log.d(TAG, "process_turnOffRadio:");
+
+ /* Set timer to check that this does not lock the state machine. */
+ Message msg = Message.obtain();
+ msg.what = FmReceiverServiceState.OPERATION_TIMEOUT;
+ msg.arg1 = FM_OFF;
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+ operationHandler.sendMessageDelayed(msg,
+ FmReceiverServiceState.OPERATION_TIMEOUT_SHUTDOWN);
+
+ int returnStatus = FmProxy.STATUS_OK;
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_STOPPING;
+ try {
+ if (!disableFmNative(false))
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ } catch (Exception e) {
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ Log.e(TAG, "process_turnOffRadio: disableFmNative failed", e);
+ }
+
+ if (returnStatus != FmProxy.STATUS_OK)
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ // State machine is reset immediately since there is risk of stack failure
+ // which would lock state machine in STOPPING state.
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_OFF;
+
+ return returnStatus;
+ }
+ private native boolean disableFmNative(boolean bForcing);
+
+ /**
+ * Initiates forced clean-up of FMReceiverService from the application.
+ *
+ * @return STATUS_OK
+ */
+ public synchronized int cleanupFmService() {
+ onRadioOffEvent(FmReceiverServiceState.BTA_FM_OK);
+ return FmProxy.STATUS_OK;
+ }
+
+ /**
+ * Tunes the radio to a specific frequency. If successful results in a
+ * status event callback.
+ *
+ * @param freq
+ * the frequency to tune to.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error code.
+ */
+ public synchronized int tuneRadio(int freq) {
+ if (V) Log.d(TAG, "tuneRadio: freq = " + freq);
+
+ /* Sanity check of parameters. */
+ if (freq < FmReceiverServiceState.MIN_ALLOWED_FREQUENCY_CODE ||
+ freq > FmReceiverServiceState.MAX_ALLOWED_FREQUENCY_CODE)
+ {
+ Log.e(TAG, "tuneRadio: Illegal freq = " + freq);
+ return FmProxy.STATUS_ILLEGAL_PARAMETERS;
+ }
+
+ queueFMCommand(new FMJob(FM_TUNE_RADIO, freq));
+ return FmProxy.STATUS_OK;
+ }
+
+ private int process_tuneRadio(int freq) {
+ if (V) Log.d(TAG, "process_tuneRadio: freq = " + freq);
+
+ if (FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND != FmReceiverServiceState.radio_state) {
+ Log.w(TAG, "process_tuneRadio: STATE = " + FmReceiverServiceState.radio_state);
+ return FmProxy.STATUS_ILLEGAL_COMMAND;
+ }
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_BUSY;
+
+ /* Set timer to check that this does not lock the state machine. */
+ Message msg = Message.obtain();
+ msg.what = FmReceiverServiceState.OPERATION_TIMEOUT;
+ msg.arg1 = FM_TUNE_RADIO;
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+ operationHandler.sendMessageDelayed(msg,
+ FmReceiverServiceState.OPERATION_TIMEOUT_SEARCH);
+
+ int returnStatus = FmProxy.STATUS_OK;
+ try {
+ if (!tuneNative(freq))
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ } catch (Exception e) {
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ Log.e(TAG, "process_tuneRadio: tuneNative failed", e);
+ }
+ if (returnStatus != FmProxy.STATUS_OK)
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ return returnStatus;
+ }
+ private native boolean tuneNative(int freq);
+
+ /**
+ * Gets current radio status. This results in a status event callback.
+ *
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error code.
+ */
+ public synchronized int getStatus() {
+ if (V) Log.d(TAG, "getStatus:");
+ queueFMCommand(new FMJob(FM_GET_STATUS));
+ return FmProxy.STATUS_OK;
+ }
+
+ private int process_getStatus() {
+ if (V) Log.d(TAG, "process_getStatus:");
+
+ if (FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND != FmReceiverServiceState.radio_state) {
+ Log.w(TAG, "process_getStatus: STATE = " + FmReceiverServiceState.radio_state);
+ return FmProxy.STATUS_ILLEGAL_COMMAND;
+ }
+
+ /* Return latest known data. */
+ sendStatusEventCallbackFromLocalStore(FM_GET_STATUS, false);
+ return FmProxy.STATUS_OK;
+ }
+
+ /**
+ * Mutes/unmutes radio audio. If muted the hardware will stop sending audio.
+ * This results in a status event callback.
+ *
+ * @param mute
+ * TRUE to mute audio, FALSE to unmute audio.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error code.
+ */
+ public synchronized int muteAudio(boolean mute) {
+ if (V) Log.d(TAG, "muteAudio: mute = " + mute);
+ queueFMCommand(new FMJob(FM_MUTE_AUDIO, mute));
+ return FmProxy.STATUS_OK;
+ }
+
+ private int process_muteAudio(boolean mute) {
+ if (V) Log.d(TAG, "process_muteAudio: mute = " + mute);
+
+ if (FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND != FmReceiverServiceState.radio_state) {
+ Log.w(TAG, "process_muteAudio: STATE = " + FmReceiverServiceState.radio_state);
+ return FmProxy.STATUS_ILLEGAL_COMMAND;
+ }
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_BUSY;
+
+ /* Set timer to check that this does not lock the state machine. */
+ Message msg = Message.obtain();
+ msg.what = FmReceiverServiceState.OPERATION_TIMEOUT;
+ msg.arg1 = FM_MUTE_AUDIO;
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+ operationHandler.sendMessageDelayed(msg,
+ FmReceiverServiceState.OPERATION_TIMEOUT_GENERIC);
+
+ int returnStatus = FmProxy.STATUS_OK;
+ try {
+ if (!muteNative(mute))
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ } catch (Exception e) {
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ Log.e(TAG, "process_muteAudio: muteNative failed", e);
+ }
+ if (returnStatus != FmProxy.STATUS_OK)
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ return returnStatus;
+ }
+ private native boolean muteNative(boolean mute);
+
+ /**
+ * Scans FM toward higher/lower frequency for next clear channel. Will
+ * result in a seek complete event callback.
+ *
+ * @param scanMode
+ * see {@link FmProxy#SCAN_MODE_NORMAL},
+ * {@link FmProxy#SCAN_MODE_DOWN},
+ * {@link FmProxy#SCAN_MODE_UP} and
+ * {@link FmProxy#SCAN_MODE_FULL}.
+ * @param minSignalStrength
+ * minimum signal strength, default =
+ * {@link FmProxy#MIN_SIGNAL_STRENGTH_DEFAULT}
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error code.
+ */
+ public synchronized int seekStation(int scanMode, int minSignalStrength) {
+ if (V) Log.d(TAG, "seekStation: scanMode = " + scanMode + ", minSignalStrength = " + minSignalStrength);
+
+ /* Sanity check of parameters. */
+ if (minSignalStrength < FmReceiverServiceState.MIN_ALLOWED_SIGNAL_STRENGTH_NUMBER ||
+ minSignalStrength > FmReceiverServiceState.MAX_ALLOWED_SIGNAL_STRENGTH_NUMBER)
+ {
+ Log.e(TAG, "seekStation: Illegal minSignalStrength = " + minSignalStrength);
+ return FmProxy.STATUS_ILLEGAL_PARAMETERS;
+ }
+ if (scanMode != FmProxy.SCAN_MODE_DOWN && scanMode != FmProxy.SCAN_MODE_UP &&
+ scanMode != FmProxy.SCAN_MODE_FAST && scanMode != FmProxy.SCAN_MODE_FULL)
+ {
+ Log.e(TAG, "seekStation: Illegal scanMode = " + scanMode);
+ return FmProxy.STATUS_ILLEGAL_PARAMETERS;
+ }
+
+ queueFMCommand(new FMJob(FM_SEEK_STATION, scanMode, minSignalStrength));
+ return FmProxy.STATUS_OK;
+ }
+
+ private int process_seekStation(int scanMode, int minSignalStrength) {
+ if (V) Log.d(TAG, "process_seekStation: scanMode = " + scanMode + ", minSignalStrength = " + minSignalStrength);
+
+ /* Sanity check of parameters. */
+ if (FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND != FmReceiverServiceState.radio_state) {
+ Log.w(TAG, "process_seekStation: STATE = " + FmReceiverServiceState.radio_state);
+ return FmProxy.STATUS_ILLEGAL_COMMAND;
+ }
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_BUSY;
+
+ /* Set timer to check that this does not lock the state machine. */
+ Message msg = Message.obtain();
+ msg.what = FmReceiverServiceState.OPERATION_TIMEOUT;
+ msg.arg1 = FM_SEEK_STATION;
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+ operationHandler.sendMessageDelayed(msg,
+ FmReceiverServiceState.OPERATION_TIMEOUT_SEARCH);
+
+ int returnStatus = FmProxy.STATUS_OK;
+ scanMode = scanMode & FmReceiverServiceState.SCAN_MODE_BITMASK;
+ try {
+ if (!searchNative(scanMode, minSignalStrength, FmProxy.RDS_COND_NONE, 0))
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ } catch (Exception e) {
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ Log.e(TAG, "process_seekStation: searchNative failed", e);
+ }
+ if (returnStatus != FmProxy.STATUS_OK)
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ return returnStatus;
+ }
+
+ /**
+ * Scans FM toward higher/lower frequency for next clear channel depending on the
+ * scanDirection. Will do wrap around when reached to mMaxFreq/mMinFreq,
+ * when no wrap around is needed, use the low_bound or high_bound as endFrequency.
+ * Will result in a seek complete event callback.
+ *
+ * @param startFrequency
+ * Starting frequency of search operation range.
+ * @param endFrequency
+ * Ending frequency of search operation
+ * @param minSignalStrength
+ * Minimum signal strength, default =
+ * {@link #MIN_SIGNAL_STRENGTH_DEFAULT}
+ * @param scanDirection
+ * the direction to search in, it can only be either
+ * {@link #SCAN_MODE_UP} and {@link #SCAN_MODE_DOWN}.
+ * @param scanMethod
+ * see {@link #SCAN_MODE_NORMAL}, {@link #SCAN_MODE_FAST},
+ * @param multi_channel
+ * Is multiple channels are required, or only find next valid channel(seek).
+ * @param rdsType
+ * the type of RDS condition to scan for.
+ * @param rdsTypeValue
+ * the condition value to match.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error code.
+ */
+ public synchronized int seekStationCombo(int startFreq, int endFreq,
+ int minSignalStrength, int direction, int scanMethod,
+ boolean multiChannel, int rdsType, int rdsTypeValue) {
+ if (V) Log.d(TAG, "seekStationCombo: startFreq = " + startFreq +
+ ", endFreq = " + endFreq +
+ ", minSignalStrength = " + minSignalStrength +
+ ", direction = " + direction + ", scanMethod = " + scanMethod +
+ ", multiChannel = " + multiChannel + ", rdsType = " + rdsType +
+ ", rdsTypeValue = " + rdsTypeValue);
+
+ /* Sanity check of parameters. */
+ if (minSignalStrength < FmReceiverServiceState.MIN_ALLOWED_SIGNAL_STRENGTH_NUMBER ||
+ minSignalStrength > FmReceiverServiceState.MAX_ALLOWED_SIGNAL_STRENGTH_NUMBER)
+ {
+ Log.e(TAG, "seekStationCombo: Illegal minSignalStrength = " + minSignalStrength);
+ return FmProxy.STATUS_ILLEGAL_PARAMETERS;
+ }
+
+ queueFMCommand(new FMJob(FM_SEEK_STATION_COMBO, multiChannel,
+ startFreq, endFreq, minSignalStrength, direction,
+ scanMethod, rdsType, rdsTypeValue));
+ return FmProxy.STATUS_OK;
+ }
+
+ private int process_seekStationCombo(boolean multiChannel, int startFreq, int endFreq,
+ int minSignalStrength, int direction, int scanMethod, int rdsType, int rdsTypeValue) {
+ if (V) Log.d(TAG, "process_seekStationCombo: startFreq = " + startFreq +
+ ", endFreq = " + endFreq +
+ ", minSignalStrength = " + minSignalStrength +
+ ", direction = " + direction + ", scanMethod = " + scanMethod +
+ ", multiChannel = " + multiChannel + ", rdsType = " + rdsType +
+ ", rdsTypeValue = " + rdsTypeValue);
+
+ if (FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND != FmReceiverServiceState.radio_state) {
+ Log.w(TAG, "process_seekStationCombo: STATE = " + FmReceiverServiceState.radio_state);
+ return FmProxy.STATUS_ILLEGAL_COMMAND;
+ }
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_BUSY;
+
+ /* Set timer to check that this does not lock the state machine. */
+ Message msg = Message.obtain();
+ msg.what = FmReceiverServiceState.OPERATION_TIMEOUT;
+ msg.arg1 = FM_SEEK_STATION_COMBO;
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+ operationHandler.sendMessageDelayed(msg,
+ FmReceiverServiceState.OPERATION_TIMEOUT_SEARCH);
+
+ int returnStatus = FmProxy.STATUS_OK;
+ try {
+ if (!comboSearchNative(startFreq, endFreq, minSignalStrength, direction,
+ scanMethod, multiChannel, rdsType, rdsTypeValue))
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ } catch (Exception e) {
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ Log.e(TAG, "process_seekStationCombo: comboSearchNative failed", e);
+ }
+ if (returnStatus != FmProxy.STATUS_OK)
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ return returnStatus;
+ }
+ private native boolean comboSearchNative(int startFreq, int endFreq, int rssiThreshold,
+ int direction, int scanMethod, boolean multiChannel, int rdsType, int rdsTypeValue);
+
+ /**
+ * Scans FM toward higher/lower frequency for next clear channel. Will
+ * result in a seek complete event callback. Will seek out a station that
+ * matches the requested value for the desired RDS functionality support.
+ *
+ * @param scanMode
+ * see {@link FmProxy#SCAN_MODE_NORMAL},
+ * {@link FmProxy#SCAN_MODE_DOWN},
+ * {@link FmProxy#SCAN_MODE_UP} and
+ * {@link FmProxy#SCAN_MODE_FULL}.
+ * @param minSignalStrength
+ * Minimum signal strength, default =
+ * {@link FmProxy#MIN_SIGNAL_STRENGTH_DEFAULT}
+ * @param rdsCondition
+ * the type of RDS condition to scan for.
+ * @param rdsValue
+ * the condition value to match.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error code.
+ */
+ public synchronized int seekRdsStation(int scanMode, int minSignalStrength,
+ int rdsCondition, int rdsValue) {
+ if (V) Log.d(TAG, "seekRdsStation: scanMode = " + scanMode +
+ ", minSignalStrength = " + minSignalStrength +
+ ", rdsCondition = " + rdsCondition + ", rdsValue = " + rdsValue);
+
+ /* Sanity check of parameters. */
+ if (minSignalStrength < FmReceiverServiceState.MIN_ALLOWED_SIGNAL_STRENGTH_NUMBER ||
+ minSignalStrength > FmReceiverServiceState.MAX_ALLOWED_SIGNAL_STRENGTH_NUMBER)
+ {
+ Log.e(TAG, "seekRdsStation: Illegal minSignalStrength = " + minSignalStrength);
+ return FmProxy.STATUS_ILLEGAL_PARAMETERS;
+ }
+ if (rdsValue < FmReceiverServiceState.MIN_ALLOWED_RDS_CONDITION_VALUE ||
+ rdsValue > FmReceiverServiceState.MAX_ALLOWED_RDS_CONDITION_VALUE)
+ {
+ Log.e(TAG, "seekRdsStation: Illegal rdsValue = " + rdsValue);
+ return FmProxy.STATUS_ILLEGAL_PARAMETERS;
+ }
+ if (rdsCondition != FmProxy.RDS_COND_NONE && rdsCondition != FmProxy.RDS_COND_PTY &&
+ rdsCondition != FmProxy.RDS_COND_TP)
+ {
+ Log.e(TAG, "seekRdsStation: Illegal rdsCondition = " + rdsCondition);
+ return FmProxy.STATUS_ILLEGAL_PARAMETERS;
+ }
+
+ queueFMCommand(new FMJob(FM_SEEK_RDS_STATION, scanMode, minSignalStrength,
+ rdsCondition, rdsValue));
+ return FmProxy.STATUS_OK;
+ }
+
+ private int process_seekRdsStation(int scanMode, int minSignalStrength,
+ int rdsCondition, int rdsValue) {
+ if (V) Log.d(TAG, "process_seekRdsStation: scanMode = " + scanMode +
+ ", minSignalStrength = " + minSignalStrength +
+ ", rdsCondition = " + rdsCondition + ", rdsValue = " + rdsValue);
+
+ if (FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND != FmReceiverServiceState.radio_state) {
+ Log.w(TAG, "process_seekRdsStation: STATE = " + FmReceiverServiceState.radio_state);
+ return FmProxy.STATUS_ILLEGAL_COMMAND;
+ }
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_BUSY;
+
+ /* Set timer to check that this does not lock the state machine. */
+ Message msg = Message.obtain();
+ msg.what = FmReceiverServiceState.OPERATION_TIMEOUT;
+ msg.arg1 = FM_SEEK_RDS_STATION;
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+ operationHandler.sendMessageDelayed(msg,
+ FmReceiverServiceState.OPERATION_TIMEOUT_SEARCH);
+
+ int returnStatus = FmProxy.STATUS_OK;
+ scanMode &= FmReceiverServiceState.SCAN_MODE_BITMASK;
+ try {
+ if (!searchNative(scanMode, minSignalStrength, rdsCondition, rdsValue))
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ } catch (Exception e) {
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ Log.e(TAG, "process_seekRdsStation: searchNative failed", e);
+ }
+ if (returnStatus != FmProxy.STATUS_OK)
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ return returnStatus;
+ }
+ private native boolean searchNative(int scanMode, int rssiThreshold, int condVal, int condType);
+
+ /**
+ * Aborts the current station seeking operation if any. Will result in a
+ * seek complete event containing the last scanned frequency.
+ * <p>
+ *
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error code.
+ */
+ public synchronized int seekStationAbort() {
+ if (V) Log.d(TAG, "seekStationAbort:");
+
+ int returnStatus = FmProxy.STATUS_ILLEGAL_COMMAND;
+ if (mCurrCmd == FM_SEEK_STATION || mCurrCmd == FM_SEEK_STATION_COMBO ||
+ mCurrCmd == FM_SEEK_RDS_STATION)
+ {
+ try {
+ if (!searchAbortNative())
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ } catch (Exception e) {
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ Log.e(TAG, "seekStationAbort: searchAbortNative failed", e);
+ }
+ }
+ return returnStatus;
+ }
+ private native boolean searchAbortNative();
+
+ /**
+ * Enables/disables RDS/RDBS feature and AF algorithm. Will result in an RDS
+ * mode event callback.
+ * <p>
+ *
+ * @param rdsMode
+ * Turns on the RDS or RBDS. See {@link FmProxy#RDS_MODE_OFF},
+ * {@link FmProxy#RDS_MODE_DEFAULT_ON},
+ * {@link FmProxy#RDS_MODE_RDS_ON},
+ * {@link FmProxy#RDS_MODE_RDBS_ON}
+ * @param rdsFields
+ * the mask specifying which types of RDS data to signal back.
+ * @param afmode
+ * enables AF algorithm if True. Disables it if False
+ * @param afThreshold
+ * the RSSI threshold when the AF should jump frequencies.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error code.
+ */
+ public synchronized int setRdsMode(int rdsMode, int rdsFeatures, int afMode, int afThreshold) {
+ if (V) Log.d(TAG, "setRdsMode: rdsMode = " + rdsMode + ", rdsFeatures = " +
+ rdsFeatures + ", afMode = " + afMode + ", afThreshold = " + afThreshold);
+
+ /* Sanity check of parameters. */
+ if (afThreshold < FmReceiverServiceState.MIN_ALLOWED_AF_JUMP_THRESHOLD ||
+ afThreshold > FmReceiverServiceState.MAX_ALLOWED_AF_JUMP_THRESHOLD)
+ {
+ Log.e(TAG, "seekStation: Illegal afThreshold = " + afThreshold);
+ return FmProxy.STATUS_ILLEGAL_PARAMETERS;
+ }
+
+ queueFMCommand(new FMJob(FM_SET_RDS_MODE, rdsMode, rdsFeatures, afMode, afThreshold));
+ return FmProxy.STATUS_OK;
+ }
+
+ private int process_setRdsMode(int rdsMode, int rdsFeatures,
+ int afMode, int afThreshold) {
+ if (V) Log.d(TAG, "process_setRdsMode: rdsMode = " + rdsMode + ", rdsFeatures = " +
+ rdsFeatures + ", afMode = " + afMode + ", afThreshold = " + afThreshold);
+
+ if (FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND != FmReceiverServiceState.radio_state) {
+ Log.w(TAG, "process_setRdsMode: STATE = " + FmReceiverServiceState.radio_state);
+ return FmProxy.STATUS_ILLEGAL_COMMAND;
+ }
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_BUSY;
+ FmReceiverServiceState.radio_op_state = FmReceiverServiceState.RADIO_OP_STATE_NONE;
+
+ /* Set timer to check that this does not lock the state machine. */
+ Message msg = Message.obtain();
+ msg.what = FmReceiverServiceState.OPERATION_TIMEOUT;
+ msg.arg1 = FM_SET_RDS_MODE;
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+ operationHandler.sendMessageDelayed(msg,
+ FmReceiverServiceState.OPERATION_TIMEOUT_GENERIC);
+
+ int returnStatus = FmProxy.STATUS_OK;
+ rdsMode &= FmReceiverServiceState.RDS_MODE_BITMASK;
+ afMode &= FmReceiverServiceState.AF_MODE_BITMASK;
+ rdsFeatures &= FmReceiverServiceState.RDS_FEATURES_BITMASK;
+ boolean rdsOnNative = (rdsMode != FmProxy.RDS_MODE_OFF);
+ boolean afOnNative = (afMode != FmProxy.AF_MODE_OFF);
+ int rdsModeNative = rdsMode & FmReceiverServiceState.RDS_RBDS_BITMASK;
+ try {
+ if (!setRdsModeNative(rdsOnNative, afOnNative, rdsModeNative))
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ } catch (Exception e) {
+ Log.e(TAG, "process_setRdsMode: setRdsModeNative failed", e);
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ }
+ if (returnStatus != FmProxy.STATUS_OK)
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ return returnStatus;
+ }
+ private native boolean setRdsModeNative(boolean rdsOn, boolean afOn, int rdsType);
+
+ /**
+ * Configures FM audio mode to be mono, stereo or blend. Will result in an
+ * audio mode event callback.
+ *
+ * @param audioMode
+ * the audio mode such as stereo or mono. The following values
+ * should be used {@link FmProxy#AUDIO_MODE_AUTO},
+ * {@link FmProxy#AUDIO_MODE_STEREO},
+ * {@link FmProxy#AUDIO_MODE_MONO} or
+ * {@link FmProxy#AUDIO_MODE_BLEND}.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error code.
+ */
+ public synchronized int setAudioMode(int audioMode) {
+ if (V) Log.d(TAG, "setAudioMode: audioMode = " + audioMode);
+
+ queueFMCommand(new FMJob(FM_SET_AUDIO_MODE, audioMode));
+ return FmProxy.STATUS_OK;
+ }
+
+ private int process_setAudioMode(int audioMode) {
+ if (V) Log.d(TAG, "process_setAudioMode: audioMode = " + audioMode);
+
+ if (FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND != FmReceiverServiceState.radio_state) {
+ Log.w(TAG, "process_setAudioMode: STATE = " + FmReceiverServiceState.radio_state);
+ return FmProxy.STATUS_ILLEGAL_COMMAND;
+ }
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_BUSY;
+
+ /* Set timer to check that this does not lock the state machine. */
+ Message msg = Message.obtain();
+ msg.what = FmReceiverServiceState.OPERATION_TIMEOUT;
+ msg.arg1 = FM_SET_AUDIO_MODE;
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+ operationHandler.sendMessageDelayed(msg,
+ FmReceiverServiceState.OPERATION_TIMEOUT_GENERIC);
+
+ int returnStatus = FmProxy.STATUS_OK;
+ audioMode &= FmReceiverServiceState.AUDIO_MODE_BITMASK;
+ try {
+ if (!setAudioModeNative(audioMode))
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ } catch (Exception e) {
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ Log.e(TAG, "process_setAudioMode: setAudioModeNative failed", e);
+ }
+ if (returnStatus != FmProxy.STATUS_OK)
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ return returnStatus;
+ }
+ private native boolean setAudioModeNative(int audioMode);
+
+ /**
+ * Configures FM audio path to AUDIO_PATH_NONE, AUDIO_PATH_SPEAKER,
+ * AUDIO_PATH_WIRED_HEADSET or AUDIO_PATH_DIGITAL. Will result in an audio
+ * path event callback.
+ *
+ * @param audioPath
+ * the audio path such as AUDIO_PATH_NONE, AUDIO_PATH_SPEAKER,
+ * AUDIO_PATH_WIRED_HEADSET or AUDIO_PATH_DIGITAL. The following
+ * values should be used {@link FmProxy#AUDIO_PATH_NONE},
+ * {@link FmProxy#AUDIO_PATH_SPEAKER},
+ * {@link FmProxy#AUDIO_PATH_WIRED_HEADSET} or
+ * {@link FmProxy#AUDIO_PATH_DIGITAL}.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error code.
+ */
+ public synchronized int setAudioPath(int audioPath) {
+ if (V) Log.d(TAG, "setAudioPath: audioPath = " + audioPath);
+
+ queueFMCommand(new FMJob(FM_SET_AUDIO_PATH, audioPath));
+ return FmProxy.STATUS_OK;
+ }
+
+ private int process_setAudioPath(int audioPath) {
+ if (V) Log.d(TAG, "process_setAudioPath: audioPath = " + audioPath);
+
+ if (FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND != FmReceiverServiceState.radio_state) {
+ Log.w(TAG, "process_setAudioPath: STATE = " + FmReceiverServiceState.radio_state);
+ return FmProxy.STATUS_ILLEGAL_COMMAND;
+ }
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_BUSY;
+
+ /* Set timer to check that this does not lock the state machine. */
+ Message msg = Message.obtain();
+ msg.what = FmReceiverServiceState.OPERATION_TIMEOUT;
+ msg.arg1 = FM_SET_AUDIO_PATH;
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+ operationHandler.sendMessageDelayed(msg,
+ FmReceiverServiceState.OPERATION_TIMEOUT_GENERIC);
+
+ int returnStatus = FmProxy.STATUS_OK;
+ audioPath &= FmReceiverServiceState.AUDIO_PATH_BITMASK;
+ try {
+ if (!setAudioPathNative(audioPath))
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ } catch (Exception e) {
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ Log.e(TAG, "process_setAudioPath: setAudioPathNative failed", e);
+ }
+ if (returnStatus != FmProxy.STATUS_OK)
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ return returnStatus;
+ }
+ private native boolean setAudioPathNative(int audioPath);
+
+ /**
+ * Sets the minimum frequency step size to use when scanning for stations.
+ * This function does not result in a status callback and the calling
+ * application should therefore keep track of this setting.
+ *
+ * @param stepSize
+ * a frequency interval set to
+ * {@link FmProxy#FREQ_STEP_100KHZ} or
+ * {@link FmProxy#FREQ_STEP_50KHZ}.
+ *
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error code.
+ */
+ public synchronized int setStepSize(int stepSize) {
+ if (V) Log.d(TAG, "setStepSize: stepSize = " + stepSize);
+
+ /* Sanity check of parameters. */
+ if (stepSize != FmProxy.FREQ_STEP_50KHZ && stepSize != FmProxy.FREQ_STEP_100KHZ) {
+ Log.e(TAG, "setStepSize: Illegal stepSize = " + stepSize);
+ return FmProxy.STATUS_ILLEGAL_PARAMETERS;
+ }
+
+ queueFMCommand(new FMJob(FM_SET_STEP_SIZE, stepSize));
+ return FmProxy.STATUS_OK;
+ }
+
+ private int process_setStepSize(int stepSize) {
+ if (V) Log.d(TAG, "process_setStepSize: stepSize = " + stepSize);
+
+ if (FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND != FmReceiverServiceState.radio_state) {
+ Log.w(TAG, "process_setStepSize: STATE = " + FmReceiverServiceState.radio_state);
+ return FmProxy.STATUS_ILLEGAL_COMMAND;
+ }
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_BUSY;
+
+ /* Set timer to check that this does not lock the state machine. */
+ Message msg = Message.obtain();
+ msg.what = FmReceiverServiceState.OPERATION_TIMEOUT;
+ msg.arg1 = FM_SET_STEP_SIZE;
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+ operationHandler.sendMessageDelayed(msg,
+ FmReceiverServiceState.OPERATION_TIMEOUT_GENERIC);
+
+ int returnStatus = FmProxy.STATUS_OK;
+ try {
+ if (!setScanStepNative(stepSize))
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ } catch (Exception e) {
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ Log.e(TAG, "process_setStepSize: setScanStepNative failed", e);
+ }
+ if (returnStatus != FmProxy.STATUS_OK)
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ return returnStatus;
+ }
+ private native boolean setScanStepNative(int stepSize);
+
+ /**
+ * Sets the volume to use.
+ *
+ * @param volume
+ * range from 0 to 0x100
+ *
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error code.
+ */
+ public synchronized int setFMVolume(int volume) {
+ if (V) Log.d(TAG, "setFMVolume: volume = " + volume);
+
+ if (volume < 0 || volume > 255) {
+ Log.e(TAG, "setFMVolume: Illegal volume = " + volume);
+ return FmProxy.STATUS_ILLEGAL_PARAMETERS;
+ }
+
+ queueFMCommand(new FMJob(FM_SET_VOLUME, volume));
+ return FmProxy.STATUS_OK;
+ }
+
+ private int process_setFMVolume(int volume) {
+ if (V) Log.d(TAG, "process_setFMVolume: volume = " + volume);
+
+ if (FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND != FmReceiverServiceState.radio_state) {
+ Log.w(TAG, "process_setFMVolume: STATE = " + FmReceiverServiceState.radio_state);
+ return FmProxy.STATUS_ILLEGAL_COMMAND;
+ }
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_BUSY;
+
+ /* Set timer to check that this does not lock the state machine. */
+ Message msg = Message.obtain();
+ msg.what = FmReceiverServiceState.OPERATION_TIMEOUT;
+ msg.arg1 = FM_SET_VOLUME;
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+ operationHandler.sendMessageDelayed(msg,
+ FmReceiverServiceState.OPERATION_TIMEOUT_GENERIC);
+
+ int returnStatus = FmProxy.STATUS_OK;
+ try {
+ if (!setFMVolumeNative(volume))
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ } catch (Exception e) {
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ Log.e(TAG, "process_setFMVolume: setFMVolumeNative failed", e);
+ }
+ if (returnStatus != FmProxy.STATUS_OK)
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ return returnStatus;
+ }
+ private native boolean setFMVolumeNative(int volume);
+
+ /**
+ * Sets a the world frequency region and deemphasis time. This results in a
+ * world region event callback.
+ *
+ * @param worldRegion
+ * the world region the FM receiver is located. Set to
+ * {@link FmProxy#FUNC_REGION_NA},
+ * {@link FmProxy#FUNC_REGION_EUR} or
+ * {@link FmProxy#FUNC_REGION_JP}.
+ * @param deemphasisTime
+ * the deemphasis time that can be set to either
+ * {@link FmProxy#DEEMPHASIS_50U} or
+ * {@link FmProxy#DEEMPHASIS_75U}.
+ *
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error
+ * code.
+ */
+ public synchronized int setWorldRegion(int worldRegion, int deemphasisTime) {
+ if (V) Log.d(TAG, "setWorldRegion: worldRegion = " + worldRegion +
+ ", deemphasisTime = " + deemphasisTime);
+
+ /* Sanity check of parameters. */
+ if (worldRegion != FmProxy.FUNC_REGION_NA &&
+ worldRegion != FmProxy.FUNC_REGION_EUR &&
+ worldRegion != FmProxy.FUNC_REGION_JP)
+ {
+ Log.e(TAG, "setWorldRegion: Illegal worldRegion = " + worldRegion);
+ return FmProxy.STATUS_ILLEGAL_PARAMETERS;
+ }
+ if (deemphasisTime != FmProxy.DEEMPHASIS_50U &&
+ deemphasisTime != FmProxy.DEEMPHASIS_75U)
+ {
+ Log.e(TAG, "setWorldRegion: Illegal deemphasisTime = " + deemphasisTime);
+ return FmProxy.STATUS_ILLEGAL_PARAMETERS;
+ }
+
+ queueFMCommand(new FMJob(FM_SET_WORLD_REGION, worldRegion, deemphasisTime));
+ return FmProxy.STATUS_OK;
+ }
+
+ private int process_setWorldRegion(int worldRegion, int deemphasisTime) {
+ if (V) Log.d(TAG, "process_setWorldRegion: worldRegion = " + worldRegion +
+ ", deemphasisTime = " + deemphasisTime);
+
+ if (FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND != FmReceiverServiceState.radio_state) {
+ Log.w(TAG, "process_setWorldRegion: STATE = " + FmReceiverServiceState.radio_state);
+ return FmProxy.STATUS_ILLEGAL_COMMAND;
+ }
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_BUSY;
+
+ /* Set timer to check that this does not lock the state machine. */
+ Message msg = Message.obtain();
+ msg.what = FmReceiverServiceState.OPERATION_TIMEOUT;
+ msg.arg1 = FM_SET_WORLD_REGION;
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+ operationHandler.sendMessageDelayed(msg,
+ FmReceiverServiceState.OPERATION_TIMEOUT_GENERIC);
+
+ int returnStatus = FmProxy.STATUS_OK;
+ try {
+ if (!setRegionNative(worldRegion) || !configureDeemphasisNative(deemphasisTime))
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ } catch (Exception e) {
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ Log.e(TAG, "process_setWorldRegion: setRdsNative failed", e);
+ }
+ if (returnStatus != FmProxy.STATUS_OK)
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ return returnStatus;
+ }
+ private native boolean setRegionNative(int region);
+ private native boolean configureDeemphasisNative(int time);
+
+ /**
+ * Estimates the current frequencies noise floor level. Generates an
+ * Estimated NFL Event when complete. The returned NFL value can be used to
+ * determine which minimum signal strength to use seeking stations.
+ *
+ * @param estimatedNoiseFloorLevel
+ * Estimate noise floor to {@link FmProxy#NFL_LOW},
+ * {@link FmProxy#NFL_MED} or {@link FmRecei-vver#NFL_FINE}.
+ *
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error code.
+ */
+ public synchronized int estimateNoiseFloorLevel(int nflLevel) {
+ if (V) Log.d(TAG, "estimateNoiseFloorLevel: nflLevel = " + nflLevel);
+
+ if (nflLevel != FmProxy.NFL_FINE && nflLevel != FmProxy.NFL_MED &&
+ nflLevel != FmProxy.NFL_LOW)
+ {
+ Log.e(TAG, "estimateNoiseFloorLevel: Illegal nflLevel = " + nflLevel);
+ return FmProxy.STATUS_ILLEGAL_PARAMETERS;
+ }
+
+ queueFMCommand(new FMJob(FM_ESTIMATE_NOISE_FLOOR_LEVEL, nflLevel));
+ return FmProxy.STATUS_OK;
+ }
+
+ private int process_estimateNoiseFloorLevel(int nflLevel) throws RemoteException {
+ if (V) Log.d(TAG, "process_estimateNoiseFloorLevel: nflLevel = " + nflLevel);
+
+
+ /* Sanity check of parameters. */
+ if (FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND != FmReceiverServiceState.radio_state) {
+ Log.w(TAG, "process_estimateNoiseFloorLevel: STATE = " + FmReceiverServiceState.radio_state);
+ return FmProxy.STATUS_ILLEGAL_COMMAND;
+ }
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_BUSY;
+
+ /* Set timer to check that this does not lock the state machine. */
+ Message msg = Message.obtain();
+ msg.what = FmReceiverServiceState.OPERATION_TIMEOUT;
+ msg.arg1 = FM_ESTIMATE_NOISE_FLOOR_LEVEL;
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+ operationHandler.sendMessageDelayed(msg,FmReceiverServiceState.OPERATION_TIMEOUT_NFL);
+
+ int returnStatus = FmProxy.STATUS_OK;
+ try {
+ if (!estimateNoiseFloorNative(nflLevel))
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ } catch (Exception e) {
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ Log.e(TAG, "process_estimateNoiseFloorLevel: estimateNoiseFloorNative failed", e);
+ }
+ if (returnStatus != FmProxy.STATUS_OK)
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ return returnStatus;
+ }
+ private native boolean estimateNoiseFloorNative(int level);
+
+ /**
+ * Sets the live audio polling function that can provide RSSI data on the
+ * currently tuned frequency at specified intervals.
+ *
+ * @param liveAudioPolling
+ * enable/disable live audio data quality updating.
+ * @param signalPollInterval
+ * time between RSSI signal polling in milliseconds.
+ *
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error code.
+ */
+ public synchronized int setLiveAudioPolling(boolean liveAudioPolling, int signalPollInterval) {
+ if (V) Log.d(TAG, "setLiveAudioPolling: liveAudioPolling = " + liveAudioPolling +
+ ", signalPollInterval = " + signalPollInterval);
+
+ /* Sanity check of parameters. */
+ if (liveAudioPolling &&
+ (signalPollInterval < FmReceiverServiceState.MIN_ALLOWED_SIGNAL_POLLING_TIME ||
+ signalPollInterval > FmReceiverServiceState.MAX_ALLOWED_SIGNAL_POLLING_TIME))
+ {
+ Log.e(TAG, "seekStation: Illegal liveAudioPolling = " + liveAudioPolling +
+ ", signalPollInterval = " + signalPollInterval);
+ return FmProxy.STATUS_ILLEGAL_PARAMETERS;
+ }
+
+ queueFMCommand(new FMJob(FM_SET_LIVE_AUDIO_POLLING, liveAudioPolling, signalPollInterval));
+ return FmProxy.STATUS_OK;
+ }
+
+ private int process_setLiveAudioPolling(boolean liveAudioPolling,
+ int signalPollInterval) throws RemoteException {
+ if (V) Log.d(TAG, "process_setLiveAudioPolling: liveAudioPolling = " +
+ liveAudioPolling + ", signalPollInterval = " + signalPollInterval);
+
+ if (FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND != FmReceiverServiceState.radio_state) {
+ Log.w(TAG, "process_setLiveAudioPolling: STATE = " + FmReceiverServiceState.radio_state);
+ return FmProxy.STATUS_ILLEGAL_COMMAND;
+ }
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_BUSY;
+
+ // TBD: why not take care timeout for this operation
+
+ int returnStatus = FmProxy.STATUS_OK;
+ try {
+ if (!getAudioQualityNative(liveAudioPolling) ||
+ !configureSignalNotificationNative(signalPollInterval))
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ } catch (Exception e) {
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ Log.e(TAG, "process_setLiveAudioPolling: setLiveAudioPolling failed", e);
+ }
+
+ FmReceiverServiceState.radio_state =
+ FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND;
+
+ fetchNextJob(FM_SET_LIVE_AUDIO_POLLING);
+ return returnStatus;
+ }
+ private native boolean getAudioQualityNative(boolean enabled);
+ private native boolean configureSignalNotificationNative(int interval);
+
+ /*
+ * Callback generator section.
+ */
+
+ /**
+ * Sends event data from the local cache to all registered callbacks.
+ */
+ private void sendStatusEventCallbackFromLocalStore(int currCmd, boolean sendNextJob) {
+ FM_Status_Params status = new FM_Status_Params(FmReceiverServiceState.mFreq,
+ FmReceiverServiceState.mRssi,
+ FmReceiverServiceState.mSnr,
+ FmReceiverServiceState.mRadioIsOn,
+ FmReceiverServiceState.mRdsProgramType,
+ FmReceiverServiceState.mRdsProgramService,
+ FmReceiverServiceState.mRdsRadioText,
+ FmReceiverServiceState.mRdsProgramTypeName,
+ FmReceiverServiceState.mIsMute);
+ Message msg = Message.obtain();
+ msg.what = FmReceiverServiceState.OPERATION_STATUS_EVENT_CALLBACK;
+ msg.arg1 = currCmd;
+ msg.arg2 = (sendNextJob == true) ? 1 : 0;
+ msg.obj = (Object) status;
+ operationHandler.sendMessage(msg);
+ }
+
+ /**
+ * Sends the contents typically supplied by a Status Event
+ * to all registered callbacks.
+ *
+ * @param freq the current listening frequency.
+ * @param rssi the RSSI of the current frequency.
+ * @param snr the SNR value at the current frequency.
+ * @param radioIsOn TRUE if the radio is on and FALSE if off.
+ * @param rdsProgramType integer representation of program type.
+ * @param rdsProgramService name of the service.
+ * @param rdsRadioText text of the current program/service.
+ * @param rdsProgramTypeName string version of the rdsProgramType parameter.
+ * @param isMute TRUE if muted by hardware and FALSE if not.
+ */
+ private void sendStatusEventCallback(int freq, int rssi, int snr,
+ boolean radioIsOn, int rdsProgramType,
+ String rdsProgramService, String rdsRadioText,
+ String rdsProgramTypeName, boolean isMute,
+ int currCmd, int sendNextJob) {
+ if (V) Log.d(TAG, "sendStatusEventCallback: freq = " + freq + ", rssi = " + rssi +
+ ", snr = " + snr + ", radioIsOn = " + radioIsOn +
+ ", rdsProgramType = " + rdsProgramType +
+ ", rdsProgramService = " + rdsProgramService +
+ ", rdsRadioText = " + rdsRadioText +
+ ", rdsProgramTypeName = " + rdsProgramTypeName +
+ ", isMute = " + isMute);
+ if (V) Log.d(TAG, "sendStatusEventCallback: currCmd = " + currCmd +
+ ", sendNextJob = " + sendNextJob);
+
+ try {
+ final int callbacks = mCallbacks.beginBroadcast();
+ for (int i = 0; i < callbacks; i++) {
+ try {
+ /* Send the callback to each registered receiver. */
+ mCallbacks.getBroadcastItem(i).onStatusEvent(freq,
+ rssi, snr, radioIsOn, rdsProgramType,
+ rdsProgramService, rdsRadioText,
+ rdsProgramTypeName, isMute);
+ } catch (RemoteException e) {
+ e.printStackTrace();
+ }
+ }
+ mCallbacks.finishBroadcast();
+ } catch (IllegalStateException e_i) {
+ e_i.printStackTrace();
+ }
+ if (FmReceiverServiceState.radio_state != FmReceiverServiceState.RADIO_STATE_OFF &&
+ FmReceiverServiceState.radio_state != FmReceiverServiceState.RADIO_STATE_STOPPING)
+ FmReceiverServiceState.radio_state =
+ FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND;
+
+ if (sendNextJob > 0)
+ fetchNextJob(currCmd);
+ }
+
+ /**
+ * Sends event data from the local cache to all registered callbacks.
+ */
+ private void sendSeekCompleteEventCallbackFromLocalStore(int currCmd, boolean sendNextJob) {
+ FM_Search_Params search_st
+ = new FM_Search_Params(FmReceiverServiceState.mFreq,
+ FmReceiverServiceState.mRssi,
+ FmReceiverServiceState.mSnr,
+ FmReceiverServiceState.mSeekSuccess);
+ Message msg = Message.obtain();
+ msg.what = FmReceiverServiceState.OPERATION_SEARCH_EVENT_CALLBACK;
+ msg.arg1 = currCmd;
+ msg.arg2 = (sendNextJob == true) ? 1 : 0;
+ msg.obj = (Object) search_st;
+ operationHandler.sendMessage(msg);
+ }
+
+ /**
+ * Sends the contents of a seek complete event to all registered callbacks.
+ *
+ * @param freq
+ * The station frequency if located or the last seek frequency if
+ * not successful.
+ * @param rssi
+ * the RSSI at the current frequency.
+ * @param snr
+ * the SNR at the current frequency.
+ * @param seekSuccess
+ * TRUE if search was successful, false otherwise.
+ */
+ private void sendSeekCompleteEventCallback(int freq, int rssi,
+ int snr, boolean seekSuccess, int currCmd, int sendNextJob) {
+ if (V) Log.d(TAG, "sendSeekCompleteEventCallback: freq = " + freq +
+ ", rssi = " + rssi + ", snr = " + snr +
+ ", seekSuccess = " + seekSuccess);
+ if (V) Log.d(TAG, "sendSeekCompleteEventCallback: currCmd = " + currCmd +
+ ", sendNextJob = " + sendNextJob);
+
+ try {
+ final int callbacks = mCallbacks.beginBroadcast();
+ for (int i = 0; i < callbacks; i++) {
+ try {
+ /* Send the callback to each registered receiver. */
+ mCallbacks.getBroadcastItem(i).onSeekCompleteEvent(freq, rssi, snr, seekSuccess);
+ } catch (Throwable t) {
+ Log.e(TAG, "sendSeekCompleteEventCallback", t);
+ }
+ }
+ mCallbacks.finishBroadcast();
+ } catch (IllegalStateException e_i) {
+ e_i.printStackTrace();
+ }
+
+ /* Update state machine. */
+ if (FmReceiverServiceState.radio_state != FmReceiverServiceState.RADIO_STATE_OFF &&
+ FmReceiverServiceState.radio_state != FmReceiverServiceState.RADIO_STATE_STOPPING)
+ {
+ FmReceiverServiceState.radio_state =
+ FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND;
+ }
+ if (sendNextJob > 0)
+ fetchNextJob(currCmd);
+ }
+
+ /**
+ * Sends event data from the local cache to all registered callbacks.
+ */
+ private void sendRdsModeEventCallbackFromLocalStore() {
+ int af = FmReceiverServiceState.mAfOn ? 1 : 0;
+ int rds = 0;
+ if (FmReceiverServiceState.mRdsOn) {
+ rds = (FmReceiverServiceState.mRdsType == 0) ? 1 : 2;
+ }
+ Message msg = Message.obtain();
+ msg.what = FmReceiverServiceState.OPERATION_RDS_EVENT_CALLBACK;
+ msg.arg1 = rds;
+ msg.arg2 = af;
+ operationHandler.sendMessage(msg);
+ }
+
+ /**
+ * Sends the contents of an RDS mode event to all registered callbacks.
+ *
+ * @param rdsMode
+ * the current RDS mode
+ * @param alternateFreqHopEnabled
+ * TRUE if AF is enabled, false otherwise.
+ */
+ private void sendRdsModeEventCallback(int rdsMode, int alternateFreqMode) {
+ if (V) Log.d(TAG, "sendRdsModeEventCallback: rdsMode = " + rdsMode +
+ ", alternateFreqMode = " + alternateFreqMode);
+
+ try {
+ final int callbacks = mCallbacks.beginBroadcast();
+ for (int i = 0; i < callbacks; i++) {
+ try {
+ /* Send the callback to each registered receiver. */
+ mCallbacks.getBroadcastItem(i).onRdsModeEvent(rdsMode, alternateFreqMode);
+ } catch (Throwable t) {
+ Log.e(TAG, "sendRdsModeEventCallback", t);
+ }
+ }
+ mCallbacks.finishBroadcast();
+ } catch (IllegalStateException e_i) {
+ e_i.printStackTrace();
+ }
+
+ /* Update state machine. */
+ if (FmReceiverServiceState.radio_state != FmReceiverServiceState.RADIO_STATE_OFF &&
+ FmReceiverServiceState.radio_state != FmReceiverServiceState.RADIO_STATE_STOPPING)
+ {
+ FmReceiverServiceState.radio_state
+ = FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND;
+ }
+ fetchNextJob(FM_SET_RDS_MODE);
+ }
+
+ /**
+ * Sends event data from the local cache to all registered callbacks.
+ */
+ private void sendRdsDataEventCallbackFromLocalStore(int rdsDataType, int rdsIndex, String rdsText) {
+ Message msg = Message.obtain();
+ msg.what = FmReceiverServiceState.OPERATION_RDS_DATA_EVENT_CALLBACK;
+ msg.arg1 = rdsDataType;
+ msg.arg2 = rdsIndex;
+ msg.obj = new String(rdsText);
+ operationHandler.sendMessage(msg);
+ }
+
+ /**
+ * Sends the contents of an RDS data event to all registered callbacks.
+ *
+ * @param rdsDataType
+ * the RDS data type
+ * @param rdsIndex
+ * the RDS index
+ * @param rdsText
+ * the RDS text
+ */
+ private void sendRdsDataEventCallback(int rdsDataType, int rdsIndex, String rdsText) {
+ if (V) Log.d(TAG, "sendRdsDataEventCallback: rdsDataType = " + rdsDataType +
+ ", rdsIndex = " + rdsIndex + "rdsText = " + rdsText);
+
+ try {
+ final int callbacks = mCallbacks.beginBroadcast();
+ for (int i = 0; i < callbacks; i++) {
+ try {
+ /* Send the callback to each registered receiver. */
+ mCallbacks.getBroadcastItem(i).onRdsDataEvent(rdsDataType, rdsIndex, rdsText);
+ } catch (Throwable t) {
+ Log.e(TAG, "sendRdsDataEventCallback", t);
+ }
+ }
+ mCallbacks.finishBroadcast();
+ } catch (IllegalStateException e_i) {
+ e_i.printStackTrace();
+ }
+ }
+
+ /**
+ * Sends event data from the local cache to all registered callbacks.
+ */
+ private void sendAudioModeEventCallbackFromLocalStore() {
+ Message msg = Message.obtain();
+ msg.what = FmReceiverServiceState.OPERATION_AUDIO_MODE_EVENT_CALLBACK;
+ msg.arg1 = FmReceiverServiceState.mAudioMode;
+ operationHandler.sendMessage(msg);
+ }
+
+ /**
+ * Sends the contents of an audio mode event to all registered callbacks.
+ *
+ * @param audioMode
+ * the current audio mode in use.
+ */
+ private void sendAudioModeEventCallback(int audioMode) {
+ if (V) Log.d(TAG, "sendAudioModeEventCallback: audioMode = " + audioMode);
+
+ try {
+ final int callbacks = mCallbacks.beginBroadcast();
+ for (int i = 0; i < callbacks; i++) {
+ try {
+ /* Send the callback to each registered receiver. */
+ mCallbacks.getBroadcastItem(i).onAudioModeEvent(audioMode);
+ } catch (Throwable t) {
+ Log.e(TAG, "sendAudioModeEventCallback", t);
+ }
+ }
+ mCallbacks.finishBroadcast();
+ } catch (IllegalStateException e_i) {
+ e_i.printStackTrace();
+ }
+
+ /* Update state machine. */
+ if (FmReceiverServiceState.radio_state != FmReceiverServiceState.RADIO_STATE_OFF &&
+ FmReceiverServiceState.radio_state != FmReceiverServiceState.RADIO_STATE_STOPPING)
+ {
+ FmReceiverServiceState.radio_state
+ = FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND;
+ }
+ fetchNextJob(FM_SET_AUDIO_MODE);
+ }
+
+ /**
+ * Sends event data from the local cache to all registered callbacks.
+ */
+ private void sendAudioPathEventCallbackFromLocalStore() {
+ Message msg = Message.obtain();
+ msg.what = FmReceiverServiceState.OPERATION_AUDIO_PATH_EVENT_CALLBACK;
+ msg.arg1 = FmReceiverServiceState.mAudioPath;
+ operationHandler.sendMessage(msg);
+ }
+
+ /**
+ * Sends the contents of an audio path event to all registered callbacks.
+ *
+ * @param audioPath
+ * the current audio mode in use.
+ */
+ private void sendAudioPathEventCallback(int audioPath) {
+ if (V) Log.d(TAG, "sendAudioPathEventCallback: audioPath = " + audioPath);
+
+ try {
+ final int callbacks = mCallbacks.beginBroadcast();
+ for (int i = 0; i < callbacks; i++) {
+ try {
+ /* Send the callback to each registered receiver. */
+ mCallbacks.getBroadcastItem(i).onAudioPathEvent(audioPath);
+ } catch (Throwable t) {
+ Log.e(TAG, "sendAudioPathEventCallback", t);
+ }
+ }
+ mCallbacks.finishBroadcast();
+ } catch (IllegalStateException e_i) {
+ e_i.printStackTrace();
+ }
+
+ /* Update state machine. */
+ if (FmReceiverServiceState.radio_state != FmReceiverServiceState.RADIO_STATE_OFF &&
+ FmReceiverServiceState.radio_state != FmReceiverServiceState.RADIO_STATE_STOPPING)
+ {
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND;
+ }
+ fetchNextJob(FM_SET_AUDIO_PATH);
+ }
+
+ /**
+ * Sends event data from the local cache to all registered callbacks.
+ */
+ private void sendWorldRegionEventCallbackFromLocalStore() {
+ Message msg = Message.obtain();
+ msg.what = FmReceiverServiceState.OPERATION_REGION_EVENT_CALLBACK;
+ msg.arg1 = FmReceiverServiceState.mWorldRegion;
+ operationHandler.sendMessage(msg);
+ }
+
+ /**
+ * Sends the contents of a world region event to all registered callbacks.
+ *
+ * @param worldRegion
+ * the current frequency band region in use.
+ */
+ private void sendWorldRegionEventCallback(int worldRegion) {
+ if (V) Log.d(TAG, "sendWorldRegionEventCallback: worldRegion = " + worldRegion);
+
+ try {
+ final int callbacks = mCallbacks.beginBroadcast();
+ for (int i = 0; i < callbacks; i++) {
+ try {
+ /* Send the callback to each registered receiver. */
+ mCallbacks.getBroadcastItem(i).onWorldRegionEvent(worldRegion);
+ } catch (Throwable t) {
+ Log.e(TAG, "sendWorldRegionEventCallback", t);
+ }
+ }
+ mCallbacks.finishBroadcast();
+ } catch (IllegalStateException e_i) {
+ e_i.printStackTrace();
+ }
+
+ /* Update state machine. */
+ if (FmReceiverServiceState.radio_state != FmReceiverServiceState.RADIO_STATE_OFF &&
+ FmReceiverServiceState.radio_state != FmReceiverServiceState.RADIO_STATE_STOPPING)
+ {
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND;
+ }
+
+ fetchNextJob(FM_SET_WORLD_REGION);
+ }
+
+ /**
+ * Sends event data from the local cache to all registered callbacks.
+ */
+ private void sendEstimateNflEventCallbackFromLocalStore() {
+ Message msg = Message.obtain();
+ msg.what = FmReceiverServiceState.OPERATION_NFE_EVENT_CALLBACK;
+ msg.arg1 = FmReceiverServiceState.mEstimatedNoiseFloorLevel;
+ operationHandler.sendMessage(msg);
+ }
+
+ /**
+ * Sends the contents of the noise floor estimate event to all registered callbacks.
+ *
+ * @param nfl
+ * the current NFL value in use.
+ */
+ private void sendEstimateNflEventCallback(int nfl) {
+ if (V) Log.d(TAG, "sendEstimateNflEventCallback: nfl = " + nfl);
+
+ try {
+ final int callbacks = mCallbacks.beginBroadcast();
+ for (int i = 0; i < callbacks; i++) {
+ try {
+ /* Send the callback to each registered receiver. */
+ mCallbacks.getBroadcastItem(i).onEstimateNflEvent(nfl);
+ } catch (Throwable t) {
+ Log.e(TAG, "sendEstimateNflEventCallback", t);
+ }
+ }
+ mCallbacks.finishBroadcast();
+ } catch (IllegalStateException e_i) {
+ e_i.printStackTrace();
+ }
+
+ /* Update state machine. */
+ if (FmReceiverServiceState.radio_state != FmReceiverServiceState.RADIO_STATE_OFF &&
+ FmReceiverServiceState.radio_state != FmReceiverServiceState.RADIO_STATE_STOPPING)
+ {
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND;
+ }
+
+ fetchNextJob(FM_ESTIMATE_NOISE_FLOOR_LEVEL);
+ }
+
+ /**
+ * Sends event data from the local cache to all registered callbacks.
+ */
+ private void sendLiveAudioQualityEventCallbackFromLocalStore(int rssi, int snr) {
+ Message msg = Message.obtain();
+ msg.what = FmReceiverServiceState.OPERATION_LIVE_AUDIO_EVENT_CALLBACK;
+ msg.arg1 = rssi;
+ msg.arg2 = snr;
+ operationHandler.sendMessage(msg);
+ }
+
+ /**
+ * Sends the contents of the live audio quality data event to all registered callbacks.
+ *
+ * @param rssi
+ * the RSSI at the current frequency.
+ * @param snr
+ * the SNR at the current frequency.
+ */
+ private void sendLiveAudioQualityEventCallback(int rssi, int snr) {
+ if (V) Log.d(TAG, "sendLiveAudioQualityEventCallback: rssi = " + rssi + ", snr = " + snr);
+
+ try {
+ final int callbacks = mCallbacks.beginBroadcast();
+ for (int i = 0; i < callbacks; i++) {
+ try {
+ /* Send the callback to each registered receiver. */
+ mCallbacks.getBroadcastItem(i).onLiveAudioQualityEvent(rssi, snr);
+ } catch (Throwable t) {
+ Log.e(TAG, "sendLiveAudioQualityEventCallback", t);
+ }
+ }
+ mCallbacks.finishBroadcast();
+ } catch (IllegalStateException e_i) {
+ e_i.printStackTrace();
+ }
+ }
+
+ /**
+ * Sends volume event from the local cache to all registered callbacks.
+ */
+ private void sendVolumeEventCallbackFromLocalStore(int status, int volume) {
+ Message msg = Message.obtain();
+ msg.what = FmReceiverServiceState.OPERATION_VOLUME_EVENT_CALLBACK;
+ msg.arg1 = status;
+ msg.arg2 = volume;
+ operationHandler.sendMessage(msg);
+ }
+
+ /**
+ * Sends the contents of a FM volume event to all registered callbacks.
+ *
+ * @param status
+ * equal to 0 if successful. Otherwise returns a non-zero error code.
+ * @param volume
+ * range from 0 to 255
+ */
+ private void sendVolumeEventCallback(int status, int volume) {
+ if (V) Log.d(TAG, "sendVolumeEventCallback: status = " + status + ", volume = " + volume);
+
+ try {
+ final int callbacks = mCallbacks.beginBroadcast();
+ for (int i = 0; i < callbacks; i++) {
+ try {
+ /* Send the callback to each registered receiver. */
+ mCallbacks.getBroadcastItem(i).onVolumeEvent(status, volume);
+ } catch (Throwable t) {
+ Log.e(TAG, "sendVolumeEventCallback", t);
+ }
+ }
+ mCallbacks.finishBroadcast();
+ } catch (IllegalStateException e_i) {
+ e_i.printStackTrace();
+ }
+
+ /* Update state machine. */
+ if (FmReceiverServiceState.radio_state != FmReceiverServiceState.RADIO_STATE_OFF &&
+ FmReceiverServiceState.radio_state != FmReceiverServiceState.RADIO_STATE_STOPPING)
+ {
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND;
+ }
+
+ fetchNextJob(FM_SET_VOLUME);
+ }
+
+ /**
+ * Sets the SNR threshold for the subsequent FM frequency tuning.
+ * This value will be used by BTA stack internally.
+ *
+ * @param signalPollInterval
+ * SNR Threshold value (0 ~ 31 (BTA_FM_SNR_MAX) )
+ *
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error
+ * code.
+ */
+ public synchronized int setSnrThreshold(int snrThreshold) {
+ if (V) Log.d(TAG, "setSnrThreshold: snrThreshold = " + snrThreshold);
+
+ /* Sanity check of parameters. */
+ if (snrThreshold < FmReceiverServiceState.MIN_ALLOWED_SNR_THRESHOLD ||
+ snrThreshold > FmReceiverServiceState.MAX_ALLOWED_SNR_THRESHOLD)
+ {
+ Log.e(TAG, "setSnrThreshold: Illegal snrThreshold = " + snrThreshold);
+ return FmProxy.STATUS_ILLEGAL_PARAMETERS;
+ }
+ if (FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND != FmReceiverServiceState.radio_state) {
+ Log.w(TAG, "setSnrThreshold: STATE = " + FmReceiverServiceState.radio_state);
+ return FmProxy.STATUS_ILLEGAL_COMMAND;
+ }
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_BUSY;
+
+ int returnStatus = FmProxy.STATUS_OK;
+ try {
+ if (!setSnrThresholdNative(snrThreshold))
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ } catch (Exception e) {
+ returnStatus = FmProxy.STATUS_SERVER_FAIL;
+ Log.e(TAG, "setSnrThreshold: setSnrThreshold failed", e);
+ }
+
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND;
+ return returnStatus;
+ }
+ private native boolean setSnrThresholdNative(int snrThreshold);
+
+ /* JNI BTA Event callback functions. */
+ public void onRadioOnEvent(int status) {
+ if (V) Log.d(TAG, "onRadioOnEvent: status = " + status);
+
+ /* Update local cache. */
+ if (FmReceiverServiceState.BTA_FM_OK == status) {
+ FmReceiverServiceState.mRadioIsOn = true;
+ }
+
+ /* This response indicates that system is alive and well. */
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ /* Update state machine. */
+ if (!FmReceiverServiceState.mRadioIsOn) {
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_OFF;
+ }
+
+ /* Send the callback... */
+ sendStatusEventCallbackFromLocalStore(FM_ON, true);
+ }
+
+ public void onRadioOffEvent(int status) {
+ if (V) Log.d(TAG, "onRadioOffEvent: status = " + status);
+
+ /* Update local cache. */
+ if (FmReceiverServiceState.BTA_FM_OK == status) {
+ FmReceiverServiceState.mRadioIsOn = false;
+ }
+
+ /* This response indicates that system is alive and well. */
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ /* Update state machine. */
+ if (FmReceiverServiceState.mRadioIsOn) {
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND;
+ }
+
+ /* Send the callback... */
+ sendStatusEventCallbackFromLocalStore(FM_OFF, true);
+ }
+
+ public void onRadioMuteEvent(int status, boolean muted) {
+ if (V) Log.d(TAG, "onRadioMuteEvent: status = " + status + ", muted = " + muted);
+
+ /* Update local cache. */
+ if (FmReceiverServiceState.BTA_FM_OK == status) {
+ FmReceiverServiceState.mIsMute = muted;
+ }
+
+ /* This response indicates that system is alive and well. */
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ /* Send the callback... */
+ sendStatusEventCallbackFromLocalStore(FM_MUTE_AUDIO, true);
+ }
+
+ public void onRadioTuneEvent(int status, int rssi, int snr, int freq) {
+ if (V) Log.d(TAG, "onRadioTuneEvent: status = " + status + ", rssi = " + rssi +
+ ", snr = " + snr + ", freq = " + freq);
+
+ /* This response indicates that system is alive and well. */
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ /* Update local cache. */
+ if (FmReceiverServiceState.BTA_FM_OK == status) {
+ FmReceiverServiceState.mRssi = rssi;
+ FmReceiverServiceState.mSnr = snr;
+ FmReceiverServiceState.mFreq = freq;
+ }
+
+ /* Send the callback... */
+ sendStatusEventCallbackFromLocalStore(FM_TUNE_RADIO, true);
+ }
+
+ public void onRadioSearchEvent(int rssi, int snr, int freq) {
+ if (V) Log.d(TAG, "onRadioSearchEvent: rssi = " + rssi + ", snr = " + snr + ", freq = " + freq);
+
+ /* This response indicates that system is alive and well. */
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ /* Update local cache. */
+ FmReceiverServiceState.mRssi = rssi;
+ FmReceiverServiceState.mFreq = freq;
+ FmReceiverServiceState.mSnr = snr;
+ FmReceiverServiceState.mSeekSuccess = true;
+
+ sendSeekCompleteEventCallbackFromLocalStore(FM_CMD_ANY, false);
+ }
+
+ public void onRadioSearchCompleteEvent(int status, int rssi, int snr, int freq) {
+ if (V) Log.d(TAG, "onRadioSearchCompleteEvent: status = " + status +
+ ", rssi = " + rssi + ", snr = " + snr + ", freq = " + freq);
+
+ /* This response indicates that system is alive and well. */
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ /* Update local cache. */
+ FmReceiverServiceState.mRssi = rssi;
+ FmReceiverServiceState.mSnr = snr;
+ FmReceiverServiceState.mFreq = freq;
+ FmReceiverServiceState.mSeekSuccess = (status == FmProxy.STATUS_OK);
+
+ sendSeekCompleteEventCallbackFromLocalStore(FM_CMD_ANY, true);
+ }
+
+ public void onRadioAfJumpEvent(int status, int rssi, int freq) {
+ Log.v(TAG, "onRadioAfJumpEvent: status = " + status + ", rssi = " + rssi +
+ ", freq = " + freq + ")");
+ /* This response indicates that system is alive and well. */
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+ FmReceiverServiceState.mRssi = rssi;
+ FmReceiverServiceState.mFreq = freq;
+ FmReceiverServiceState.mSeekSuccess = true;
+ sendSeekCompleteEventCallbackFromLocalStore(FM_CMD_ANY, true);
+
+ /* Is this of interest internally without knowing the new frequency? */
+ }
+
+ public void onRadioAudioModeEvent(int status, int mode) {
+ if (V) Log.d(TAG, "onRadioAudioModeEvent: status = " + status + ", mode = " + mode);
+
+ /* This response indicates that system is alive and well. */
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ /* Update local cache. */
+ if (FmReceiverServiceState.BTA_FM_OK == status) {
+ FmReceiverServiceState.mAudioMode = mode;
+ }
+ sendAudioModeEventCallbackFromLocalStore();
+
+ }
+
+ public void onRadioAudioPathEvent(int status, int path) {
+ if (V) Log.d(TAG, "onRadioAudioPathEvent: status = " + status + ", path = " + path);
+
+ /* This response indicates that system is alive and well. */
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ /* Update local cache. */
+ if (FmReceiverServiceState.BTA_FM_OK == status) {
+ FmReceiverServiceState.mAudioPath = path;
+ }
+ sendAudioPathEventCallbackFromLocalStore();
+ }
+
+ public void onRadioAudioDataEvent(int status, int rssi, int snr, int mode) {
+ if (V) Log.d(TAG, "onRadioAudioDataEvent: status = " + status +
+ ", rssi = " + rssi + ", snr = " + snr + ", mode = " + mode);
+
+ /* This response indicates that system is alive and well. */
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ /* Update local cache. */
+ if (FmReceiverServiceState.BTA_FM_OK == status) {
+ FmReceiverServiceState.mRssi = rssi;
+ FmReceiverServiceState.mSnr = snr;
+ FmReceiverServiceState.mAudioMode = mode;
+ }
+
+ sendLiveAudioQualityEventCallbackFromLocalStore(rssi, snr);
+ }
+
+ public void onRadioRdsModeEvent(int status, boolean rdsOn, boolean afOn, int rdsType) {
+ if (V) Log.d(TAG, "onRadioRdsModeEvent: status = " + status + ", rdsOn = " + rdsOn +
+ ", afOn = " + afOn + "rdsType = " + rdsType);
+
+ /* This response indicates that system is alive and well. */
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ /* Update local cache. */
+ if (FmReceiverServiceState.BTA_FM_OK == status) {
+ FmReceiverServiceState.mRdsOn = rdsOn;
+ FmReceiverServiceState.mAfOn = afOn;
+ FmReceiverServiceState.mRdsType = rdsType;
+ }
+ FmReceiverServiceState.radio_op_state = FmReceiverServiceState.RADIO_OP_STATE_NONE;
+
+ /* Transmit event upwards to app. */
+ sendRdsModeEventCallbackFromLocalStore();
+ }
+
+ // Should not be needed anymore
+ public void onRadioRdsTypeEvent(int status, int rdsType) {
+ if (V) Log.d(TAG, "onRadioRdsTypeEvent: status = " + status + ", rdsType = " + rdsType);
+
+ /* Update local cache. */
+ if (FmReceiverServiceState.BTA_FM_OK == status) {
+ FmReceiverServiceState.mRdsType = rdsType;
+ }
+
+ if (FmReceiverServiceState.RADIO_OP_STATE_STAGE_1 == FmReceiverServiceState.radio_op_state) {
+ /* This response indicates that system is alive and well. */
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+ FmReceiverServiceState.radio_op_state = FmReceiverServiceState.RADIO_OP_STATE_NONE;
+ /* Transmit event upwards to app. */
+ sendRdsModeEventCallbackFromLocalStore();
+ } else {
+ /* Indicate that this command in the sequence is finished. */
+ FmReceiverServiceState.radio_op_state = FmReceiverServiceState.RADIO_OP_STATE_STAGE_2;
+ }
+ }
+
+ public void onRadioRdsUpdateEvent(int status, int data, int index, String text) {
+ if (V) Log.d(TAG, "onRadioRdsUpdateEvent: status = " + status + ", data = " + data +
+ ", index = " + index + ", text = " + text);
+
+ if (FmReceiverServiceState.BTA_FM_OK == status) {
+ /* Update local cache. (For retrieval in status commands.) */
+ switch (data) {
+ case FmReceiverServiceState.RDS_ID_PTY_EVT:
+ FmReceiverServiceState.mRdsProgramType = index;
+ break;
+ case FmReceiverServiceState.RDS_ID_PTYN_EVT:
+ FmReceiverServiceState.mRdsProgramTypeName = text;
+ break;
+ case FmReceiverServiceState.RDS_ID_RT_EVT:
+ FmReceiverServiceState.mRdsRadioText = text;
+ break;
+ case FmReceiverServiceState.RDS_ID_PS_EVT:
+ FmReceiverServiceState.mRdsProgramService = text;
+ break;
+ default:
+ break;
+ }
+
+ /* Transmit individual message to app. */
+ sendRdsDataEventCallbackFromLocalStore(data, index, text);
+ }
+ }
+
+ public void onRadioDeemphEvent(int status, int time) {
+ if (V) Log.d(TAG, "onRadioDeemphEvent: status = " + status + ", time = " + time);
+
+ /* This response indicates that system is alive and well. */
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ /* Update state machine. */
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND;
+ fetchNextJob(FM_CMD_ANY);
+ }
+
+ public void onRadioScanStepEvent(int stepSize) {
+ if (V) Log.d(TAG, "onRadioScanStepEvent: stepSize = " + stepSize);
+
+ /* This response indicates that system is alive and well. */
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ /* Update state machine. */
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND;
+ fetchNextJob(FM_SET_STEP_SIZE);
+ }
+
+ public void onRadioRegionEvent(int status, int region) {
+ if (V) Log.d(TAG, "onRadioRegionEvent: status = " + status + ", region = " + region);
+
+ /* This response indicates that system is alive and well. */
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ FmReceiverServiceState.mWorldRegion = region;
+ sendWorldRegionEventCallbackFromLocalStore();
+ }
+
+ public void onRadioNflEstimationEvent(int level) {
+ if (V) Log.d(TAG, "onRadioNflEstimationEvent: level = " + level);
+
+ /* This response indicates that system is alive and well. */
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ /* Update state machine. */
+ FmReceiverServiceState.radio_state = FmReceiverServiceState.RADIO_STATE_READY_FOR_COMMAND;
+
+ /* Update local cache. */
+ FmReceiverServiceState.mEstimatedNoiseFloorLevel = level;
+ sendEstimateNflEventCallbackFromLocalStore();
+ }
+
+ public void onRadioVolumeEvent(int status, int volume) {
+ if (V) Log.d(TAG, "onRadioVolumeEvent: status = " + status + ", volume = " + volume);
+
+ /* This response indicates that system is alive and well. */
+ operationHandler.removeMessages(FmReceiverServiceState.OPERATION_TIMEOUT);
+
+ sendVolumeEventCallbackFromLocalStore(status, volume);
+ }
+}
diff --git a/fm/app/src/com/broadcom/fm/fmreceiver/FmReceiverServiceState.java b/fm/app/src/com/broadcom/fm/fmreceiver/FmReceiverServiceState.java
new file mode 100644
index 0000000..7beb7e2
--- /dev/null
+++ b/fm/app/src/com/broadcom/fm/fmreceiver/FmReceiverServiceState.java
@@ -0,0 +1,213 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2009-2013 Broadcom Corporation
+ *
+ * This program is the proprietary software of Broadcom Corporation and/or its
+ * licensors, and may only be used, duplicated, modified or distributed
+ * pursuant to the terms and conditions of a separate, written license
+ * agreement executed between you and Broadcom (an "Authorized License").
+ * Except as set forth in an Authorized License, Broadcom grants no license
+ * (express or implied), right to use, or waiver of any kind with respect to
+ * the Software, and Broadcom expressly reserves all rights in and to the
+ * Software and all intellectual property rights therein.
+ * IF YOU HAVE NO AUTHORIZED LICENSE, THEN YOU HAVE NO RIGHT TO USE THIS
+ * SOFTWARE IN ANY WAY, AND SHOULD IMMEDIATELY NOTIFY BROADCOM AND DISCONTINUE
+ * ALL USE OF THE SOFTWARE.
+ *
+ * Except as expressly set forth in the Authorized License,
+ *
+ * 1. This program, including its structure, sequence and organization,
+ * constitutes the valuable trade secrets of Broadcom, and you shall
+ * use all reasonable efforts to protect the confidentiality thereof,
+ * and to use this information only in connection with your use of
+ * Broadcom integrated circuit products.
+ *
+ * 2. TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED
+ * "AS IS" AND WITH ALL FAULTS AND BROADCOM MAKES NO PROMISES,
+ * REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY,
+ * OR OTHERWISE, WITH RESPECT TO THE SOFTWARE. BROADCOM SPECIFICALLY
+ * DISCLAIMS ANY AND ALL IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY,
+ * NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES,
+ * ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
+ * CORRESPONDENCE TO DESCRIPTION. YOU ASSUME THE ENTIRE RISK ARISING
+ * OUT OF USE OR PERFORMANCE OF THE SOFTWARE.
+ *
+ * 3. TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL BROADCOM
+ * OR ITS LICENSORS BE LIABLE FOR
+ * (i) CONSEQUENTIAL, INCIDENTAL, SPECIAL, INDIRECT, OR EXEMPLARY
+ * DAMAGES WHATSOEVER ARISING OUT OF OR IN ANY WAY RELATING TO
+ * YOUR USE OF OR INABILITY TO USE THE SOFTWARE EVEN IF BROADCOM
+ * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES; OR
+ * (ii) ANY AMOUNT IN EXCESS OF THE AMOUNT ACTUALLY PAID FOR THE
+ * SOFTWARE ITSELF OR U.S. $1, WHICHEVER IS GREATER. THESE
+ * LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF
+ * ESSENTIAL PURPOSE OF ANY LIMITED REMEDY.
+ *
+ *****************************************************************************/
+
+package com.broadcom.fm.fmreceiver;
+
+/**
+ * {@hide}
+ */
+public class FmReceiverServiceState
+{
+ static final String FM_RECEIVER_PERM = "android.permission.ACCESS_FM_RECEIVER";
+ protected static final boolean V = true;
+
+ /* Main state machine states. */
+ static final int RADIO_STATE_OFF = 0;
+ static final int RADIO_STATE_STARTING = 1;
+ static final int RADIO_STATE_READY_FOR_COMMAND = 2;
+ static final int RADIO_STATE_STOPPING = 3;
+ static final int RADIO_STATE_BUSY = 4;
+
+ /* The stages in a specific operation before ready for next command. */
+ static final int RADIO_OP_STATE_NONE = 0;
+ static final int RADIO_OP_STATE_STAGE_1 = 1;
+ static final int RADIO_OP_STATE_STAGE_2 = 2;
+ static final int RADIO_OP_STATE_STAGE_3 = 3;
+ static final int RADIO_OP_STATE_STAGE_4 = 4;
+ static final int RADIO_OP_STATE_STAGE_5 = 5;
+
+ /* Handler queue command types. */
+ static final int OPERATION_TIMEOUT = 1; // When a timeout event occurs.
+
+ static final int OPERATION_STATUS_EVENT_CALLBACK = 2;
+ static final int OPERATION_SEARCH_EVENT_CALLBACK = 3;
+ static final int OPERATION_RDS_EVENT_CALLBACK = 4;
+ static final int OPERATION_RDS_DATA_EVENT_CALLBACK = 5;
+ static final int OPERATION_AUDIO_MODE_EVENT_CALLBACK = 6;
+ static final int OPERATION_AUDIO_PATH_EVENT_CALLBACK = 7;
+ static final int OPERATION_REGION_EVENT_CALLBACK = 8;
+ static final int OPERATION_NFE_EVENT_CALLBACK = 9;
+ static final int OPERATION_LIVE_AUDIO_EVENT_CALLBACK = 10;
+ static final int OPERATION_VOLUME_EVENT_CALLBACK = 11;
+
+ /* Timeout for various operations to avoid locking state machine. */
+ static final int OPERATION_TIMEOUT_STARTUP = 10000; // 10 sec startup
+ static final int OPERATION_TIMEOUT_SHUTDOWN = 10000; // 10 sec shutdown
+ static final int OPERATION_TIMEOUT_SEARCH = 20000; // 20 sec search whole band
+ static final int OPERATION_TIMEOUT_NFL = 20000; // 20 sec busy
+ static final int OPERATION_TIMEOUT_GENERIC = 5000; // 5 sec busy
+
+ /* Parameter constraints section. */
+ /** Mask over region bits. */
+ static final int FUNC_REGION_BITMASK = 0x03;
+ /** Mask over RDS or RBDS bits. (also AF) */
+ static final int FUNC_RDS_BITMASK = 0x70;
+ /** Mask over SOFTMUTE bit */
+ static final int FUNC_SOFTMUTE_BITMASK = 0x0100;
+
+ /** Filter for allowable functionality bits. */
+ static final int FUNC_BITMASK = FUNC_REGION_BITMASK | FUNC_RDS_BITMASK;
+
+ /** Minimum allowed frequency code. */
+ static final int MIN_ALLOWED_FREQUENCY_CODE = 1; // 0.01 MHz
+ /** Maximum allowed frequency code. */
+ static final int MAX_ALLOWED_FREQUENCY_CODE = 99999; // 999.99 MHz
+
+ /* SCAN_MODE_BITMASK should be 0x83 to support both FULL SCAN and FAST SCAN */
+ /** Filter for allowable scan mode bits. */
+ static final int SCAN_MODE_BITMASK = 0x00000083;
+
+ /** Minimum allowed signal strength number. */
+ static final int MIN_ALLOWED_SIGNAL_STRENGTH_NUMBER = 0;
+ /** Maximum allowed signal strength number. */
+ static final int MAX_ALLOWED_SIGNAL_STRENGTH_NUMBER = 255;
+
+ /** Minimum allowed RDS condition value. */
+ static final int MIN_ALLOWED_RDS_CONDITION_VALUE = 0;
+ /** Maximum allowed RDS condition value. */
+ static final int MAX_ALLOWED_RDS_CONDITION_VALUE = 255;
+
+ /** Filter for allowable scan mode bits. */
+ static final int RDS_FEATURES_BITMASK = 0x0000007c;
+
+ /** RDS event type ID table. */
+ static final int RDS_ID_PTY_EVT = 2;
+ static final int RDS_ID_PS_EVT = 7;
+ static final int RDS_ID_PTYN_EVT = 8;
+ static final int RDS_ID_RT_EVT = 9;
+
+ /** Filter for allowable RDS mode bits. */
+ static final int RDS_MODE_BITMASK = 0x00000003;
+ static final int RDS_RBDS_BITMASK = 0x00000001;
+ /** Filter for allowable AF mode bits. */
+ static final int AF_MODE_BITMASK = 0x00000001;
+ /** RDS mode native code. */
+ static final int RDS_MODE_NATIVE = 0x00000000;
+ /** RBDS mode native code. */
+ static final int RBDS_MODE_NATIVE = 0x00000001;
+ /** Minimum allowed AF jump threshold value. */
+ static final int MIN_ALLOWED_AF_JUMP_THRESHOLD = 0;
+ /** Maximum allowed AF jump threshold value. */
+ static final int MAX_ALLOWED_AF_JUMP_THRESHOLD = 255;
+
+ /** Filter for allowable audio mode bits. */
+ static final int AUDIO_MODE_BITMASK = 0x00000003;
+
+ /** Filter for allowable audio path bits. */
+ static final int AUDIO_PATH_BITMASK = 0x00000003;
+
+ /** Minimum allowed signal polling time. */
+ static final int MIN_ALLOWED_SIGNAL_POLLING_TIME = 10; /* 10 ms. */
+ /** Maximum allowed signal polling time. */
+ static final int MAX_ALLOWED_SIGNAL_POLLING_TIME = 100000; /* 100 s. */
+
+ /** Minimum allowed SNR Threshold */
+ static final int MIN_ALLOWED_SNR_THRESHOLD = 0;
+ /** Maximum allowed SNR Threshold */
+ static final int MAX_ALLOWED_SNR_THRESHOLD = 31;
+
+ /** Status OK. */
+ static final int BTA_FM_OK = 0;
+ /** RSSI too low. */
+ static final int BTA_FM_SCAN_RSSI_LOW = 1;
+ /** Scan failed. */
+ static final int BTA_FM_SCAN_FAIL = 2;
+ /** Scan was aborted before completion. */
+ static final int BTA_FM_SCAN_ABORT = 3;
+ /** No resources available. */
+ static final int BTA_FM_SCAN_NO_RES = 4;
+ /** General error condition. */
+ static final int BTA_FM_ERR = 5;
+ /** Unsupported functionality error. */
+ static final int BTA_FM_UNSPT_ERR = 6;
+ /** Flag error. */
+ static final int BTA_FM_FLAG_TOUT_ERR = 7;
+ /** Frequency error. */
+ static final int BTA_FM_FREQ_ERR = 8;
+ /** VCMD error. */
+ static final int BTA_FM_VCMD_ERR = 9;
+
+ // state variables
+ //
+ /* Local data cache and associated update freshness. */
+ static int mFreq = 0;
+ static int mRssi = 127;
+ static int mSnr = 127;
+ static boolean mRadioIsOn = false;
+ static int mRdsProgramType = 0;
+ static String mRdsProgramService = "";
+ static String mRdsRadioText = "";
+ static String mRdsProgramTypeName = "";
+ static boolean mIsMute = false;
+ static boolean mSeekSuccess = false;
+ static boolean mRdsOn = false;
+ static boolean mAfOn = false;
+ static int mRdsType = 0; // RDS
+ static int mAlternateFreqHopThreshold = 0;
+ static int mAudioMode = FmProxy.AUDIO_MODE_AUTO;
+ static int mAudioPath = FmProxy.AUDIO_PATH_DIGITAL;
+ static int mWorldRegion = FmProxy.FUNC_REGION_DEFAULT;
+ static int mStepSize = FmProxy.FREQ_STEP_DEFAULT;
+ static boolean mLiveAudioQuality = FmProxy.LIVE_AUDIO_QUALITY_DEFAULT;
+ static int mEstimatedNoiseFloorLevel = FmProxy.NFL_DEFAULT;
+ static int mSignalPollInterval = FmProxy.SIGNAL_POLL_INTERVAL_DEFAULT;
+ static int mDeemphasisTime = FmProxy.DEEMPHASIS_TIME_DEFAULT;
+ static int radio_state = FmReceiverServiceState.RADIO_STATE_OFF;
+
+ /* The current state of the state machine. */
+ static int radio_op_state = FmReceiverServiceState.RADIO_OP_STATE_NONE;
+};
diff --git a/fm/app/src/com/broadcom/fm/fmreceiver/FmService.java b/fm/app/src/com/broadcom/fm/fmreceiver/FmService.java
new file mode 100644
index 0000000..7878c15
--- /dev/null
+++ b/fm/app/src/com/broadcom/fm/fmreceiver/FmService.java
@@ -0,0 +1,608 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2009-2012 Broadcom Corporation
+ *
+ * This program is the proprietary software of Broadcom Corporation and/or its
+ * licensors, and may only be used, duplicated, modified or distributed
+ * pursuant to the terms and conditions of a separate, written license
+ * agreement executed between you and Broadcom (an "Authorized License").
+ * Except as set forth in an Authorized License, Broadcom grants no license
+ * (express or implied), right to use, or waiver of any kind with respect to
+ * the Software, and Broadcom expressly reserves all rights in and to the
+ * Software and all intellectual property rights therein.
+ * IF YOU HAVE NO AUTHORIZED LICENSE, THEN YOU HAVE NO RIGHT TO USE THIS
+ * SOFTWARE IN ANY WAY, AND SHOULD IMMEDIATELY NOTIFY BROADCOM AND DISCONTINUE
+ * ALL USE OF THE SOFTWARE.
+ *
+ * Except as expressly set forth in the Authorized License,
+ *
+ * 1. This program, including its structure, sequence and organization,
+ * constitutes the valuable trade secrets of Broadcom, and you shall
+ * use all reasonable efforts to protect the confidentiality thereof,
+ * and to use this information only in connection with your use of
+ * Broadcom integrated circuit products.
+ *
+ * 2. TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED
+ * "AS IS" AND WITH ALL FAULTS AND BROADCOM MAKES NO PROMISES,
+ * REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY,
+ * OR OTHERWISE, WITH RESPECT TO THE SOFTWARE. BROADCOM SPECIFICALLY
+ * DISCLAIMS ANY AND ALL IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY,
+ * NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES,
+ * ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
+ * CORRESPONDENCE TO DESCRIPTION. YOU ASSUME THE ENTIRE RISK ARISING
+ * OUT OF USE OR PERFORMANCE OF THE SOFTWARE.
+ *
+ * 3. TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL BROADCOM
+ * OR ITS LICENSORS BE LIABLE FOR
+ * (i) CONSEQUENTIAL, INCIDENTAL, SPECIAL, INDIRECT, OR EXEMPLARY
+ * DAMAGES WHATSOEVER ARISING OUT OF OR IN ANY WAY RELATING TO
+ * YOUR USE OF OR INABILITY TO USE THE SOFTWARE EVEN IF BROADCOM
+ * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES; OR
+ * (ii) ANY AMOUNT IN EXCESS OF THE AMOUNT ACTUALLY PAID FOR THE
+ * SOFTWARE ITSELF OR U.S. $1, WHICHEVER IS GREATER. THESE
+ * LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF
+ * ESSENTIAL PURPOSE OF ANY LIMITED REMEDY.
+ *
+ *****************************************************************************/
+
+
+package com.broadcom.fm.fmreceiver;
+
+
+import com.broadcom.fm.fmreceiver.FmProxy;
+import com.broadcom.fm.fmreceiver.FmNativehandler;
+import com.broadcom.fm.fmreceiver.IFmReceiverCallback;
+import com.broadcom.fm.fmreceiver.IFmReceiverService;
+
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+
+public class FmService extends Service {
+ public static final String TAG = "FmService";
+ private static final boolean V = true;
+
+ private FmReceiverServiceStub mBinder;
+ public FmNativehandler mSvcHandler = new FmNativehandler(this);
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ mBinder = new FmReceiverServiceStub(this);
+
+ mBinder.mSvc.mSvcHandler.start();
+ if (V) Log.v(TAG, "FM Service onCreate");
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ return START_NOT_STICKY;
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (V) Log.d(TAG, "onDestroy");
+ mBinder.cleanUp();
+ mBinder = null;
+ if (V) Log.d(TAG, "onDestroy done");
+ }
+
+ public IBinder onBind(Intent intent) {
+ if (V) Log.d(TAG, "Binding service...");
+ return mBinder;
+ }
+
+ private static final class FmReceiverServiceStub extends IFmReceiverService.Stub {
+
+ private static final String TAG = "FmService";
+
+ /* Local execution context track. */
+ private FmService mSvc;
+
+ /**
+ * Constructor used by the system to initialize the operating context of
+ * the server. Should not be called directly by an application. Instead,
+ * use the local application contexts getSystemService() function to get
+ * a reference to a dedicated proxy {@link android.fm#FmProxy} class
+ * instance.
+ */
+ public FmReceiverServiceStub(FmService service) {
+ /* Store parameters locally. */
+ mSvc = service;
+
+ if (V) Log.d(TAG,"FmReceiverServiceStub created"+FmReceiverServiceStub.this+"service"+service);
+ }
+
+ public void cleanUp() {
+ mSvc.mSvcHandler.stop();
+ mSvc.mSvcHandler.finish();
+ mSvc.mSvcHandler =null;
+ }
+
+ /**
+ * Registers an event handler to receive callbacks and callouts from the
+ * server.
+ *
+ * @param cb
+ * the callback to register.
+ */
+ public synchronized void registerCallback(IFmReceiverCallback cb) throws RemoteException {
+ if (mSvc == null) {
+ return;
+ }
+ mSvc.mSvcHandler.registerCallback(cb);
+ }
+
+ /**
+ * Unregisters an event handler to receive callbacks and callouts from
+ * the server.
+ *
+ * @param cb
+ * the callback to unregister.
+ */
+ public synchronized void unregisterCallback(IFmReceiverCallback cb)
+ throws RemoteException {
+ if (mSvc == null) {
+ return;
+ }
+ mSvc.mSvcHandler.unregisterCallback(cb);
+ }
+
+ /**
+ * Turns on the radio and plays audio using the specified functionality
+ * mask.
+ * <p>
+ * After executing this function, the application should wait for a
+ * confirmatory status event callback before calling further API
+ * functions. Furthermore, applications should call the
+ * {@link #turnOffRadio()} function before shutting down.
+ *
+ * @param functionalityMask
+ * is a bitmask comprised of one or more of the following
+ * fields: {@link FmProxy#FUNC_REGION_NA},
+ * {@link FmProxy#FUNC_REGION_JP},
+ * {@link FmProxy#FUNC_REGION_EUR},
+ * {@link FmProxy#FUNC_RDS}, {@link FmProxy#FUNC_RDBS}
+ * and {@link FmProxy#FUNC_AF}
+ * @param clientPackagename
+ * is the the client application package name , this is required for the
+ * fm service to clean up it state when the client process gets killed
+ * eg scenario: when client app dies without calling turnOffRadio()
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero
+ * error code.
+ */
+ @Override
+ public synchronized int turnOnRadio(int functionalityMask,
+ char[] clientPackagename) throws RemoteException{
+ if (mSvc == null) {
+ return FmProxy.STATUS_SERVER_FAIL;
+ }
+ if (V) Log.d(TAG, "turnOnRadio() mSvc:"+mSvc);
+ return mSvc.mSvcHandler.turnOnRadio(functionalityMask,clientPackagename);
+ }
+
+ /**
+ * Turns off the radio.
+ * <p>
+ * After executing this function, the application should wait for a
+ * confirmatory status event callback before shutting down.
+ *
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero
+ * error code.
+ */
+ public synchronized int turnOffRadio() throws RemoteException{
+ if (mSvc == null) {
+ return FmProxy.STATUS_SERVER_FAIL;
+ }
+ return mSvc.mSvcHandler.turnOffRadio();
+ }
+
+ /**
+ * Force cleans the FM Receiver Service
+ *
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero
+ * error code.
+ */
+ public synchronized int cleanupFmService() {
+ if (mSvc == null) {
+ return FmProxy.STATUS_SERVER_FAIL;
+ }
+ return mSvc.mSvcHandler.cleanupFmService();
+ }
+
+ /**
+ * Tunes the radio to a specific frequency. If successful results in a
+ * status event callback.
+ *
+ * @param freq
+ * the frequency to tune to.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero
+ * error code.
+ */
+ public synchronized int tuneRadio(int freq) {
+ if (mSvc == null) {
+ return FmProxy.STATUS_SERVER_FAIL;
+ }
+ return mSvc.mSvcHandler.tuneRadio(freq);
+ }
+
+ /**
+ * Gets current radio status. This results in a status event callback.
+ *
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero
+ * error code.
+ */
+ public synchronized int getStatus() {
+ if (mSvc == null) {
+ return FmProxy.STATUS_ILLEGAL_COMMAND;
+ }
+ return mSvc.mSvcHandler.getStatus();
+ }
+
+ /**
+ * Mutes/unmutes radio audio. If muted the hardware will stop sending
+ * audio. This results in a status event callback.
+ *
+ * @param mute
+ * TRUE to mute audio, FALSE to unmute audio.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero
+ * error code.
+ */
+ public synchronized int muteAudio(boolean mute) {
+ if (mSvc == null) {
+ return FmProxy.STATUS_ILLEGAL_COMMAND;
+ }
+ return mSvc.mSvcHandler.muteAudio(mute);
+ }
+
+ /**
+ * Scans FM toward higher/lower frequency for next clear channel. Will
+ * result in a seek complete event callback.
+ *
+ * @param scanMode
+ * see {@link FmProxy#SCAN_MODE_NORMAL},
+ * {@link FmProxy#SCAN_MODE_DOWN},
+ * {@link FmProxy#SCAN_MODE_UP} and
+ * {@link FmProxy#SCAN_MODE_FULL}.
+ * @param minSignalStrength
+ * minimum signal strength, default =
+ * {@link FmProxy#MIN_SIGNAL_STRENGTH_DEFAULT}
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero
+ * error code.
+ */
+ public synchronized int seekStation(int scanMode, int minSignalStrength) {
+ if (mSvc == null) {
+ return FmProxy.STATUS_SERVER_FAIL;
+ }
+ return mSvc.mSvcHandler.seekStation(scanMode, minSignalStrength);
+ }
+
+ /**
+ * Scans FM toward higher/lower frequency for next clear channel. Will
+ * result in a seek complete event callback. Will seek out a station
+ * that matches the requested value for the desired RDS functionality
+ * support.
+ *
+ * @param scanMode
+ * see {@link FmProxy#SCAN_MODE_NORMAL},
+ * {@link FmProxy#SCAN_MODE_DOWN},
+ * {@link FmProxy#SCAN_MODE_UP} and
+ * {@link FmProxy#SCAN_MODE_FULL}.
+ * @param minSignalStrength
+ * Minimum signal strength, default =
+ * {@link FmProxy#MIN_SIGNAL_STRENGTH_DEFAULT}
+ * @param rdsCondition
+ * the type of RDS condition to scan for.
+ * @param rdsValue
+ * the condition value to match.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero
+ * error code.
+ */
+ public synchronized int seekRdsStation(int scanMode, int minSignalStrength,
+ int rdsCondition, int rdsValue) {
+ if (mSvc == null) {
+ return FmProxy.STATUS_SERVER_FAIL;
+ }
+ return mSvc.mSvcHandler.seekRdsStation(scanMode, minSignalStrength,
+ rdsCondition, rdsValue);
+ }
+
+ /**
+ * Aborts the current station seeking operation if any. Will result in a
+ * seek complete event containing the last scanned frequency.
+ * <p>
+ *
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero
+ * error code.
+ */
+ public synchronized int seekStationAbort() {
+ if (mSvc == null) {
+ return FmProxy.STATUS_SERVER_FAIL;
+ }
+ return mSvc.mSvcHandler.seekStationAbort();
+ }
+
+ /**
+ * Scans FM toward higher/lower frequency for next clear channel depending on
+ * the scanDirection.will do wrap around when reached to mMaxFreq/mMinFreq,
+ * when no wrap around is needed, use the low_bound or high_bound as endFrequency.
+ * Will result in a seek complete event callback.
+ * <p>
+ *
+ * @param startFrequency
+ * Starting frequency of search operation range.
+ * @param endFrequency
+ * Ending frequency of search operation
+ * @param minSignalStrength
+ * Minimum signal strength, default =
+ * {@link #MIN_SIGNAL_STRENGTH_DEFAULT}
+ * @param scanDirection
+ * the direction to search in, it can only be either
+ * {@link #SCAN_MODE_UP} and {@link #SCAN_MODE_DOWN}.
+ * @param scanMethod
+ * see {@link #SCAN_MODE_NORMAL}, {@link #SCAN_MODE_FAST},
+ * @param multi_channel
+ * Is multiple channels are required, or only find next valid channel(seek).
+ * @param rdsType
+ * the type of RDS condition to scan for.
+ * @param rdsTypeValue
+ * the condition value to match.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error
+ * code.
+ *
+ * @see IFmReceiverEventHandler.onSeekCompleteEvent().
+ */
+ public synchronized int seekStationCombo (int startFrequency, int endFrequency,
+ int minSignalStrength, int scanDirection,
+ int scanMethod, boolean multi_channel, int rdsType, int rdsTypeValue) {
+ if (mSvc == null) {
+ return FmProxy.STATUS_SERVER_FAIL;
+ }
+ return mSvc.mSvcHandler.seekStationCombo (startFrequency, endFrequency,
+ minSignalStrength, scanDirection, scanMethod, multi_channel,
+ rdsType, rdsTypeValue);
+ }
+
+ /**
+ * Enables/disables RDS/RDBS feature and AF algorithm. Will result in an
+ * RDS mode event callback.
+ * <p>
+ *
+ * @param rdsMode
+ * Turns on the RDS or RBDS. See
+ * {@link FmProxy#RDS_MODE_OFF},
+ * {@link FmProxy#RDS_MODE_DEFAULT_ON},
+ * {@link FmProxy#RDS_MODE_RDS_ON},
+ * {@link FmProxy#RDS_MODE_RDBS_ON}
+ * @param rdsFields
+ * the mask specifying which types of RDS data to signal
+ * back.
+ * @param afmode
+ * enables AF algorithm if True. Disables it if False
+ * @param afThreshold
+ * the RSSI threshold when the AF should jump frequencies.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero
+ * error code.
+ */
+ public synchronized int setRdsMode(int rdsMode, int rdsFeatures, int afMode,
+ int afThreshold) {
+ if (mSvc == null) {
+ return FmProxy.STATUS_SERVER_FAIL;
+ }
+ return mSvc.mSvcHandler.setRdsMode(rdsMode, rdsFeatures, afMode, afThreshold);
+ }
+
+ /**
+ * Configures FM audio mode to be mono, stereo or blend. Will result in
+ * an audio mode event callback.
+ *
+ * @param audioMode
+ * the audio mode such as stereo or mono. The following
+ * values should be used {@link FmProxy#AUDIO_MODE_AUTO},
+ * {@link FmProxy#AUDIO_MODE_STEREO},
+ * {@link FmProxy#AUDIO_MODE_MONO} or
+ * {@link FmProxy#AUDIO_MODE_BLEND}.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero
+ * error code.
+ */
+ public synchronized int setAudioMode(int audioMode) {
+ if (mSvc == null) {
+ return FmProxy.STATUS_SERVER_FAIL;
+ }
+ return mSvc.mSvcHandler.setAudioMode(audioMode);
+ }
+
+ /**
+ * Configures FM audio path to AUDIO_PATH_NONE, AUDIO_PATH_SPEAKER,
+ * AUDIO_PATH_WIRED_HEADSET or AUDIO_PATH_DIGITAL. Will result in an
+ * audio path event callback.
+ *
+ * @param audioPath
+ * the audio path such as AUDIO_PATH_NONE,
+ * AUDIO_PATH_SPEAKER, AUDIO_PATH_WIRED_HEADSET or
+ * AUDIO_PATH_DIGITAL. The following values should be used
+ * {@link FmProxy#AUDIO_PATH_NONE},
+ * {@link FmProxy#AUDIO_PATH_SPEAKER},
+ * {@link FmProxy#AUDIO_PATH_WIRED_HEADSET} or
+ * {@link FmProxy#AUDIO_PATH_DIGITAL}.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero
+ * error code.
+ */
+ public synchronized int setAudioPath(int audioPath) {
+ if (mSvc == null) {
+ return FmProxy.STATUS_SERVER_FAIL;
+ }
+ return mSvc.mSvcHandler.setAudioPath(audioPath);
+ }
+
+ /**
+ * Sets the minimum frequency step size to use when scanning for
+ * stations. This function does not result in a status callback and the
+ * calling application should therefore keep track of this setting.
+ *
+ * @param stepSize
+ * a frequency interval set to
+ * {@link FmProxy#FREQ_STEP_100KHZ} or
+ * {@link FmProxy#FREQ_STEP_50KHZ}.
+ *
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero
+ * error code.
+ */
+ public synchronized int setStepSize(int stepSize) {
+ if (mSvc == null) {
+ return FmProxy.STATUS_SERVER_FAIL;
+ }
+ return mSvc.mSvcHandler.setStepSize(stepSize);
+ }
+
+ /**
+ * Sets the volume to use.
+ *
+ * @param volume
+ * range from 0 to 0x100
+ *
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero
+ * error code.
+ */
+ public synchronized int setFMVolume(int volume) {
+ if (mSvc == null) {
+ return FmProxy.STATUS_SERVER_FAIL;
+ }
+ return mSvc.mSvcHandler.setFMVolume(volume);
+ }
+
+ /**
+ * Sets the SNR threshold value to use.
+ *
+ * @param snrThreshold The SNR threshold value
+ *
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error code.
+ */
+ public synchronized int setSnrThreshold(int snrThreshold) {
+ if (mSvc == null) {
+ return FmProxy.STATUS_SERVER_FAIL;
+ }
+ return mSvc.mSvcHandler.setSnrThreshold(snrThreshold);
+ }
+
+ /**
+ * Sets a the world frequency region and deemphasis time. This results
+ * in a world region event callback.
+ *
+ * @param worldRegion
+ * the world region the FM receiver is located. Set to
+ * {@link FmProxy#FUNC_REGION_NA},
+ * {@link FmProxy#FUNC_REGION_EUR} or
+ * {@link FmProxy#FUNC_REGION_JP}.
+ * @param deemphasisTime
+ * the deemphasis time that can be set to either
+ * {@link FmProxy#DEEMPHASIS_50U} or
+ * {@link FmProxy#DEEMPHASIS_75U}.
+ *
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero
+ * error code.
+ */
+ public synchronized int setWorldRegion(int worldRegion, int deemphasisTime) {
+ if (mSvc == null) {
+ return FmProxy.STATUS_SERVER_FAIL;
+ }
+ return mSvc.mSvcHandler.setWorldRegion(worldRegion, deemphasisTime);
+ }
+
+ /**
+ * Estimates the current frequencies noise floor level. Generates an
+ * Estimated NFL Event when complete. The returned NFL value can be used
+ * to determine which minimum signal strength to use seeking stations.
+ *
+ * @param estimatedNoiseFloorLevel
+ * Estimate noise floor to {@link FmProxy#NFL_LOW},
+ * {@link FmProxy#NFL_MED} or {@link FmRecei
+ * -vver#NFL_FINE}.
+ *
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero
+ * error code.
+ */
+ public synchronized int estimateNoiseFloorLevel(int nflLevel) throws RemoteException {
+ if (mSvc == null) {
+ return FmProxy.STATUS_SERVER_FAIL;
+ }
+ return mSvc.mSvcHandler.estimateNoiseFloorLevel(nflLevel);
+ }
+
+ /**
+ * Sets the live audio polling function that can provide RSSI data on
+ * the currently tuned frequency at specified intervals.
+ *
+ * @param liveAudioPolling
+ * enable/disable live audio data quality updating.
+ * @param signalPollInterval
+ * time between RSSI signal polling in milliseconds.
+ *
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero
+ * error code.
+ */
+ public synchronized int setLiveAudioPolling(boolean liveAudioPolling,
+ int signalPollInterval)
+ throws RemoteException {
+ if (mSvc == null) {
+ return FmProxy.STATUS_SERVER_FAIL;
+ }
+ return mSvc.mSvcHandler.setLiveAudioPolling(liveAudioPolling, signalPollInterval);
+ }
+
+ /**
+ * Returns the FM Audio Mode state.
+ * @param none
+ * @return {@link FmProxy#AUDIO_MODE_AUTO},
+ * {@link FmProxy#AUDIO_MODE_STEREO},
+ * {@link FmProxy#AUDIO_MODE_MONO} or
+ * {@link FmProxy#AUDIO_MODE_BLEND}.
+ */
+ public synchronized int getMonoStereoMode() throws RemoteException {
+ if (mSvc == null) {
+ return FmProxy.STATUS_SERVER_FAIL;
+ }
+ return mSvc.mSvcHandler.getMonoStereoMode();
+ }
+
+ /**
+ * Returns the present tuned FM Frequency
+ * @param none
+ * @return Tuned frequency
+ */
+ public synchronized int getTunedFrequency() throws RemoteException {
+ if (mSvc == null) {
+ return FmProxy.STATUS_SERVER_FAIL;
+ }
+ return mSvc.mSvcHandler.getTunedFrequency();
+ }
+
+ /**
+ * Returns whether MUTE is turned ON or OFF
+ * @param none
+ * @return false if MUTE is OFF ; true otherwise
+ */
+ public synchronized boolean getIsMute() throws RemoteException {
+ if (mSvc == null) {
+ return false;
+ }
+ return mSvc.mSvcHandler.getIsMute();
+ }
+
+ public void init() throws RemoteException {
+
+ }
+ public boolean getRadioIsOn() throws RemoteException {
+ if (mSvc == null) {
+ return false;
+ }
+ return mSvc.mSvcHandler.getRadioIsOn();
+ }
+
+ }
+
+}
diff --git a/fm/lib/Android.mk b/fm/lib/Android.mk
new file mode 100644
index 0000000..c317c4c
--- /dev/null
+++ b/fm/lib/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src) \
+ src/com/broadcom/fm/fmreceiver/IFmReceiverService.aidl \
+ src/com/broadcom/fm/fmreceiver/IFmReceiverCallback.aidl
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE := com.broadcom.fm
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/fm/lib/src/com/broadcom/fm/fmreceiver/FmProxy.java b/fm/lib/src/com/broadcom/fm/fmreceiver/FmProxy.java
new file mode 100644
index 0000000..29e3ae0
--- /dev/null
+++ b/fm/lib/src/com/broadcom/fm/fmreceiver/FmProxy.java
@@ -0,0 +1,1112 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2009-2012 Broadcom Corporation
+ *
+ * This program is the proprietary software of Broadcom Corporation and/or its
+ * licensors, and may only be used, duplicated, modified or distributed
+ * pursuant to the terms and conditions of a separate, written license
+ * agreement executed between you and Broadcom (an "Authorized License").
+ * Except as set forth in an Authorized License, Broadcom grants no license
+ * (express or implied), right to use, or waiver of any kind with respect to
+ * the Software, and Broadcom expressly reserves all rights in and to the
+ * Software and all intellectual property rights therein.
+ * IF YOU HAVE NO AUTHORIZED LICENSE, THEN YOU HAVE NO RIGHT TO USE THIS
+ * SOFTWARE IN ANY WAY, AND SHOULD IMMEDIATELY NOTIFY BROADCOM AND DISCONTINUE
+ * ALL USE OF THE SOFTWARE.
+ *
+ * Except as expressly set forth in the Authorized License,
+ *
+ * 1. This program, including its structure, sequence and organization,
+ * constitutes the valuable trade secrets of Broadcom, and you shall
+ * use all reasonable efforts to protect the confidentiality thereof,
+ * and to use this information only in connection with your use of
+ * Broadcom integrated circuit products.
+ *
+ * 2. TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED
+ * "AS IS" AND WITH ALL FAULTS AND BROADCOM MAKES NO PROMISES,
+ * REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY,
+ * OR OTHERWISE, WITH RESPECT TO THE SOFTWARE. BROADCOM SPECIFICALLY
+ * DISCLAIMS ANY AND ALL IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY,
+ * NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES,
+ * ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
+ * CORRESPONDENCE TO DESCRIPTION. YOU ASSUME THE ENTIRE RISK ARISING
+ * OUT OF USE OR PERFORMANCE OF THE SOFTWARE.
+ *
+ * 3. TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL BROADCOM
+ * OR ITS LICENSORS BE LIABLE FOR
+ * (i) CONSEQUENTIAL, INCIDENTAL, SPECIAL, INDIRECT, OR EXEMPLARY
+ * DAMAGES WHATSOEVER ARISING OUT OF OR IN ANY WAY RELATING TO
+ * YOUR USE OF OR INABILITY TO USE THE SOFTWARE EVEN IF BROADCOM
+ * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES; OR
+ * (ii) ANY AMOUNT IN EXCESS OF THE AMOUNT ACTUALLY PAID FOR THE
+ * SOFTWARE ITSELF OR U.S. $1, WHICHEVER IS GREATER. THESE
+ * LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF
+ * ESSENTIAL PURPOSE OF ANY LIMITED REMEDY.
+ *
+ *****************************************************************************/
+
+
+package com.broadcom.fm.fmreceiver;
+
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.util.Log;
+import android.content.Context;
+
+import com.broadcom.fm.fmreceiver.IFmReceiverCallback;
+import com.broadcom.fm.fmreceiver.IFmReceiverService;
+import com.broadcom.fm.fmreceiver.IFmReceiverEventHandler;
+import com.broadcom.fm.fmreceiver.IFmProxyCallback;
+
+import android.content.ComponentName;
+import android.content.ServiceConnection;
+
+/**
+ * FmProxy is the Java API entry point to issue commands to FM receiver
+ * hardware. After a command is issued one or more FmReceiverEvents will be
+ * issued to the requested client application handler. The application must
+ * implement the {@link IFmReceiverEventHandler} interface to receive the
+ * results of requested operations.
+ * <p>
+ * PUBLIC FM PROXY API
+ * <p>
+ * An FmProxy object acts as a proxy to the FmService/FmTransmitterService etc
+ * <p>
+ * Usage:
+ * <p>
+ * First create a reference to the FM Proxy system service:
+ * <p>
+ * <code> FmProxy mFmPRoxu = (FmProxy) FmProxy.getProxy(); </code>
+ * <p>
+ * Then register as an event handler:
+ * <p>
+ * <code> mFmReceiver.registerEventHandler(this); </code>
+ * <p>
+ * The application should then call the turnOnRadio() function and wait for a
+ * confirmation status event before calling further functions.
+ * <p>
+ * On closing the high level application, turnOffRadio() should be called to
+ * disconnect from the FmService. A confirmation status event should be
+ * received before the high level application is terminated.
+ * <p>
+ * This class first acquires an interface to the FmService module.
+ * This allows the FmProxy instance
+ * to act as a proxy to the FmService through which all FM Proxy
+ * related commands are relayed. The FmService answers the FmProxy
+ * instance by issuing FmServiceEvents to the FmProxy instance. (In
+ * practice using multiple synchronized callback functions.)
+ * {@hide}
+ */
+public final class FmProxy {
+
+ private static final String TAG = "FmProxy";
+
+ /* FM functionality mask bit constants. */
+
+ /** This mask sets the world region to North America. */
+ public static final int FUNC_REGION_NA = 0x00; /*
+ * bit0/bit1/bit2: North
+ * America.
+ */
+ /** This mask sets the world region to Europe. */
+ public static final int FUNC_REGION_EUR = 0x01; /* bit0/bit1/bit2: Europe. */
+ /** This mask sets the world region to Japan. */
+ public static final int FUNC_REGION_JP = 0x02; /* bit0/bit1/bit2: Japan. */
+ /** This mask sets the world region to Japan II (Upper region band). */
+ public static final int FUNC_REGION_JP_II = 0x03; /* bit0/bit1/bit2: Japan. */
+
+ /** This mask enables RDS. */
+ public static final int FUNC_RDS = 1 << 4; /* bit4: RDS functionality */
+ /** This mask enables RDBS. */
+ public static final int FUNC_RBDS = 1 << 5; /*
+ * bit5: RDBS functionality,
+ * exclusive with RDS bit
+ */
+ /** This mask enables the Alternate Frequency RDS feature. */
+ public static final int FUNC_AF = 1 << 6; /* bit6: AF functionality */
+ /** This mask enables SOFTMUTE. */
+ public static final int FUNC_SOFTMUTE = 1 << 8; /* bit8: SOFTMUTE functionality */
+
+ /* FM audio output mode. */
+ /**
+ * Allows the radio to automatically select between Mono and Stereo audio
+ * output.
+ */
+ public static final int AUDIO_MODE_AUTO = 0; /* Auto blend by default. */
+ /** Forces Stereo mode audio. */
+ public static final int AUDIO_MODE_STEREO = 1; /* Manual stereo switch. */
+ /** Forces Mono mode audio. */
+ public static final int AUDIO_MODE_MONO = 2; /* Manual mono switch. */
+ /** Allows Stereo mode audio with blend activation. */
+ public static final int AUDIO_MODE_BLEND = 3; /* Deprecated. */
+ // TODO: phase out previous line in favor of next line.
+ public static final int AUDIO_MODE_SWITCH = 3; /* Switch activated. */
+ /** No FM routing */
+ public static final int AUDIO_PATH_NONE = 0; /* No FM routing */
+ /** FM routing over DAC */
+ public static final int AUDIO_PATH_ANALOG = 1; /* FM routing over DAC */
+ /** FM routing over I2S */
+ public static final int AUDIO_PATH_DIGITAL = 2; /* FM routing over I2S */
+
+ /* FM audio quality. */
+ /**
+ * The audio quality of reception.
+ */
+ /** Using Stereo mode audio quality. */
+ public static final int AUDIO_QUALITY_STEREO = 1; /* Manual stereo switch. */
+ /** Using Mono mode audio quality. */
+ public static final int AUDIO_QUALITY_MONO = 2; /* Manual mono switch. */
+ /** Using Blend mode audio quality. */
+ public static final int AUDIO_QUALITY_BLEND = 4; /*
+ * Auto stereo, and switch
+ * activated.
+ */
+
+ /* FM scan mode. */
+ /** This sets default direction scanning when seeking stations. */
+ public static final int SCAN_MODE_NORMAL = 0x00;
+ public static final int SCAN_MODE_FAST = 0x01;
+
+ /** This sets scanning to go downwards when seeking stations. */
+ public static final int SCAN_MODE_DOWN = 0x00;
+
+ /** This sets scanning to go upwards when seeking stations. */
+ public static final int SCAN_MODE_UP = 0x80;
+
+ /** This sets scanning to cover the whole bandwidth and return multiple hits. */
+ public static final int SCAN_MODE_FULL = 0x82;
+
+ /* Deemphasis time */
+ /** This sets deemphasis to the European default. */
+ public static final int DEEMPHASIS_50U = 0; /*
+ * 6th bit in FM_AUDIO_CTRL0 set
+ * to 0, Europe default
+ */
+ /** This sets deemphasis to the US default. */
+ public static final int DEEMPHASIS_75U = 1 << 6; /*
+ * 6th bit in
+ * FM_AUDIO_CTRL0 set to 1,
+ * US default
+ */
+
+ /* Step type for searching */
+ /** This sets the frequency interval to 100 KHz when seeking stations. */
+ public static final int FREQ_STEP_100KHZ = 0x00;
+ /** This sets the frequency interval to 50 KHz when seeking stations. */
+ public static final int FREQ_STEP_50KHZ = 0x10;
+
+ public static final int FM_VOLUME_MAX = 255;
+
+ /* Noise floor level */
+ /** This sets the Noise Floor Level to LOW. */
+ public static final int NFL_LOW = 0;
+ /** This sets the Noise Floor Level to MEDIUM. */
+ public static final int NFL_MED = 1;
+ /** This sets the Noise Floor Level to FINE. */
+ public static final int NFL_FINE = 2;
+
+ /* RDS RDBS type */
+ /** This deactivates all RDS and RDBS functionality. */
+ public static final int RDS_MODE_OFF = 0;
+ /** This activates RDS or RDBS as appropriate. */
+ public static final int RDS_MODE_DEFAULT_ON = 1;
+ /** This activates RDS. */
+ public static final int RDS_MODE_RDS_ON = 2;
+ /** This activates RDBS. */
+ public static final int RDS_MODE_RBDS_ON = 3;
+
+ /* RDS condition type */
+ /** Selects no PTY or TP functionality. */
+ public static final int RDS_COND_NONE = 0;
+ /** Activates RDS PTY capability. */
+ public static final int RDS_COND_PTY = 1;
+ /** Activates RDS TP capability. */
+ public static final int RDS_COND_TP = 2;
+ /* Check this again! RDS PTY (Protram types) code, 0 ~ 31, when the PTY is specified in mPendingRdsType. */
+ public static final int RDS_COND_PTY_VAL = 0;
+
+ /* RDS feature values. */
+ /** Specifies the Program Service feature. */
+ public static final int RDS_FEATURE_PS = 4;
+ /** Specifies the Program Type feature. */
+ public static final int RDS_FEATURE_PTY = 8;
+ /** Specifies the Traffic Program feature. */
+ public static final int RDS_FEATURE_TP = 16;
+ /** Specifies the Program Type Name feature. */
+ public static final int RDS_FEATURE_PTYN = 32;
+ /** Specifies the Radio Text feature. */
+ public static final int RDS_FEATURE_RT = 64;
+
+ /* AF Modes. */
+ /** Disables AF capability. */
+ public static final int AF_MODE_OFF = 0;
+ /** Enables AF capability. */
+ public static final int AF_MODE_ON = 1;
+
+ /* The default constants applied on system startup. */
+ /**
+ * Specifies default minimum signal strength that will be identified as a
+ * station when scanning.
+ * */
+ public static final int MIN_SIGNAL_STRENGTH_DEFAULT = 105;
+ /** Specifies default radio functionality. */
+ public static final int FUNCTIONALITY_DEFAULT = FUNC_REGION_NA;
+ /** Specifies default world frequency region. */
+ public static final int FUNC_REGION_DEFAULT = FUNC_REGION_NA;
+ /** Specifies default frequency scanning step to use. */
+ public static final int FREQ_STEP_DEFAULT = FREQ_STEP_100KHZ;
+ /** Specifies if live audio quality sampling is enabled by default. */
+ public static final boolean LIVE_AUDIO_QUALITY_DEFAULT = false;
+ /** Specifies the default estimated Noise Floor Level. */
+ public static final int NFL_DEFAULT = NFL_MED;
+ /** Specifies the default signal poll interval in ms. */
+ public static final int SIGNAL_POLL_INTERVAL_DEFAULT = 100;
+ /** Specifies the default signal poll interval in ms. */
+ public static final int DEEMPHASIS_TIME_DEFAULT = DEEMPHASIS_75U;
+ /** Default Alternate Frequency mode (DISABLED). */
+ public static final int AF_MODE_DEFAULT = AF_MODE_OFF;
+
+ /** Minimum allowed SNR Threshold */
+ public static final int FM_MIN_SNR_THRESHOLD = 0;
+ /** Maximum allowed SNR Threshold */
+ public static final int FM_MAX_SNR_THRESHOLD = 31;
+
+ /* Return status codes. */
+ /** Function executed correctly. Parameters checked OK. */
+ public static final int STATUS_OK = 0;
+ /** General nonspecific error occurred. */
+ public static final int STATUS_FAIL = 1;
+ /** Server call resulted in exception. */
+ public static final int STATUS_SERVER_FAIL = 2;
+ /** Function could not be executed at this time. */
+ public static final int STATUS_ILLEGAL_COMMAND = 3;
+ /** Function parameters are out of allowed range. */
+ public static final int STATUS_ILLEGAL_PARAMETERS = 4;
+
+ /* Internal reference to client application event handler. */
+ private IFmReceiverEventHandler mEventHandler = null;
+
+ /* Generic remote service reference. */
+ private IFmReceiverService mService;
+
+ /** Callback handler **/
+ private IFmReceiverCallback mCallback;
+
+ /**
+ * Get a proxy to the this service
+ * @param cb
+ * @return
+ */
+
+ public static boolean getProxy(Context ctx, IFmProxyCallback cb) {
+ boolean status = false;
+ FmProxy p = null;
+
+ try {
+ p = new FmProxy(ctx, cb);
+ } catch (Throwable t) {
+ Log.e(TAG, "Unable to get FM Proxy", t);
+ return false;
+ }
+
+ return true;
+ }
+
+ public FmProxy(Context ctx, IFmProxyCallback cb) {
+ Log.d(TAG, "FmProxy object created obj ="+this);
+ mContext = ctx;
+ mProxyAvailCb = cb;
+
+ Intent intent = new Intent();
+ intent.setAction(IFmReceiverService.class.getName());
+ intent.setPackage("com.android.bluetooth");
+ if (!mContext.bindService(intent, mConnection, Context.BIND_AUTO_CREATE)) {
+ Log.e(TAG, "Could not bind to IFmReceiverService Service");
+ }
+ }
+
+ /**
+ * Initialize the proxy with the service
+ * @hide
+ */
+ protected boolean init(IBinder service) {
+ try {
+ mService = (IFmReceiverService) IFmReceiverService.Stub.asInterface(service);
+ return true;
+ } catch (Throwable t) {
+ Log.e(TAG, "Unable to initialize BluetoothFM proxy with service", t);
+ return false;
+ }
+ }
+
+ /**
+ * Register a callback event handler to receive OPP events.
+ * <p/>
+ * @param handler the application handler to use for FM Receiver
+ * to use for handling callback events.
+ */
+ public synchronized void registerEventHandler(IFmReceiverEventHandler handler) {
+ Log.v(TAG, "registerEventHandler()");
+
+ // Store the client event handler
+ mEventHandler = handler;
+
+ if (mCallback == null) {
+ mCallback = new FmReceiverCallback();
+ try {
+ mService.registerCallback(mCallback);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Error registering callback handler", e);
+ }
+ }
+ }
+
+ public synchronized void unregisterEventHandler() {
+ Log.v(TAG, "unregisterEventHandler()");
+
+ mEventHandler = null;
+
+ try {
+ mService.unregisterCallback(mCallback);
+ } catch (Throwable t) {
+ Log.e(TAG, "Unable to unregister callback", t);
+ }
+ }
+
+ public synchronized void finish() {
+ if (mEventHandler != null) {
+ mEventHandler = null;
+ }
+
+ if (mCallback != null && mService != null) {
+ try {
+ mService.unregisterCallback(mCallback);
+ } catch (Throwable t) {
+ Log.e(TAG, "Unable to unregister callback", t);
+ }
+ mCallback = null;
+ }
+
+ if (mContext != null) {
+ mContext.unbindService(mConnection);
+ mContext = null;
+ mService = null;
+ }
+ }
+
+ /**
+ * Turns on the radio and plays audio using the specified functionality
+ * mask.
+ * <p>
+ * After executing this function, the application should wait for a
+ * confirmatory status event callback before calling further API functions.
+ * Furthermore, applications should call the {@link #turnOffRadio()}
+ * function before shutting down.
+ * @param functionalityMask
+ * is a bitmask comprised of one or more of the following fields:
+ * {@link #FUNC_REGION_NA}, {@link #FUNC_REGION_JP},
+ * {@link #FUNC_REGION_EUR}, {@link #FUNC_RDS},
+ * {@link #FUNC_RBDS} and {@link #FUNC_AF}
+ *
+ * @param clientPackagename
+ * is the the client application package name , this is required for the
+ * fm service to clean up it state when the client process gets killed
+ * eg scenario: when client app dies without calling turnOffRadio()
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error
+ * code.
+ * @see IFmReceiverEventHandler.onStatusEvent().
+ */
+ public synchronized int turnOnRadio(int functionalityMask) {
+ String clientPackagename = mContext.getPackageName();
+ int returnCode = STATUS_SERVER_FAIL;
+
+ Log.d(TAG,"Fmproxy"+FmProxy.this+"mService"+mService);
+ /* Request this action from the server. */
+ try {
+ returnCode = mService.turnOnRadio(functionalityMask, clientPackagename.toCharArray());
+ } catch (RemoteException e) {
+ Log.e(TAG, "turnOnRadio() failed", e);
+ }
+
+ return returnCode;
+ }
+
+ /**
+ * Turns on the radio and plays audio.
+ * <p>
+ * After executing this function, the application should wait for a
+ * confirmatory status event callback before calling further API functions.
+ * Furthermore, applications should call the {@link #turnOffRadio()}
+ * function before shutting down.
+ * @param clientPackagename
+ * is the the client application package name , this is required for the
+ * fm service to clean up it state when the client process gets killed
+ * eg scenario: when client app dies without calling turnOffRadio()
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error
+ * code.
+ *
+ * @see IFmReceiverEventHandler.onStatusEvent().
+ */
+ public int turnOnRadio() {
+ return turnOnRadio(FUNCTIONALITY_DEFAULT);
+ }
+
+ /**
+ * Turns off the radio.
+ * <p>
+ * After executing this function, the application should wait for a
+ * confirmatory status event callback before shutting down.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error
+ * code.
+ * @see IFmReceiverEventHandler.onStatusEvent().
+ */
+ public synchronized int turnOffRadio() {
+ int returnCode = STATUS_SERVER_FAIL;
+ /* Request this action from the server. */
+ try {
+ returnCode = mService.turnOffRadio();
+ } catch (RemoteException e) {
+ Log.e(TAG, "turnOffRadio() failed", e);
+ return returnCode;
+ }
+
+ return returnCode;
+ }
+
+ /**
+ * Initiates forced clean-up of FMReceiverService from the application
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error
+ * code.
+ */
+ public synchronized int cleanupFmService() {
+ int returnCode = STATUS_SERVER_FAIL;
+ /* Request this action from the server. */
+ try {
+ mService.cleanupFmService();
+ } catch (RemoteException e) {
+ Log.e(TAG, "cleanupFmService() failed", e);
+ }
+ Log.i(TAG, "cleanup triggered");
+ return returnCode;
+ }
+
+ /**
+ * Tunes radio to a specific frequency. If successful results in a status
+ * event callback.
+ * @param freq
+ * the frequency to tune to.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error
+ * code.
+ * @see IFmReceiverEventHandler.onStatusEvent().
+ */
+ public synchronized int tuneRadio(int freq) {
+ int returnCode = STATUS_SERVER_FAIL;
+ /* Request this action from the server. */
+ try {
+ returnCode = mService.tuneRadio(freq);
+ } catch (RemoteException e) {
+ Log.e(TAG, "tuneRadio() failed", e);
+ }
+
+ return returnCode;
+ }
+
+ /**
+ * Gets current radio status. This results in a status event callback.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error
+ * code.
+ * @see IFmReceiverEventHandler.onStatusEvent().
+ */
+ public synchronized int getStatus() {
+ int returnCode = STATUS_SERVER_FAIL;
+ /* Request this action from the server. */
+ try {
+ returnCode = mService.getStatus();
+ } catch (RemoteException e) {
+ Log.e(TAG, "getStatus() failed", e);
+ }
+
+ return returnCode;
+ }
+
+ /**
+ * Get the On/Off status of FM radio receiver module.
+ * @return true if radio is on, otherwise returns false.
+ */
+ public boolean getRadioIsOn() {
+ boolean returnStatus = false;
+ try {
+ returnStatus = mService.getRadioIsOn();
+ } catch (RemoteException e) {
+ Log.e(TAG, "getRadioIsOn() failed", e);
+ }
+ return returnStatus;
+ }
+
+ /**
+ * Get the Audio Mode -
+ * {@link FmProxy#AUDIO_MODE_AUTO},
+ * {@link FmProxy#AUDIO_MODE_STEREO},
+ * {@link FmProxy#AUDIO_MODE_MONO} or
+ * {@link FmProxy#AUDIO_MODE_BLEND}.
+ * @param none
+ * @return the mAudioMode
+ */
+ public int getMonoStereoMode() {
+ int returnStatus = AUDIO_MODE_AUTO;
+ try {
+ returnStatus = mService.getMonoStereoMode();
+ } catch (RemoteException e) {
+ Log.e(TAG, "getMonoStereoMode() failed", e);
+ }
+ return returnStatus;
+ }
+
+ /**
+ * Returns the present tuned FM Frequency
+ * @param none
+ * @return the mFreq
+ */
+ public int getTunedFrequency() {
+ int returnStatus = 0;
+ try {
+ returnStatus = mService.getTunedFrequency();
+ } catch (RemoteException e) {
+ Log.e(TAG, "getTunedFrequency() failed", e);
+ }
+ return returnStatus;
+ }
+
+ /**
+ * Returns whether MUTE is turned ON or OFF
+ * @param none
+ * @return false if MUTE is OFF ; true otherwise
+ */
+ public boolean getIsMute() {
+ boolean returnStatus = false;
+ try {
+ returnStatus = mService.getIsMute();
+ } catch (RemoteException e) {
+ Log.e(TAG, "getIsMute() failed", e);
+ }
+ return returnStatus;
+ }
+
+ /**
+ * Mutes/unmutes radio audio. If muted the hardware will stop sending audio.
+ * This results in a status event callback.
+ * @param mute
+ * True to mute audio, False to unmute audio.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error
+ * code.
+ * @see IFmReceiverEventHandler.onStatusEvent().
+ */
+ public synchronized int muteAudio(boolean mute) {
+ int returnCode = STATUS_SERVER_FAIL;
+ /* Request this action from the server. */
+ try {
+ returnCode = mService.muteAudio(mute);
+ } catch (RemoteException e) {
+ Log.e(TAG, "muteAudio() failed", e);
+ }
+
+ return returnCode;
+ }
+
+ /**
+ * Scans FM toward higher/lower frequency for next clear channel. Will
+ * result in a seek complete event callback.
+ * <p>
+ *
+ * @param scanMode
+ * see {@link #SCAN_MODE_NORMAL}, {@link #SCAN_MODE_DOWN},
+ * {@link #SCAN_MODE_UP} and {@link #SCAN_MODE_FULL}.
+ * @param minSignalStrength
+ * Minimum signal strength, default =
+ * {@link #MIN_SIGNAL_STRENGTH_DEFAULT}
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error
+ * code.
+ *
+ * @see IFmReceiverEventHandler.onSeekCompleteEvent().
+ */
+ public synchronized int seekStation(int scanMode, int minSignalStrength) {
+ int returnCode = STATUS_SERVER_FAIL;
+ /* Request this action from the server. */
+ try {
+ returnCode = mService.seekStation(scanMode, minSignalStrength);
+ } catch (RemoteException e) {
+ Log.e(TAG, "seekStation() failed", e);
+ }
+
+ return returnCode;
+ }
+
+ /**
+ * Scans FM toward higher/lower frequency for next clear channel. Will
+ * result in a seek complete event callback.
+ * <p>
+ * Scans with default signal strength setting =
+ * {@link #MIN_SIGNAL_STRENGTH_DEFAULT}
+ * @param scanMode
+ * see {@link #SCAN_MODE_NORMAL}, {@link #SCAN_MODE_DOWN},
+ * {@link #SCAN_MODE_UP} and {@link #SCAN_MODE_FULL}.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error
+ * code.
+ * @see IFmReceiverEventHandler.onSeekCompleteEvent().
+ */
+ public int seekStation(int scanMode) {
+ return seekStation(scanMode, MIN_SIGNAL_STRENGTH_DEFAULT);
+ }
+
+ /**
+ * Scans FM toward higher/lower frequency for next clear channel depending on the
+ * scanDirection. Will do wrap around when reached to mMaxFreq/mMinFreq.
+ * When no wrap around is needed, use the low_bound or high_bound as endFrequency.
+ * Will result in a seek complete event callback.
+ * <p>
+ *
+ * @param startFrequency
+ * Starting frequency of search operation range.
+ * @param endFrequency
+ * Ending frequency of search operation
+ * @param minSignalStrength
+ * Minimum signal strength, default =
+ * {@link #MIN_SIGNAL_STRENGTH_DEFAULT}
+ * @param scanDirection
+ * the direction to search in, it can only be either
+ * {@link #SCAN_MODE_UP} and {@link #SCAN_MODE_DOWN}.
+ * @param scanMethod
+ * see {@link #SCAN_MODE_NORMAL}, {@link #SCAN_MODE_FAST},
+ * @param multi_channel
+ * Is multiple channels are required, or only find next valid channel(seek).
+ * @param rdsType
+ * the type of RDS condition to scan for.
+ * @param rdsTypeValue
+ * the condition value to match.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error
+ * code.
+ *
+ * @see IFmReceiverEventHandler.onSeekCompleteEvent().
+ */
+ public synchronized int seekStationCombo(int startFrequency, int endFrequency,
+ int minSignalStrength, int scanDirection,
+ int scanMethod, boolean multi_channel, int rdsType, int rdsTypeValue) {
+ int returnCode = STATUS_SERVER_FAIL;
+ /* Request this action from the server. */
+ try {
+ returnCode = mService.seekStationCombo (startFrequency, endFrequency, minSignalStrength, scanDirection, scanMethod, multi_channel, rdsType, rdsTypeValue);
+ } catch (RemoteException e) {
+ Log.e(TAG, "seekStation() failed", e);
+ }
+
+ return returnCode;
+ }
+
+ /**
+ * Scans FM toward higher/lower frequency for next clear channel that
+ * supports the requested RDS functionality. Will result in a seek complete
+ * event callback.
+ * <p>
+ * @param scanMode
+ * see {@link #SCAN_MODE_NORMAL}, {@link #SCAN_MODE_DOWN},
+ * {@link #SCAN_MODE_UP} and {@link #SCAN_MODE_FULL}.
+ * @param minSignalStrength
+ * Minimum signal strength, default =
+ * {@link #MIN_SIGNAL_STRENGTH_DEFAULT}
+ * @param rdsCondition
+ * the type of RDS condition to scan for.
+ * @param rdsValue
+ * the condition value to match.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error
+ * code.
+ * @see IFmReceiverEventHandler.onSeekCompleteEvent().
+ */
+ public synchronized int seekRdsStation(int scanMode, int minSignalStrength,
+ int rdsCondition, int rdsValue) {
+ int returnCode = STATUS_SERVER_FAIL;
+ /* Request this action from the server. */
+ try {
+ returnCode = mService.seekRdsStation(scanMode, minSignalStrength,
+ rdsCondition, rdsValue);
+ } catch (RemoteException e) {
+ Log.e(TAG, "seekRdsStation() failed", e);
+ }
+
+ return returnCode;
+ }
+
+ /**
+ * Scans FM toward higher/lower frequency for next clear channel that
+ * supports the requested RDS functionality.. Will result in a seek complete
+ * event callback.
+ * <p>
+ * Scans with default signal strength setting =
+ * {@link #MIN_SIGNAL_STRENGTH_DEFAULT}
+ * @param scanMode
+ * see {@link #SCAN_MODE_NORMAL}, {@link #SCAN_MODE_DOWN},
+ * {@link #SCAN_MODE_UP} and {@link #SCAN_MODE_FULL}.
+ * @param rdsCondition
+ * the type of RDS condition to scan for.
+ * @param rdsValue
+ * the condition value to match.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error
+ * code.
+ * @see IFmReceiverEventHandler.onSeekCompleteEvent().
+ */
+ public int seekRdsStation(int scanMode, int rdsCondition, int rdsValue) {
+ return seekRdsStation(scanMode, MIN_SIGNAL_STRENGTH_DEFAULT, rdsCondition, rdsValue);
+ }
+
+ /**
+ * Aborts the current station seeking operation if any. Will result in a
+ * seek complete event containing the last scanned frequency.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error
+ * code.
+ * @see IFmReceiverEventHandler.onSeekCompleteEvent().
+ */
+ public synchronized int seekStationAbort() {
+ int returnCode = STATUS_SERVER_FAIL;
+ /* Request this action from the server. */
+ try {
+ returnCode = mService.seekStationAbort();
+ } catch (RemoteException e) {
+ Log.e(TAG, "seekStationAbort() failed", e);
+ }
+
+ return returnCode;
+ }
+
+ /**
+ * Enables/disables RDS/RDBS feature and AF algorithm. Will result in a RDS
+ * mode event callback.
+ * <p>
+ * @param rdsMode
+ * Turns on the RDS or RBDS. See {@link #RDS_MODE_OFF},
+ * {@link #RDS_MODE_DEFAULT_ON}, {@link #RDS_MODE_RDS_ON},
+ * {@link #RDS_MODE_RBDS_ON}
+ * @param rdsFeatures
+ * the features to enable in RDS parsing.
+ * @param afMode
+ * enables AF algorithm if True. Disables it if False
+ * @param afThreshold
+ * the RSSI that the AF should jump to an alternate frequency on.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error
+ * code.
+ * @see IFmReceiverEventHandler.onRdsModeEvent().
+ */
+ public synchronized int setRdsMode(int rdsMode, int rdsFeatures,
+ int afMode, int afThreshold) {
+ int returnCode = STATUS_SERVER_FAIL;
+ /* Request this action from the server. */
+ try {
+ returnCode = mService.setRdsMode(rdsMode, rdsFeatures, afMode, afThreshold);
+ } catch (RemoteException e) {
+ Log.e(TAG, "setRdsMode() failed", e);
+ }
+
+ return returnCode;
+ }
+
+ /**
+ * Configures FM audio mode to be mono, stereo or blend. Will result in an
+ * audio mode event callback.
+ * @param audioMode
+ * the audio mode such as stereo or mono. The following values
+ * should be used {@link #AUDIO_MODE_AUTO},
+ * {@link #AUDIO_MODE_STEREO}, {@link #AUDIO_MODE_MONO} or
+ * {@link #AUDIO_MODE_BLEND}.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error
+ * code.
+ * @see IFmReceiverEventHandler.onAudioModeEvent().
+ */
+ public synchronized int setAudioMode(int audioMode) {
+ int returnCode = STATUS_SERVER_FAIL;
+ /* Request this action from the server. */
+ try {
+ returnCode = mService.setAudioMode(audioMode);
+ } catch (RemoteException e) {
+ Log.e(TAG, "setAudioMode() failed", e);
+ }
+
+ return returnCode;
+ }
+
+ /**
+ * Configures FM audio path to AUDIO_PATH_NONE, AUDIO_PATH_ANALOG,
+ * or AUDIO_PATH_DIGITAL. Will result in an audio path event callback.
+
+ * @param audioPath
+ * the audio path such as AUDIO_PATH_NONE, AUDIO_PATH_ANALOG,
+ * or AUDIO_PATH_DIGITAL. The following values should be used
+ * {@link #AUDIO_PATH_NONE}, {@link #AUDIO_PATH_ANALOG}
+ * or {@link #AUDIO_PATH_DIGITAL}.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error
+ * code.
+ * @see IFmReceiverEventHandler.onAudioPathEvent().
+ */
+ public synchronized int setAudioPath(int audioPath) {
+ int returnCode = STATUS_SERVER_FAIL;
+ /* Request this action from the server. */
+ try {
+ returnCode = mService.setAudioPath(audioPath);
+ } catch (RemoteException e) {
+ Log.e(TAG, "setAudioPath() failed", e);
+ }
+
+ return returnCode;
+ }
+
+ /**
+ * Sets the minimum frequency step size to use when scanning for stations.
+ * This function does not result in a status callback and the calling
+ * application should therefore keep track of this setting.
+ * @param stepSize
+ * a frequency interval set to {@link #FREQ_STEP_100KHZ} or
+ * {@link #FREQ_STEP_50KHZ}.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error
+ * code.
+ */
+ public synchronized int setStepSize(int stepSize) {
+ int returnCode = STATUS_SERVER_FAIL;
+ /* Request this action from the server. */
+ try {
+ returnCode = mService.setStepSize(stepSize);
+ } catch (RemoteException e) {
+ Log.e(TAG, "setStepSize() failed", e);
+ }
+
+ return returnCode;
+ }
+
+ /**
+ * Sets the FM volume.
+ * @param volume
+ * range from 0 to 255
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error
+ * code.
+ * @see IFmReceiverEventHandler.onVolumeEvent().
+ */
+ public synchronized int setFMVolume(int volume) {
+ int returnCode = STATUS_SERVER_FAIL;
+ /* Request this action from the server. */
+ try {
+ returnCode = mService.setFMVolume(volume);
+ } catch (RemoteException e) {
+ Log.e(TAG, "setFMVolume() failed", e);
+ }
+
+ return returnCode;
+ }
+
+ /**
+ * Sets a the world frequency region and the deemphasis time. This results
+ * in a world frequency event callback.
+ * @param worldRegion
+ * the world region the FM receiver is located. Set to
+ * {@link #FUNC_REGION_NA}, {@link #FUNC_REGION_EUR},
+ * {@link #FUNC_REGION_JP}, {@link #FUNC_REGION_JP_II}.
+ * @param deemphasisTime
+ * the deemphasis time that can be set to either
+ * {@link #DEEMPHASIS_50U} or {@link #DEEMPHASIS_75U}.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error
+ * code.
+ * @see IFmReceiverEventHandler.onWorldRegionEvent().
+ */
+ public synchronized int setWorldRegion(int worldRegion, int deemphasisTime) {
+ int returnCode = STATUS_SERVER_FAIL;
+ /* Request this action from the server. */
+ try {
+ returnCode = mService.setWorldRegion(worldRegion, deemphasisTime);
+ } catch (RemoteException e) {
+ Log.e(TAG, "setWorldRegion() failed", e);
+ }
+
+ return returnCode;
+ }
+
+ /**
+ * Estimates the noise floor level given a specific type request. This
+ * function returns an RSSI level that is useful for specifying as the
+ * minimum signal strength for scan operations.
+ * @param nflLevel
+ * estimate noise floor for {@link #NFL_LOW}, {@link #NFL_MED} or
+ * {@link #NFL_FINE}.
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error
+ * code.
+ * @see IFmReceiverEventHandler.onEstimateNflEvent().
+ */
+ public synchronized int estimateNoiseFloorLevel(int nflLevel) {
+ int returnCode = STATUS_SERVER_FAIL;
+ /* Request this action from the server. */
+ try {
+ returnCode = mService.estimateNoiseFloorLevel(nflLevel);
+ } catch (RemoteException e) {
+ Log.e(TAG, "estimateNoiseFloorLevel() failed", e);
+ }
+
+ return returnCode;
+ }
+
+ /**
+ * Enables or disables the live polling of audio quality on the currently
+ * tuned frequency using a specific poll interval.
+ * NOTE : SNR value will be returned a 0 for chips not supporting this SNR feature.
+ * @param liveAudioPolling
+ * enables/disables live polling of audio quality.
+ * @param signalPollInterval
+ * the sample interval for live polling of audio quality.
+ *
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error
+ * code.
+ * @see IFmReceiverEventHandler.onLiveAudioQualityEvent().
+ */
+ public synchronized int setLiveAudioPolling(boolean liveAudioPolling, int signalPollInterval) {
+ int returnCode = STATUS_SERVER_FAIL;
+ /* Request this action from the server. */
+ try {
+ returnCode = mService.setLiveAudioPolling(liveAudioPolling, signalPollInterval);
+ } catch (RemoteException e) {
+ Log.e(TAG, "setLiveAudioPolling() failed", e);
+ }
+
+ return returnCode;
+ }
+
+ /**
+ * Sets the SNR threshold for the subsequent FM frequency tuning.
+ * This value will be used by BTA stack internally.
+ *
+ * @param signalPollInterval
+ * SNR Threshold value (0 ~ 31 (BTA_FM_SNR_MAX) )
+ *
+ * @return STATUS_OK = 0 if successful. Otherwise returns a non-zero error code.
+ */
+ public synchronized int setSnrThreshold(int snrThreshold) {
+ int returnCode = STATUS_SERVER_FAIL;
+
+ /* Request this action from the server. */
+ try {
+ returnCode = mService.setSnrThreshold(snrThreshold);
+ } catch (RemoteException e) {
+ Log.e(TAG, "setSnrThreshold() failed", e);
+ }
+ return returnCode;
+ }
+
+ protected void finalize() {
+ finish();
+ }
+
+ /**
+ * The class containing all the FmProxy callback function handlers. These
+ * functions will be called by the FmService module when callback
+ * events occur. They in turn relay the callback information back to the
+ * main applications callback handler.
+ */
+ private class FmReceiverCallback extends IFmReceiverCallback.Stub {
+
+ public synchronized void onStatusEvent(int freq, int rssi, int snr,
+ boolean radioIsOn, int rdsProgramType,
+ String rdsProgramService, String rdsRadioText,
+ String rdsProgramTypeName, boolean isMute)
+ throws RemoteException {
+ /* Process and hand this event information to the application. */
+ if (null != mEventHandler) {
+ mEventHandler.onStatusEvent(freq, rssi, snr, radioIsOn,
+ rdsProgramType, rdsProgramService, rdsRadioText,
+ rdsProgramTypeName, isMute);
+ }
+ }
+
+ public synchronized void onSeekCompleteEvent(int freq, int rssi, int snr,
+ boolean seeksuccess) throws RemoteException {
+ /* Process and hand this event information to the application. */
+ if (null != mEventHandler)
+ mEventHandler.onSeekCompleteEvent(freq, rssi, snr, seeksuccess);
+ }
+
+ public synchronized void onRdsModeEvent(int rdsMode,
+ int alternateFreqHopEnabled) throws RemoteException {
+ /* Process and hand this event information to the application. */
+ if (null != mEventHandler)
+ mEventHandler.onRdsModeEvent(rdsMode, alternateFreqHopEnabled);
+ }
+
+
+ public synchronized void onRdsDataEvent(int rdsDataType, int rdsIndex,
+ String rdsText) throws RemoteException {
+ /* Process and hand this event information to the application. */
+ if (null != mEventHandler)
+ mEventHandler.onRdsDataEvent(rdsDataType, rdsIndex, rdsText);
+ }
+
+
+ public synchronized void onAudioModeEvent(int audioMode) throws RemoteException {
+ /* Process and hand this event information to the application. */
+ if (null != mEventHandler)
+ mEventHandler.onAudioModeEvent(audioMode);
+ }
+
+
+ public synchronized void onAudioPathEvent(int audioPath) throws RemoteException {
+ /* Process and hand this event information to the application. */
+ if (null != mEventHandler)
+ mEventHandler.onAudioPathEvent(audioPath);
+ }
+
+
+ public synchronized void onEstimateNflEvent(int nfl) throws RemoteException {
+ /* Process and hand this event information to the application. */
+ if (null != mEventHandler)
+ mEventHandler.onEstimateNoiseFloorLevelEvent(nfl);
+ }
+
+
+ public synchronized void onLiveAudioQualityEvent(int rssi, int snr) throws RemoteException {
+ /* Process and hand this event information to the application. */
+ if (null != mEventHandler)
+ mEventHandler.onLiveAudioQualityEvent(rssi, snr);
+ }
+
+
+ public synchronized void onWorldRegionEvent(int worldRegion) throws RemoteException {
+ /* Process and hand this event information to the application. */
+ if (null != mEventHandler)
+ mEventHandler.onWorldRegionEvent(worldRegion);
+ }
+
+ public synchronized void onVolumeEvent(int status, int volume) throws RemoteException {
+ /* Process and hand this event information to the application. */
+ if (null != mEventHandler)
+ mEventHandler.onVolumeEvent(status, volume);
+ }
+ };
+
+ private static final boolean D = true;
+
+ protected Context mContext;
+ protected IFmProxyCallback mProxyAvailCb;
+
+ private ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ if (D) {
+ Log.d(TAG, "Fm proxy onServiceConnected() name = " + className + ", service = " + service);
+ }
+ if (service == null || !init(service) && mProxyAvailCb!=null) {
+ Log.e(TAG, "Unable to create proxy");
+ }
+ if ( mProxyAvailCb != null ) {
+ mProxyAvailCb.onProxyAvailable(FmProxy.this);
+ mProxyAvailCb = null;
+ }
+ }
+
+ public void onServiceDisconnected(ComponentName className) {
+ if (D)
+ Log.d(TAG, "Fm Proxy object disconnected");
+ mService = null;
+ }
+ };
+
+}
diff --git a/fm/lib/src/com/broadcom/fm/fmreceiver/IFmProxyCallback.java b/fm/lib/src/com/broadcom/fm/fmreceiver/IFmProxyCallback.java
new file mode 100644
index 0000000..bcb1738
--- /dev/null
+++ b/fm/lib/src/com/broadcom/fm/fmreceiver/IFmProxyCallback.java
@@ -0,0 +1,67 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2009-2012 Broadcom Corporation
+ *
+ * This program is the proprietary software of Broadcom Corporation and/or its
+ * licensors, and may only be used, duplicated, modified or distributed
+ * pursuant to the terms and conditions of a separate, written license
+ * agreement executed between you and Broadcom (an "Authorized License").
+ * Except as set forth in an Authorized License, Broadcom grants no license
+ * (express or implied), right to use, or waiver of any kind with respect to
+ * the Software, and Broadcom expressly reserves all rights in and to the
+ * Software and all intellectual property rights therein.
+ * IF YOU HAVE NO AUTHORIZED LICENSE, THEN YOU HAVE NO RIGHT TO USE THIS
+ * SOFTWARE IN ANY WAY, AND SHOULD IMMEDIATELY NOTIFY BROADCOM AND DISCONTINUE
+ * ALL USE OF THE SOFTWARE.
+ *
+ * Except as expressly set forth in the Authorized License,
+ *
+ * 1. This program, including its structure, sequence and organization,
+ * constitutes the valuable trade secrets of Broadcom, and you shall
+ * use all reasonable efforts to protect the confidentiality thereof,
+ * and to use this information only in connection with your use of
+ * Broadcom integrated circuit products.
+ *
+ * 2. TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED
+ * "AS IS" AND WITH ALL FAULTS AND BROADCOM MAKES NO PROMISES,
+ * REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY,
+ * OR OTHERWISE, WITH RESPECT TO THE SOFTWARE. BROADCOM SPECIFICALLY
+ * DISCLAIMS ANY AND ALL IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY,
+ * NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES,
+ * ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
+ * CORRESPONDENCE TO DESCRIPTION. YOU ASSUME THE ENTIRE RISK ARISING
+ * OUT OF USE OR PERFORMANCE OF THE SOFTWARE.
+ *
+ * 3. TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL BROADCOM
+ * OR ITS LICENSORS BE LIABLE FOR
+ * (i) CONSEQUENTIAL, INCIDENTAL, SPECIAL, INDIRECT, OR EXEMPLARY
+ * DAMAGES WHATSOEVER ARISING OUT OF OR IN ANY WAY RELATING TO
+ * YOUR USE OF OR INABILITY TO USE THE SOFTWARE EVEN IF BROADCOM
+ * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES; OR
+ * (ii) ANY AMOUNT IN EXCESS OF THE AMOUNT ACTUALLY PAID FOR THE
+ * SOFTWARE ITSELF OR U.S. $1, WHICHEVER IS GREATER. THESE
+ * LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF
+ * ESSENTIAL PURPOSE OF ANY LIMITED REMEDY.
+ *
+ *****************************************************************************/
+
+ package com.broadcom.fm.fmreceiver;
+
+
+/**
+ * The callback interface that used to return the Bluetooth service proxy
+ * object.
+ * @hide
+ */
+public interface IFmProxyCallback {
+
+ /**
+ * Callback method invoked by the framework to return a Bluetooth service
+ * proxy.
+ * @param proxyObject
+ * Bluetooth service proxy object. Applications must cast the
+ * proxyObject into the corresponding type proxy type for the
+ * specific Bluetooth service.
+ */
+ public void onProxyAvailable(Object proxyObject);
+}
diff --git a/fm/lib/src/com/broadcom/fm/fmreceiver/IFmReceiverCallback.aidl b/fm/lib/src/com/broadcom/fm/fmreceiver/IFmReceiverCallback.aidl
new file mode 100644
index 0000000..dd1552a
--- /dev/null
+++ b/fm/lib/src/com/broadcom/fm/fmreceiver/IFmReceiverCallback.aidl
@@ -0,0 +1,129 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2009-2012 Broadcom Corporation
+ *
+ * This program is the proprietary software of Broadcom Corporation and/or its
+ * licensors, and may only be used, duplicated, modified or distributed
+ * pursuant to the terms and conditions of a separate, written license
+ * agreement executed between you and Broadcom (an "Authorized License").
+ * Except as set forth in an Authorized License, Broadcom grants no license
+ * (express or implied), right to use, or waiver of any kind with respect to
+ * the Software, and Broadcom expressly reserves all rights in and to the
+ * Software and all intellectual property rights therein.
+ * IF YOU HAVE NO AUTHORIZED LICENSE, THEN YOU HAVE NO RIGHT TO USE THIS
+ * SOFTWARE IN ANY WAY, AND SHOULD IMMEDIATELY NOTIFY BROADCOM AND DISCONTINUE
+ * ALL USE OF THE SOFTWARE.
+ *
+ * Except as expressly set forth in the Authorized License,
+ *
+ * 1. This program, including its structure, sequence and organization,
+ * constitutes the valuable trade secrets of Broadcom, and you shall
+ * use all reasonable efforts to protect the confidentiality thereof,
+ * and to use this information only in connection with your use of
+ * Broadcom integrated circuit products.
+ *
+ * 2. TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED
+ * "AS IS" AND WITH ALL FAULTS AND BROADCOM MAKES NO PROMISES,
+ * REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY,
+ * OR OTHERWISE, WITH RESPECT TO THE SOFTWARE. BROADCOM SPECIFICALLY
+ * DISCLAIMS ANY AND ALL IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY,
+ * NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES,
+ * ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
+ * CORRESPONDENCE TO DESCRIPTION. YOU ASSUME THE ENTIRE RISK ARISING
+ * OUT OF USE OR PERFORMANCE OF THE SOFTWARE.
+ *
+ * 3. TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL BROADCOM
+ * OR ITS LICENSORS BE LIABLE FOR
+ * (i) CONSEQUENTIAL, INCIDENTAL, SPECIAL, INDIRECT, OR EXEMPLARY
+ * DAMAGES WHATSOEVER ARISING OUT OF OR IN ANY WAY RELATING TO
+ * YOUR USE OF OR INABILITY TO USE THE SOFTWARE EVEN IF BROADCOM
+ * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES; OR
+ * (ii) ANY AMOUNT IN EXCESS OF THE AMOUNT ACTUALLY PAID FOR THE
+ * SOFTWARE ITSELF OR U.S. $1, WHICHEVER IS GREATER. THESE
+ * LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF
+ * ESSENTIAL PURPOSE OF ANY LIMITED REMEDY.
+ *
+ *****************************************************************************/
+
+package com.broadcom.fm.fmreceiver;
+
+/**
+ * Callback interface for applications to receive events from the FM radio service.
+ * @hide
+ */
+interface IFmReceiverCallback{
+ /* The FM Receiver callback function list. */
+ /**
+ * Callback for a radio status event.
+ * @param freq the radio frequency
+ * @param rssi the received signal strength indicator
+ * @param snr the received signal-to-noise ratio(SNR) value
+ * @param radioIsOn true if the radio is turned on
+ * @param rdsProgramType the RDS program type
+ * @param rdsProgramService the RDS program service
+ * @param rdsProgramText the RDS radio text
+ * @param rdsProgramTypeName the RDS program type name
+ * @param isMute if true, indicates the radio is muted
+ */
+ void onStatusEvent(int freq, int rssi, int snr, boolean radioIsOn,
+ int rdsProgramType, in String rdsProgramService, in String rdsRadioText,
+ in String rdsProgramTypeName, boolean isMute);
+
+ /**
+ * Callback for a radio seek complete event.
+ * @param freq the radio frequency
+ * @param rssi the received signal strength indicator
+ * @param snr the received signal-to-noise ratio(SNR) value
+ * @param seeksuccess if true, indicates the radio seek succeeded
+ */
+ void onSeekCompleteEvent(int freq, int rssi, int snr, boolean seeksuccess);
+
+ /**
+ * Callback for a RDS mode event
+ * @param rdsMode the RDS Mode
+ * @param alternateFreqHopEnabled if true, alternative frequency hop is enabled
+ */
+ void onRdsModeEvent(int rdsMode, int alternateFreqHopEnabled);
+
+ /**
+ * Callback for a RDS data event
+ * @param rdsMode the RDS Mode
+ * @param alternateFreqHopEnabled if true, alternative frequency hop is enabled
+ */
+ void onRdsDataEvent(int rdsDataType, int rdsIndex, in String rdsText);
+
+ /**
+ * Callback for an audio mode event
+ * @param audioMode the audio mode
+ */
+ void onAudioModeEvent(int audioMode);
+
+ /* Handle audio path event. */
+ void onAudioPathEvent(int audioPath);
+
+ /**
+ * Callback for world region mode event
+ * @param worldRegion the world region
+ */
+ void onWorldRegionEvent(int worldRegion);
+
+ /**
+ * Callback for estimate noise floor event
+ * @param nfl the noise floor
+ */
+ void onEstimateNflEvent(int nfl);
+
+ /**
+ * Callback for live audio quality event
+ * @param rssi the received signal strength indicator
+ * @param snr the received signal-to-noise ratio(SNR) value
+ */
+ void onLiveAudioQualityEvent(int rssi, int snr);
+
+ /**
+ * Callback for FM volume event
+ * @param status equal to 0 if successful. Otherwise returns a non-zero error code.
+ * @param volume range from 0 to 0x100
+ */
+ void onVolumeEvent(int status,int volume);
+}
diff --git a/fm/lib/src/com/broadcom/fm/fmreceiver/IFmReceiverEventHandler.java b/fm/lib/src/com/broadcom/fm/fmreceiver/IFmReceiverEventHandler.java
new file mode 100644
index 0000000..3d8a5bd
--- /dev/null
+++ b/fm/lib/src/com/broadcom/fm/fmreceiver/IFmReceiverEventHandler.java
@@ -0,0 +1,167 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2009-2012 Broadcom Corporation
+ *
+ * This program is the proprietary software of Broadcom Corporation and/or its
+ * licensors, and may only be used, duplicated, modified or distributed
+ * pursuant to the terms and conditions of a separate, written license
+ * agreement executed between you and Broadcom (an "Authorized License").
+ * Except as set forth in an Authorized License, Broadcom grants no license
+ * (express or implied), right to use, or waiver of any kind with respect to
+ * the Software, and Broadcom expressly reserves all rights in and to the
+ * Software and all intellectual property rights therein.
+ * IF YOU HAVE NO AUTHORIZED LICENSE, THEN YOU HAVE NO RIGHT TO USE THIS
+ * SOFTWARE IN ANY WAY, AND SHOULD IMMEDIATELY NOTIFY BROADCOM AND DISCONTINUE
+ * ALL USE OF THE SOFTWARE.
+ *
+ * Except as expressly set forth in the Authorized License,
+ *
+ * 1. This program, including its structure, sequence and organization,
+ * constitutes the valuable trade secrets of Broadcom, and you shall
+ * use all reasonable efforts to protect the confidentiality thereof,
+ * and to use this information only in connection with your use of
+ * Broadcom integrated circuit products.
+ *
+ * 2. TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED
+ * "AS IS" AND WITH ALL FAULTS AND BROADCOM MAKES NO PROMISES,
+ * REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY,
+ * OR OTHERWISE, WITH RESPECT TO THE SOFTWARE. BROADCOM SPECIFICALLY
+ * DISCLAIMS ANY AND ALL IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY,
+ * NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES,
+ * ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
+ * CORRESPONDENCE TO DESCRIPTION. YOU ASSUME THE ENTIRE RISK ARISING
+ * OUT OF USE OR PERFORMANCE OF THE SOFTWARE.
+ *
+ * 3. TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL BROADCOM
+ * OR ITS LICENSORS BE LIABLE FOR
+ * (i) CONSEQUENTIAL, INCIDENTAL, SPECIAL, INDIRECT, OR EXEMPLARY
+ * DAMAGES WHATSOEVER ARISING OUT OF OR IN ANY WAY RELATING TO
+ * YOUR USE OF OR INABILITY TO USE THE SOFTWARE EVEN IF BROADCOM
+ * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES; OR
+ * (ii) ANY AMOUNT IN EXCESS OF THE AMOUNT ACTUALLY PAID FOR THE
+ * SOFTWARE ITSELF OR U.S. $1, WHICHEVER IS GREATER. THESE
+ * LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF
+ * ESSENTIAL PURPOSE OF ANY LIMITED REMEDY.
+ *
+ *****************************************************************************/
+
+
+package com.broadcom.fm.fmreceiver;
+
+/**
+ * Applications wishing use the FmProxy must implement the
+ * functions described by the
+ * IFmReceiverEventHandler interface to receive asynchronous
+ * information and status updates from the FM Receiver.
+ * {@hide}
+ */
+public interface IFmReceiverEventHandler {
+
+ /**
+ * Is called when the FM Receiver has finished processing a successful
+ * turnOnradio(), turnOffRadio(), tuneRadio(), getStatus() request.
+ * This is a useful function to call to update on the basic information
+ * required to update a GUI.
+ * @param freq indicates the currently tuned frequency.
+ * @param rssi indicates the RSSI of the currently tuned frequency.
+ * @param snr indicates the SNR value of the currently tuned frequency.
+ * @param radioIsOn is true if the radio is on and false if not.
+ * @param rdsProgramType indicates the type of program currently playing
+ * if that information is available on the selected frequency and
+ * PTY is enabled.
+ * @param rdsProgramService indicates the name of the currently selected
+ * station if that information is available.
+ * @param rdsRadioText indicates the radio text from the currently selected
+ * station if available.
+ * @param rdsProgramTypeName indicates the string version of the program
+ * currently playing if that information is available on the
+ * selected frequency.
+ * @param isMute is true if the audio is muted at hardware level.
+ */
+ public void onStatusEvent(int freq, int rssi, int snr, boolean radioIsOn,
+ int rdsProgramType, String rdsProgramService, String rdsRadioText,
+ String rdsProgramTypeName, boolean isMute);
+
+ /**
+ * Is called when the FM Receiver has finished processing a successful
+ * seekStation(), seekSpecificStation() or seekStationAbort() request.
+ *
+ * @param freq indicates the frequency of the located station if found.
+ * @param rssi indicates the RSSI of the located station if found.
+ * @param snr indicates the SNR value of the located station if found.
+ * @param seeksuccess is true if a valid station was located.
+ */
+ public void onSeekCompleteEvent(int freq, int rssi, int snr, boolean seeksuccess);
+
+ /**
+ * Is called when the FM Receiver has finished processing a successful
+ * setRdsMode() request.
+ *
+ * @param rdsMode indicates the current RDS or RDBS mode.
+ * @param alternateFreqHopEnabled indicates whether alternate frequency
+ * hopping is enabled.
+ */
+ public void onRdsModeEvent(int rdsMode, int alternateFreqHopEnabled);
+
+ /**
+ * Is called when the FM Receiver has compiled an RDS message for the
+ * application. Only those RDS message types requested when setting
+ * the RDS mode will be relayed.
+ *
+ * @param rdsDataType indicates the type of RDS data.
+ * @param rdsIndex indicates the integer value of the data if available.
+ * @param rdsText indicates the string value of the data if available.
+ */
+ public void onRdsDataEvent(int rdsDataType, int rdsIndex, String rdsText);
+
+ /**
+ * Is called when the FM Receiver has finished processing a successful
+ * setAudioMode() request.
+ *
+ * @param audioMode indicates the current audio mode.
+ */
+ public void onAudioModeEvent(int audioMode);
+
+ /**
+ * Is called when the FM Receiver has finished processing a successful
+ * setAudioPath() request.
+ *
+ * @param audioPath indicates the current audio path.
+ */
+ public void onAudioPathEvent(int audioPath);
+
+ /**
+ * Is called when the FM Receiver has finished processing a successful
+ * setFrequencyParameters() request.
+ *
+ * @param worldRegion indicates the world frequency band region in use.
+ */
+ public void onWorldRegionEvent(int worldRegion);
+
+ /**
+ * Is called when the FM Receiver has finished processing a successful
+ * estimateNoiseFloorLevel() request. The returned nfl parameter can be
+ * used as a requested minimum signal strength value when seeking stations.
+ *
+ * @param nfl indicates the current frequencies Noise Floor Level.
+ */
+ public void onEstimateNoiseFloorLevelEvent(int nfl);
+
+ /**
+ * Is called repeatedly when the FM Receiver is actively sampling the signal
+ * quality. Provides a live stream of signal quality data to the application.
+ *
+ * @param rssi the signal quality at the time of the latest audio sampling.
+ * @param snr the SNR(Signal Noise Reading) reading at the time of the
+ */
+ public void onLiveAudioQualityEvent(int rssi, int snr);
+
+ /**
+ * Is called when the FM Receiver has finished processing a successful
+ * setFMVolume() request.
+ * @param status equal to 0 if successful. Otherwise returns a non-zero error code.
+ * @param volume range from 0 to 0x100
+ */
+ public void onVolumeEvent(int status,int volume);
+
+}
diff --git a/fm/lib/src/com/broadcom/fm/fmreceiver/IFmReceiverService.aidl b/fm/lib/src/com/broadcom/fm/fmreceiver/IFmReceiverService.aidl
new file mode 100644
index 0000000..092ae8e
--- /dev/null
+++ b/fm/lib/src/com/broadcom/fm/fmreceiver/IFmReceiverService.aidl
@@ -0,0 +1,88 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2009-2012 Broadcom Corporation
+ *
+ * This program is the proprietary software of Broadcom Corporation and/or its
+ * licensors, and may only be used, duplicated, modified or distributed
+ * pursuant to the terms and conditions of a separate, written license
+ * agreement executed between you and Broadcom (an "Authorized License").
+ * Except as set forth in an Authorized License, Broadcom grants no license
+ * (express or implied), right to use, or waiver of any kind with respect to
+ * the Software, and Broadcom expressly reserves all rights in and to the
+ * Software and all intellectual property rights therein.
+ * IF YOU HAVE NO AUTHORIZED LICENSE, THEN YOU HAVE NO RIGHT TO USE THIS
+ * SOFTWARE IN ANY WAY, AND SHOULD IMMEDIATELY NOTIFY BROADCOM AND DISCONTINUE
+ * ALL USE OF THE SOFTWARE.
+ *
+ * Except as expressly set forth in the Authorized License,
+ *
+ * 1. This program, including its structure, sequence and organization,
+ * constitutes the valuable trade secrets of Broadcom, and you shall
+ * use all reasonable efforts to protect the confidentiality thereof,
+ * and to use this information only in connection with your use of
+ * Broadcom integrated circuit products.
+ *
+ * 2. TO THE MAXIMUM EXTENT PERMITTED BY LAW, THE SOFTWARE IS PROVIDED
+ * "AS IS" AND WITH ALL FAULTS AND BROADCOM MAKES NO PROMISES,
+ * REPRESENTATIONS OR WARRANTIES, EITHER EXPRESS, IMPLIED, STATUTORY,
+ * OR OTHERWISE, WITH RESPECT TO THE SOFTWARE. BROADCOM SPECIFICALLY
+ * DISCLAIMS ANY AND ALL IMPLIED WARRANTIES OF TITLE, MERCHANTABILITY,
+ * NONINFRINGEMENT, FITNESS FOR A PARTICULAR PURPOSE, LACK OF VIRUSES,
+ * ACCURACY OR COMPLETENESS, QUIET ENJOYMENT, QUIET POSSESSION OR
+ * CORRESPONDENCE TO DESCRIPTION. YOU ASSUME THE ENTIRE RISK ARISING
+ * OUT OF USE OR PERFORMANCE OF THE SOFTWARE.
+ *
+ * 3. TO THE MAXIMUM EXTENT PERMITTED BY LAW, IN NO EVENT SHALL BROADCOM
+ * OR ITS LICENSORS BE LIABLE FOR
+ * (i) CONSEQUENTIAL, INCIDENTAL, SPECIAL, INDIRECT, OR EXEMPLARY
+ * DAMAGES WHATSOEVER ARISING OUT OF OR IN ANY WAY RELATING TO
+ * YOUR USE OF OR INABILITY TO USE THE SOFTWARE EVEN IF BROADCOM
+ * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES; OR
+ * (ii) ANY AMOUNT IN EXCESS OF THE AMOUNT ACTUALLY PAID FOR THE
+ * SOFTWARE ITSELF OR U.S. $1, WHICHEVER IS GREATER. THESE
+ * LIMITATIONS SHALL APPLY NOTWITHSTANDING ANY FAILURE OF
+ * ESSENTIAL PURPOSE OF ANY LIMITED REMEDY.
+ *
+ *****************************************************************************/
+package com.broadcom.fm.fmreceiver;
+
+import com.broadcom.fm.fmreceiver.IFmReceiverCallback;
+
+/**
+ * @hide
+ */
+interface IFmReceiverService{
+
+ /* The FM Receiver Service API. */
+ void init();
+ void registerCallback(IFmReceiverCallback cb);
+ void unregisterCallback(IFmReceiverCallback cb);
+
+ boolean getRadioIsOn();
+ int getMonoStereoMode();
+ int getTunedFrequency();
+ boolean getIsMute();
+
+ int turnOffRadio();
+ int turnOnRadio(int functionalityMask, inout char[] clientPackagename);
+
+ int tuneRadio(int freq);
+ int getStatus();
+ int muteAudio(boolean mute);
+ int seekStation(int scanDirection, int minSignalStrength);
+ int seekStationCombo(int startFreq, int endFreq, int minSignalStrength, int dir,
+ int scanMethod, boolean multiChannel, int rdsType, int rdsTypeValue);
+ int seekRdsStation(int scanDirection, int minSignalStrength,
+ int rdsCondition, int rdsValue);
+ int seekStationAbort();
+ int setRdsMode(int rdsMode, int rdsFeatures, int afMode, int afThreshold);
+ int setAudioMode(int audioMode);
+ int setAudioPath(int audioPath);
+ int setStepSize(int stepSize);
+ int setWorldRegion(int worldRegion, int deemphasisTime);
+ int estimateNoiseFloorLevel(int nflLevel);
+ int setLiveAudioPolling(boolean liveAudioPolling, int signalPollInterval);
+ int setFMVolume(int volume);
+ int setSnrThreshold(int snrThreshold);
+ int cleanupFmService();
+}
diff --git a/jni/Android.mk b/jni/Android.mk
index 68ca6e7..ec0c81c 100644
--- a/jni/Android.mk
+++ b/jni/Android.mk
@@ -19,6 +19,11 @@ LOCAL_SRC_FILES:= \
com_android_bluetooth_gatt.cpp \
com_android_bluetooth_sdp.cpp
+ifeq ($(strip $(BOARD_HAVE_FMRADIO_BCM)),true)
+LOCAL_SRC_FILES += ../fm/app/jni/com_broadcom_fm_service.cpp
+LOCAL_CFLAGS += -DBOARD_HAVE_FMRADIO_BCM
+endif
+
LOCAL_C_INCLUDES += \
$(JNI_H_INCLUDE) \
diff --git a/jni/com_android_bluetooth.h b/jni/com_android_bluetooth.h
index b8beb23..33588c9 100644
--- a/jni/com_android_bluetooth.h
+++ b/jni/com_android_bluetooth.h
@@ -56,6 +56,10 @@ int register_com_android_bluetooth_gatt (JNIEnv* env);
int register_com_android_bluetooth_sdp (JNIEnv* env);
+#ifdef BOARD_HAVE_FMRADIO_BCM
+int register_com_broadcom_fm_service(JNIEnv* env);
+#endif
+
}
#endif /* COM_ANDROID_BLUETOOTH_H */
diff --git a/jni/com_android_bluetooth_btservice_AdapterService.cpp b/jni/com_android_bluetooth_btservice_AdapterService.cpp
index a6ffd19..9cb9dba 100644
--- a/jni/com_android_bluetooth_btservice_AdapterService.cpp
+++ b/jni/com_android_bluetooth_btservice_AdapterService.cpp
@@ -1405,5 +1405,12 @@ jint JNI_OnLoad(JavaVM *jvm, void *reserved)
return JNI_ERR;
}
+#ifdef BOARD_HAVE_FMRADIO_BCM
+ if ((status = android::register_com_broadcom_fm_service(e)) < 0) {
+ ALOGE("jni fm registration failure: %d", status);
+ return JNI_ERR;
+ }
+#endif
+
return JNI_VERSION_1_6;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment