blob: 4ec1b249a41adbfc04bd641d5ab58751c727dec6 [file] [log] [blame]
// Copyright 2016 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package io.v.android.impl.google.discovery.plugins.ble;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothServerSocket;
import android.bluetooth.BluetoothSocket;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
/**
* An advertiser that broadcasts Vanadium services through Bluetooth.
*/
class BluetoothAdvertiser {
private static final String TAG = Driver.TAG;
private final Context mContext;
private final BluetoothAdapter mBluetoothAdapter;
private RfcommListener mRfcommListener;
private final Set<UUID> mServices;
private class RfcommListener extends Thread {
private BluetoothServerSocket mServerSocket;
public void run() {
try {
mServerSocket =
mBluetoothAdapter.listenUsingInsecureRfcommWithServiceRecord(
Constants.SDP_NAME, Constants.SDP_UUID);
} catch (IOException e) {
Log.e(TAG, "rfcomm listen failed", e);
return;
}
for (; ; ) {
try (BluetoothSocket socket = mServerSocket.accept();
ObjectInputStream in = new ObjectInputStream(socket.getInputStream());
ObjectOutputStream out = new ObjectOutputStream(socket.getOutputStream())) {
Set<UUID> uuids = (Set<UUID>) in.readObject();
boolean found;
synchronized (BluetoothAdvertiser.this) {
if (uuids.isEmpty()) {
found = !mServices.isEmpty();
} else {
found = !Collections.disjoint(mServices, uuids);
}
}
out.writeBoolean(found);
out.flush();
} catch (Exception e) {
Log.e(TAG, "rfcomm accept failed", e);
break;
}
}
}
void close() {
if (mServerSocket == null) {
return;
}
try {
mServerSocket.close();
} catch (IOException e) {
Log.e(TAG, "close failed", e);
}
}
}
BluetoothAdvertiser(Context context, BluetoothAdapter bluetoothAdapter) {
mContext = context;
mBluetoothAdapter = bluetoothAdapter;
mServices = new HashSet<>();
}
/**
* Add a service to the Bluetooth advertiser.
*/
synchronized void addService(UUID uuid, int discoverableDurationInSec) {
mServices.add(uuid);
if (discoverableDurationInSec <= 0) {
return;
}
if (mRfcommListener == null) {
mRfcommListener = new RfcommListener();
mRfcommListener.start();
}
// Make it discoverable if not already in discoverable mode.
if (mBluetoothAdapter.getScanMode()
== BluetoothAdapter.SCAN_MODE_CONNECTABLE_DISCOVERABLE) {
Log.w(TAG, "already in discoverable mode");
return;
}
Intent discoverableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE);
discoverableIntent.putExtra(
BluetoothAdapter.EXTRA_DISCOVERABLE_DURATION, discoverableDurationInSec);
discoverableIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(discoverableIntent);
}
/**
* Removes a service from the Bluetooth advertiser.
*/
synchronized void removeService(UUID uuid) {
mServices.remove(uuid);
if (mServices.isEmpty() && mRfcommListener != null) {
mRfcommListener.close();
mRfcommListener = null;
}
}
/**
* Closes the BT advertiser.
*/
synchronized void close() {
mServices.clear();
if (mRfcommListener != null) {
mRfcommListener.close();
mRfcommListener = null;
}
}
}