Android の Bluetooth PAN の実装について調べてみました。
対象のソースコードのバージョンは2016年時点のmasterです。
ネットワークインターフェイスの生成
PANを開始すると、bt-panという名前のnetwork interfaceが生成されます。
ネットワークインターフェイスの生成はBluetoothプロトコルスタックで行っています。
# define TAP_IF_NAME "bt-pan"
int btpan_tap_open()
{
struct ifreq ifr;
int fd, err;
const char *clonedev = "/dev/tun";
/* open the clone device */
if ((fd = open(clonedev, O_RDWR)) < 0)
{
BTIF_TRACE_DEBUG("could not open %s, err:%d", clonedev, errno);
return fd;
}
memset(&ifr, 0, sizeof(ifr));
ifr.ifr_flags = IFF_TAP | IFF_NO_PI;
strncpy(ifr.ifr_name, TAP_IF_NAME, IFNAMSIZ);
/* try to create the device */
if ((err = ioctl(fd, TUNSETIFF, (void *) &ifr)) < 0)
{
BTIF_TRACE_DEBUG("ioctl error:%d, errno:%s", err, strerror(errno));
close(fd);
return err;
}
if (tap_if_up(TAP_IF_NAME, controller_get_interface()->get_address()) == 0)
{
int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);
return fd;
}
BTIF_TRACE_ERROR("can not bring up tap interface:%s", TAP_IF_NAME);
close(fd);
return INVALID_FD;
}
ソースコードを説明すると、
- /dev/tunというtunnel deviceをOPENする
- ioctlで、bt-pan のネットワークインターフェイスを生成する
- ioctlで、bt-panをupする。 /dev/tunを開いたfile descriptorを返す
データの送信
PANのデータ送信は、PANのネットワークインターフェイス bt-panを経由して行われる。
Bluetoothプロトコルスタックは、bt-panに書き込まれたデータは/dev/tunを経由して読みだす。
bt-panからデータを受け取るまで
Bluetoothプロトコルスタックは/dev/tunを読み出すために、専用のthreadを使う。
void bta_pan_co_open(uint16_t handle, uint8_t app_id, tBTA_PAN_ROLE local_role,
tBTA_PAN_ROLE peer_role, BD_ADDR peer_addr)
{
BTIF_TRACE_API("bta_pan_co_open:app_id:%d, local_role:%d, peer_role:%d, "
"handle:%d", app_id, local_role, peer_role, handle);
btpan_conn_t* conn = btpan_find_conn_addr(peer_addr);
if(conn == NULL)
conn = btpan_new_conn(handle, peer_addr, local_role, peer_role);
if(conn)
{
BTIF_TRACE_DEBUG("bta_pan_co_open:tap_fd:%d, open_count:%d, "
"conn->handle:%d should = handle:%d, local_role:%d, remote_role:%d",
btpan_cb.tap_fd, btpan_cb.open_count, conn->handle, handle,
conn->local_role, conn->remote_role);
//refresh the role & bt address
btpan_cb.open_count++;
conn->handle = handle;
//bdcpy(conn->peer, peer_addr);
if(btpan_cb.tap_fd < 0)
{
btpan_cb.tap_fd = btpan_tap_open();
if(btpan_cb.tap_fd >= 0)
create_tap_read_thread(btpan_cb.tap_fd);
}
if(btpan_cb.tap_fd >= 0)
{
btpan_cb.flow = 1;
conn->state = PAN_STATE_OPEN;
bta_pan_ci_rx_ready(ha*(ndle);
}
}
}
void create_tap_read_thread(int tap_fd)
{
if (pan_pth < 0)
pan_pth = btsock_thread_create(btpan_tap_fd_signaled, NULL);
if (pan_pth >= 0)
btsock_thread_add_fd(pan_pth, tap_fd, 0, SOCK_THREAD_FD_RD, 0);
}
static void btpan_tap_fd_signaled(int fd, int type, int flags, uint32_t user_id) {
assert(btpan_cb.tap_fd == INVALID_FD || btpan_cb.tap_fd == fd);
if (btpan_cb.tap_fd != fd) {
BTIF_TRACE_WARNING("%s Signaled on mismatched fds exp:%d act:%d\n",
__func__, btpan_cb.tap_fd, fd);
return;
}
if (flags & SOCK_THREAD_FD_EXCEPTION) {
btpan_cb.tap_fd = INVALID_FD;
btpan_tap_close(fd);
btif_pan_close_all_conns();
} else if (flags & SOCK_THREAD_FD_RD)
bta_dmexecutecallback(btu_exec_tap_fd_read, INT_TO_PTR(fd));
}
ソースコードを説明すると
- 先ほど説明したbtpan_tap_open()でbt-panの生成と/dev/tunのopenを行う
- create_tap_read_thread()でスレッドを生成する。
- スレッドはbtpan_tap_fd_signaled()でsignal処理をし、/dev/tunのfdと関連づけられる
- スレッドはSOCK_THREAD_FD_RDのシグナルを受信すると、PANの送信データをbta_dmexecutecallback()に渡す
PANの送信
bta_dmexecutecallback(btu_exec_tap_fd_read, INT_TO_PTR(fd));
の先の話です。btaのコールバックとしてbtu_exec_tap_fd_read()を実行します。
- buffer を確保する。
- read()でbufferに送信データをコピーする
- 送信データをforward_bnep()に渡す
- forward_bnep()はPAN_WriteBuf()に送信データを渡す。
- PAN_WriteBuf()はBNEP_Write()に送信データを渡す。
Bluetooth PANの Java部分
frameworks/base に PANのコードが含まれている
frameworks/base/core/java/android/bluetooth/BluetoothPan.java
Bluetooth tethering
Bluetooth テザリングのコードはframeworks/base に含まれている。
frameworks/base/services/core/java/com/android/server/connectivity/Tethering.java
テザリングを有効にしている部分。TetherInterfaceSMはテザリングのステートマシンのクラス。
TetherInterfaceSM sm = mIfaces.get(iface);
if (sm != null) {
if (VDBG) Log.d(TAG, "active iface (" + iface + ") reported as added, ignoring");
return;
}
sm = new TetherInterfaceSM(iface, mLooper, usb);
mIfaces.put(iface, sm);
sm.start();