Created
October 4, 2016 16:27
-
-
Save ngoquang2708/ed5e4b71dae1db2b381a6675e1e46539 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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