/* * Copyright (c) 2016, The Linux Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above * copyright notice, this list of conditions and the following * disclaimer in the documentation and/or other materials provided * with the distribution. * * Neither the name of The Linux Foundation nor the names of its * contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* * FMhal service used to access RFKILL **/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "private/android_filesystem_config.h" //#include "bt_hci_bdroid.h" #include "bt_vendor_lib.h" #include "fm_hci.h" #include "wcnss_fmhci.h" #include #define LOG_TAG "FMHalService" #define FM_HAL_SOCK "fmhal_sock" #define BT_SSR_TRIGGERED 0xee #define FM_POWER_OFF 0x01 #define FM_POWER_ON 0x02 #define FM_USERIAL_OPEN 0x03 #define FM_USERIAL_CLOSE 0x04 #define FM_CMD_PACKET_TYPE 0x11 #define FM_EVT_PACKET_TYPE 0x14 #ifndef BLUETOOTH_UID #define BLUETOOTH_UID 1002 #endif #ifndef SYSTEM_UID #define SYSTEM_UID 1000 #endif #ifndef ROOT_UID #define ROOT_UID 0 #endif pthread_mutex_t signal_mutex; bt_vendor_interface_t *fm_if = NULL; int remote_fm_hal_fd; int do_write(int fd, unsigned char *buf,int len); unsigned char reset_cmpl[] = {0x04, 0x0e, 0x04, 0x01,0x03, 0x0c, 0x00}; static int extract_uid(int uuid) { int userid; int appid; appid = userid = uuid % AID_USER; if (userid > BLUETOOTH_UID) { appid = userid % AID_APP; } ALOGD("%s appid = %d",__func__,appid); return appid; } void service_cleanup() { char ref_count[PROPERTY_VALUE_MAX]; char cleanup[PROPERTY_VALUE_MAX]; int ref_val,clean; ALOGE("Service is stopped "); property_get("wc_transport.clean_up", cleanup, "0"); property_set("wc_transport.fm_service_status", "0"); property_set("wc_transport.start_fmhci", "0"); property_set("wc_transport.fm_power_status", "0"); clean = atoi(cleanup); ALOGE("clean Value = %d",clean); } static int establish_fm_remote_socket(char *name) { int fd = -1; struct sockaddr_un client_address; socklen_t clen; int sock_id, ret; struct ucred creds; int c_uid; ALOGI("%s(%s) Entry ", __func__, name); sock_id = socket(AF_LOCAL, SOCK_STREAM, 0); if (sock_id < 0) { ALOGE("%s: server Socket creation failure", __func__); return fd; } ALOGI("convert name to android abstract name:%s %d", name, sock_id); if (socket_local_server_bind(sock_id, name, ANDROID_SOCKET_NAMESPACE_ABSTRACT) >= 0) { if (listen(sock_id, 5) == 0) { ALOGI("listen to local socket:%s, fd:%d", name, sock_id); } else { ALOGE("listen to local socket:failed"); close(sock_id); return fd; } } else { close(sock_id); ALOGE("%s: server bind failed for socket : %s", __func__, name); return fd; } clen = sizeof(client_address); /*Indicate that, server is ready to accept*/ property_set("wc_transport.fm_service_status", "1"); ALOGI("%s: wc_transport.fm_service_status set to 1 ", __func__); ALOGI("%s: before accept_server_socket", name); fd = accept(sock_id, (struct sockaddr *)&client_address, &clen); if (fd > 0) { ALOGI("%s accepted fd:%d for server fd:%d", name, fd, sock_id); close(sock_id); memset(&creds, 0, sizeof(creds)); socklen_t szCreds = sizeof(creds); ret = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &creds, &szCreds); if (ret < 0) { ALOGE("%s: error getting remote socket creds: %d\n", __func__, ret); close(fd); return -1; } c_uid = creds.uid; if (c_uid > BLUETOOTH_UID) c_uid = extract_uid(creds.uid); if (c_uid != BLUETOOTH_UID && c_uid != SYSTEM_UID && c_uid != ROOT_UID) { ALOGE("%s: client doesn't have required credentials", __func__); ALOGE("<%s req> client uid: %d", name, creds.uid); close(fd); return -1; } ALOGI("%s: Remote socket credentials: %d\n", __func__, creds.uid); return fd; } else { ALOGE("BTC accept failed fd:%d sock d:%d error %s", fd, sock_id, strerror(errno)); close(sock_id); return fd; } close(sock_id); return fd; } int handle_fmcommand_writes(int fd) { ALOGI("%s: ", __func__); unsigned char first_byte; int retval,val; int fd_array[CH_MAX]; ALOGE("%s: FMHAL: read 1st byte to determine the power on/off ", __func__); retval = read (fd, &first_byte, 1); if (retval < 0) { ALOGE("%s:read returns err: %d\n", __func__,retval); return -1; } if (retval == 0) { ALOGE("%s: This indicates the close of other end", __func__); return -99; } ALOGE("%s: FM command type: 0x%x", __func__, first_byte); switch(first_byte) { case FM_POWER_OFF: ALOGE("%s: Received power off command from FM stack: %d", __func__, first_byte); val = 0; retval = fm_if->op(BT_VND_OP_POWER_CTRL, &val); if (retval < 0) { ALOGE("Failed to turn off power from bt vendor interface"); // return -1; } else { property_set("wc_transport.fm_power_status", "0"); retval = -99; } break; case FM_POWER_ON: ALOGE("%s: Received power ON command from FM stack: %d", __func__, first_byte); val = 1; retval =fm_if->op(FM_VND_OP_POWER_CTRL, &val); if (retval < 0) { ALOGE("Failed to turn on power from bt vendor interface"); } else property_set("wc_transport.fm_power_status", "1"); break; default: ALOGE("%s: Unexpected data format!!",__func__); retval = -1; } return retval; } int do_read(int fd, unsigned char* buf, size_t len) { int bytes_left, bytes_read = 0, read_offset; bytes_left = len; read_offset = 0; do { bytes_read = read(fd, buf+read_offset, bytes_left); if (bytes_read < 0) { ALOGE("%s: Read error: %d (%s)", __func__, bytes_left, strerror(errno)); return -1; } else if (bytes_read == 0) { ALOGE("%s: read returned 0, err = %s, read bytes: %d, expected: %d", __func__, strerror(errno), (len-bytes_left), len); return (len-bytes_left); } else { if (bytes_read < bytes_left) { ALOGV("Still there are %d bytes to read", bytes_left-bytes_read); bytes_left = bytes_left-bytes_read; read_offset = read_offset+bytes_read; } else { ALOGV("%s: done with read",__func__); break; } } }while(1); return len; } int do_write(int fd, unsigned char *buf,int len) { int ret = 0; int write_offset = 0; int write_len = len; do { ret = write(fd, buf+write_offset, write_len); if (ret < 0) { ALOGE("%s: write failed ret = %d err = %s",__func__,ret,strerror(errno)); return -1; } else if (ret == 0) { ALOGE("%s: Write returned 0, err = %s, Written bytes: %d, expected: %d", __func__, strerror(errno), (len-write_len), len); return (len-write_len); } else { if (ret < write_len) { ALOGD("%s, Write pending,do write ret = %d err = %s",__func__,ret, strerror(errno)); write_len = write_len - ret; write_offset = ret; } else if (ret > write_len) { ALOGE("%s: FATAL wrote more than expected: written bytes: %d expected: %d", __func__, write_len, ret); break; } else { ALOGV("Write successful"); break; } } } while(1); return len; } void vnd_load_if() { void *dlhandle; unsigned char bdaddr[] = {0xaa, 0xbb, 0xcc, 0x11, 0x22, 0x33}; dlhandle = dlopen("libbt-vendor.so", RTLD_NOW); if (!dlhandle) { ALOGE("!!! Failed to load libbt-vendor.so !!!"); return; } fm_if = (bt_vendor_interface_t *) dlsym(dlhandle, "BLUETOOTH_VENDOR_LIB_INTERFACE"); if (!fm_if) { ALOGE("!!! Failed to get bt vendor interface !!!"); return; } ALOGI("FM-HCI: Registering the WCNSS HAL library by passing CBs and BD addr."); fm_if->init(&fmhci_vendor_callbacks, bdaddr); } int main() { fd_set client_fds; int retval = -1, n; ALOGI("%s: Entry ", __func__); ALOGI("FM HAL SERVICE: Loading the WCNSS HAL library..."); vnd_load_if(); ALOGI("create socket"); remote_fm_hal_fd = establish_fm_remote_socket(FM_HAL_SOCK); if (remote_fm_hal_fd < 0) { ALOGE("%s: invalid remote socket", __func__); return -1; } FD_ZERO(&client_fds); FD_SET(remote_fm_hal_fd, &client_fds); do { ALOGI("%s: Step 1-FM-HAL SERVICE: Waiting for FM HAL cmd ", __func__); n = select(remote_fm_hal_fd+1, &client_fds, NULL, NULL, NULL); if(n < 0){ ALOGE("Select: failed: %s", strerror(errno)); break; } ALOGI("%s: Step 2-FM-HAL SERVICE: FM POWER CMD available for processing...\n", __func__); if (FD_ISSET(remote_fm_hal_fd, &client_fds)) { retval = handle_fmcommand_writes(remote_fm_hal_fd); ALOGI("%s: handle_fmcommand_writes . %d", __func__, retval); if(retval < 0) { if (retval == -99) { ALOGI("%s:End of wait loop", __func__); break; } ALOGI("%s: handle_fmcommand_writes returns: %d: ", __func__, retval); // break; } } } while(1); service_cleanup(); ALOGI("%s: FM turned off or power off failed .service kill itself", __func__); close(remote_fm_hal_fd); remote_fm_hal_fd = 0; ALOGI("%s: Exit: %d", __func__, retval); return retval; }