C++程序  |  205行  |  5.64 KB

/*
 *
 *  BlueZ - Bluetooth protocol stack for Linux
 *
 *  Copyright (C) 2004-2009  Marcel Holtmann <marcel@holtmann.org>
 *  Copyright (C) 2009 The Android Open Source Project
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 */

#include <unistd.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/socket.h>

#include <private/android_filesystem_config.h>
#include <sys/prctl.h>
#include <linux/capability.h>

#include <bluetooth/bluetooth.h>
#include <bluetooth/hci.h>

/* Set UID to bluetooth w/ CAP_NET_RAW, CAP_NET_ADMIN and CAP_NET_BIND_SERVICE
 * (Android's init.rc does not yet support applying linux capabilities) */
void android_set_aid_and_cap() {
	prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
	setuid(AID_BLUETOOTH);

	struct __user_cap_header_struct header;
	struct __user_cap_data_struct cap;
	header.version = _LINUX_CAPABILITY_VERSION;
	header.pid = 0;
	cap.effective = cap.permitted = 1 << CAP_NET_RAW |
					1 << CAP_NET_ADMIN |
					1 << CAP_NET_BIND_SERVICE;
	cap.inheritable = 0;
	capset(&header, &cap);
}

static int write_flush_timeout(int fd, uint16_t handle,
        unsigned int timeout_ms) {
    uint16_t timeout = (timeout_ms * 1000) / 625;  // timeout units of 0.625ms
    unsigned char hci_write_flush_cmd[] = {
        0x01,               // HCI command packet
        0x28, 0x0C,         // HCI_Write_Automatic_Flush_Timeout
        0x04,               // Length
        0x00, 0x00,         // Handle
        0x00, 0x00,         // Timeout
    };

    hci_write_flush_cmd[4] = (uint8_t)handle;
    hci_write_flush_cmd[5] = (uint8_t)(handle >> 8);
    hci_write_flush_cmd[6] = (uint8_t)timeout;
    hci_write_flush_cmd[7] = (uint8_t)(timeout >> 8);

    int ret = write(fd, hci_write_flush_cmd, sizeof(hci_write_flush_cmd));
    if (ret < 0) {
        error("write(): %s (%d)]", strerror(errno), errno);
        return -1;
    } else if (ret != sizeof(hci_write_flush_cmd)) {
        error("write(): unexpected length %d", ret);
        return -1;
    }
    return 0;
}

#ifdef BOARD_HAVE_BLUETOOTH_BCM
static int vendor_high_priority(int fd, uint16_t handle) {
    unsigned char hci_sleep_cmd[] = {
        0x01,               // HCI command packet
        0x57, 0xfc,         // HCI_Write_High_Priority_Connection
        0x02,               // Length
        0x00, 0x00          // Handle
    };

    hci_sleep_cmd[4] = (uint8_t)handle;
    hci_sleep_cmd[5] = (uint8_t)(handle >> 8);

    int ret = write(fd, hci_sleep_cmd, sizeof(hci_sleep_cmd));
    if (ret < 0) {
        error("write(): %s (%d)]", strerror(errno), errno);
        return -1;
    } else if (ret != sizeof(hci_sleep_cmd)) {
        error("write(): unexpected length %d", ret);
        return -1;
    }
    return 0;
}

static int get_hci_sock() {
    int sock = socket(AF_BLUETOOTH, SOCK_RAW, BTPROTO_HCI);
    struct sockaddr_hci addr;
    int opt;

    if(sock < 0) {
        error("Can't create raw HCI socket!");
        return -1;
    }

    opt = 1;
    if (setsockopt(sock, SOL_HCI, HCI_DATA_DIR, &opt, sizeof(opt)) < 0) {
        error("Error setting data direction\n");
        return -1;
    }

    /* Bind socket to the HCI device */
    addr.hci_family = AF_BLUETOOTH;
    addr.hci_dev = 0;  // hci0
    if(bind(sock, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
        error("Can't attach to device hci0. %s(%d)\n",
             strerror(errno),
             errno);
        return -1;
    }
    return sock;
}

static int get_acl_handle(int fd, bdaddr_t *bdaddr) {
    int i;
    int ret = -1;
    struct hci_conn_list_req *conn_list;
    struct hci_conn_info *conn_info;
    int max_conn = 10;

    conn_list = malloc(max_conn * (
            sizeof(struct hci_conn_list_req) + sizeof(struct hci_conn_info)));
    if (!conn_list) {
        error("Out of memory in %s\n", __FUNCTION__);
        return -1;
    }

    conn_list->dev_id = 0;  /* hardcoded to HCI device 0 */
    conn_list->conn_num = max_conn;

    if (ioctl(fd, HCIGETCONNLIST, (void *)conn_list)) {
        error("Failed to get connection list\n");
        goto out;
    }

    for (i=0; i < conn_list->conn_num; i++) {
        conn_info = &conn_list->conn_info[i];
        if (conn_info->type == ACL_LINK &&
                !memcmp((void *)&conn_info->bdaddr, (void *)bdaddr,
                sizeof(bdaddr_t))) {
            ret = conn_info->handle;
            goto out;
        }
    }
    ret = 0;

out:
    free(conn_list);
    return ret;
}

/* Request that the ACL link to a given Bluetooth connection be high priority,
 * for improved coexistance support
 */
int android_set_high_priority(bdaddr_t *ba) {
    int ret;
    int fd = get_hci_sock();
    int acl_handle;

    if (fd < 0)
        return fd;

    acl_handle = get_acl_handle(fd, ba);
    if (acl_handle < 0) {
        ret = acl_handle;
        goto out;
    }

    ret = vendor_high_priority(fd, acl_handle);
    if (ret < 0)
        goto out;
    ret = write_flush_timeout(fd, acl_handle, 200);

out:
    close(fd);

    return ret;
}

#else

int android_set_high_priority(bdaddr_t *ba) {
    return 0;
}

#endif