Got advertising to work.
Change-Id: I5a7e7997fbdcdae267c6ef768654f241f374c61d
diff --git a/android-lib/src/main/java/io/v/android/libs/discovery/ble/BlePlugin.java b/android-lib/src/main/java/io/v/android/libs/discovery/ble/BlePlugin.java
index 67ad58a..88a2fbf 100644
--- a/android-lib/src/main/java/io/v/android/libs/discovery/ble/BlePlugin.java
+++ b/android-lib/src/main/java/io/v/android/libs/discovery/ble/BlePlugin.java
@@ -4,9 +4,9 @@
package io.v.android.libs.discovery.ble;
-import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
+import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCharacteristic;
import android.bluetooth.BluetoothGattServer;
import android.bluetooth.BluetoothGattServerCallback;
@@ -30,6 +30,7 @@
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -46,14 +47,21 @@
import io.v.impl.google.lib.discovery.ScanHandler;
import io.v.x.ref.lib.discovery.Advertisement;
+/**
+ * BlePlugin implements the Discovery Plugin Interface for Bluetooth.
+ */
public class BlePlugin {
+ // We are using a constant for the MTU because Android and paypal/gatt don't get along
+ // when the paypal gatt client sends a setMTU message. The Android server seems to send
+ // a malformed L2CAP message.
+ private static final int MTU = 23;
+
// Object used to lock advertisement objects.
private final Object advertisementLock;
// The id to assign to the next advertisment.
private int nextAdv;
// A map of advertisement ids to the advertisement that corresponds to them.
private Map<Integer, BluetoothGattService> advertisements;
-
// A map of advertisement ids to the thread waiting for cancellation of the context.
private Map<Integer, Thread> advCancellationThreads;
@@ -62,20 +70,26 @@
// A map of scanner ids to the thread waiting for cancellation of the context.
private Map<Integer, Thread> scanCancellationThreads;
private DeviceCache cachedDevices;
+ // Used to track the set of devices we currently talking to.
+ private Set<String> pendingCalls;
+ // Set of Ble objects that will be interacted with to perform operations.
private BluetoothLeAdvertiser bluetoothLeAdvertise;
private BluetoothLeScanner bluetoothLeScanner;
private BluetoothGattServer bluetoothGattServer;
- private ScanCallback callback;
- private Set<String> pendingCalls;
-
+ // We need to hold onto the callbacks for scan an advertise because that is what is used
+ // to stop the operation.
+ private ScanCallback scanCallback;
+ private AdvertiseCallback advertiseCallback;
private boolean isScanning;
private Context androidContext;
+ // A thread to wait for the cancellation of a particular advertisement. VContext.done().await()
+ // is blocking so have to spin up a thread per outstanding advertisement.
private class AdvertisementCancellationRunner implements Runnable{
private VContext mCtx;
@@ -96,6 +110,7 @@
}
}
+ // Similar to AdvertisementCancellationRunner except for scanning.
private class ScannerCancellationRunner implements Runnable{
private VContext mCtx;
@@ -138,10 +153,29 @@
public void onConnectionStateChange(BluetoothDevice device, int status, int newState) {
super.onConnectionStateChange(device, status, newState);
}
+
+ @Override
+ public void onCharacteristicReadRequest(BluetoothDevice device, int requestId, int offset, BluetoothGattCharacteristic characteristic) {
+ super.onCharacteristicReadRequest(device, requestId, offset, characteristic);
+ byte[] total =characteristic.getValue();
+ byte[] res = {};
+ // Only send MTU - 1 bytes. The first byte of all packets is the op code..
+ if (offset < total.length) {
+ int finalByte = offset + MTU - 1;
+ if (finalByte > total.length) {
+ finalByte = total.length;
+ }
+ res = Arrays.copyOfRange(total, offset, finalByte);
+ bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_SUCCESS, 0, res);
+ } else {
+ bluetoothGattServer.sendResponse(device, requestId, BluetoothGatt.GATT_FAILURE, 0, res);
+ }
+ }
});
}
+ // Converts a Vanadium Advertisement to a Bluetooth gatt service.
private BluetoothGattService convertToService(Advertisement adv) throws IOException {
Map<UUID, byte[]> attributes = BleAdvertisementConverter.vadvertismentToBleAttr(adv);
BluetoothGattService service = new BluetoothGattService(
@@ -164,12 +198,16 @@
t.start();
advCancellationThreads.put(currentId, t);
bluetoothGattServer.addService(service);
-
+ readvertise();
}
}
private void removeAdvertisement(int id) {
synchronized (advertisements) {
+ BluetoothGattService s = advertisements.get(id);
+ if (s != null) {
+ bluetoothGattServer.removeService(s);
+ }
advertisements.remove(id);
advCancellationThreads.remove(id);
readvertise();
@@ -198,7 +236,7 @@
private void updateScanning() {
if (isScanning && scanCancellationThreads.size() == 0) {
isScanning = false;
- bluetoothLeScanner.stopScan(callback);
+ bluetoothLeScanner.stopScan(scanCallback);
return;
}
@@ -213,16 +251,13 @@
scanFilter.add(builder.build());
- callback = new ScanCallback() {
+ scanCallback = new ScanCallback() {
@Override
public void onScanResult(int callbackType, ScanResult result) {
// in L the only value for callbackType is CALLBACK_TYPE_ALL_MATCHES, so
// we don't look at it's value.
- Log.d("vanadium", "Got a callback");
ScanRecord record = result.getScanRecord();
byte[] data = record.getManufacturerSpecificData(1001);
- Log.d("vanadium", "result is " + result.getDevice().getName());
- Log.d("vanadium", "data is " + data);
ByteBuffer buffer = ByteBuffer.wrap(data);
final long hash = buffer.getLong();
final String deviceId = result.getDevice().getAddress();
@@ -255,11 +290,11 @@
pendingCalls.remove(deviceId);
}
bluetoothLeScanner.startScan(scanFilter, new ScanSettings.Builder().
- setScanMode(ScanSettings.SCAN_MODE_BALANCED).build(), callback);
+ setScanMode(ScanSettings.SCAN_MODE_BALANCED).build(), scanCallback);
}
};
BluetoothGattClientCallback cb = new BluetoothGattClientCallback(ccb);
- bluetoothLeScanner.stopScan(callback);
+ bluetoothLeScanner.stopScan(scanCallback);
Log.d("vanadium", "connecting to " + result.getDevice());
result.getDevice().connectGatt(androidContext, false, cb);
}
@@ -273,11 +308,19 @@
}
};
bluetoothLeScanner.startScan(scanFilter, new ScanSettings.Builder().
- setScanMode(ScanSettings.SCAN_MODE_BALANCED).build(), callback);
+ setScanMode(ScanSettings.SCAN_MODE_BALANCED).build(), scanCallback);
}
}
private void readvertise() {
+ if (advertiseCallback != null) {
+ bluetoothLeAdvertise.stopAdvertising(advertiseCallback);
+ advertiseCallback = null;
+ }
+ if (advertisements.size() == 0) {
+ return;
+ }
+
int hash = advertisements.hashCode();
AdvertiseData.Builder builder = new AdvertiseData.Builder();
@@ -286,10 +329,9 @@
buf.putLong(hash);
builder.addManufacturerData(1001, buf.array());
AdvertiseSettings.Builder settingsBuilder = new AdvertiseSettings.Builder();
- settingsBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_BALANCED);
+ settingsBuilder.setAdvertiseMode(AdvertiseSettings.ADVERTISE_MODE_LOW_LATENCY);
settingsBuilder.setConnectable(true);
- bluetoothLeAdvertise.startAdvertising(settingsBuilder.build(), builder.build(),
- new AdvertiseCallback() {
+ advertiseCallback = new AdvertiseCallback() {
@Override
public void onStartSuccess(AdvertiseSettings settingsInEffect) {
Log.i("vanadium", "Successfully started " + settingsInEffect);
@@ -299,6 +341,8 @@
public void onStartFailure(int errorCode) {
Log.i("vanadium", "Failed to start advertising " + errorCode);
}
- });
+ };
+ bluetoothLeAdvertise.startAdvertising(settingsBuilder.build(), builder.build(),
+ advertiseCallback);
}
}
\ No newline at end of file
diff --git a/android-lib/src/main/java/io/v/android/libs/discovery/ble/BluetoothGattClientCallback.java b/android-lib/src/main/java/io/v/android/libs/discovery/ble/BluetoothGattClientCallback.java
index 1e848e1..067ba8d 100644
--- a/android-lib/src/main/java/io/v/android/libs/discovery/ble/BluetoothGattClientCallback.java
+++ b/android-lib/src/main/java/io/v/android/libs/discovery/ble/BluetoothGattClientCallback.java
@@ -4,7 +4,6 @@
package io.v.android.libs.discovery.ble;
-import android.app.Activity;
import android.bluetooth.BluetoothGatt;
import android.bluetooth.BluetoothGattCallback;
import android.bluetooth.BluetoothGattCharacteristic;
@@ -19,7 +18,7 @@
import java.util.UUID;
/**
- * Created by bjornick on 10/6/15.
+ * BluetoothGattClientCallback is a handler for responses from a GattServer.
*/
public class BluetoothGattClientCallback extends BluetoothGattCallback {
public interface Callback {
@@ -43,11 +42,17 @@
@Override
public void onServicesDiscovered(BluetoothGatt gatt, int status) {
for (BluetoothGattService service : gatt.getServices()) {
+ // Skip the GATT AND GAP Services.
if (service.getUuid().toString().startsWith("0000180")) {
continue;
}
services.put(service.getUuid(), new HashMap<UUID, byte[]>());
- chars.addAll(service.getCharacteristics());
+ // We only keep track of the characteristics that can be read.
+ for (BluetoothGattCharacteristic ch : chars) {
+ if ((ch.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) != 0) {
+ chars.add(ch);
+ }
+ }
}
pos = 0;
maybeReadNextCharacteristic();
@@ -63,13 +68,10 @@
return;
}
BluetoothGattCharacteristic c = chars.get(pos++);
-
- if ((c.getProperties() & BluetoothGattCharacteristic.PROPERTY_READ) == 0) {
+ if (!gatt.readCharacteristic(c)) {
+ Log.d("vanadium", "Failed to read characteristic " + c.getUuid());
maybeReadNextCharacteristic();
- return;
}
-
- Log.d("vanadium", "trying to read " + c + " " + gatt.readCharacteristic(c));
}
@Override
diff --git a/lib/src/main/java/io/v/impl/google/lib/discovery/DeviceCache.java b/lib/src/main/java/io/v/impl/google/lib/discovery/DeviceCache.java
index 97c250b..480de84 100644
--- a/lib/src/main/java/io/v/impl/google/lib/discovery/DeviceCache.java
+++ b/lib/src/main/java/io/v/impl/google/lib/discovery/DeviceCache.java
@@ -18,59 +18,56 @@
import io.v.x.ref.lib.discovery.Advertisement;
-/**
- * Created by bjornick on 10/9/15.
- */
public class DeviceCache {
- private Map<Long, CacheEntry> mCachedDevices;
- private Map<String, CacheEntry> mKnownIds;
+ private Map<Long, CacheEntry> cachedDevices;
+ private Map<String, CacheEntry> knownIds;
private class CacheEntry {
- Set<Advertisement> mAdvertisements;
+ Set<Advertisement> advertisements;
- long mHash;
+ long hash;
- Date mLastSeen;
+ Date lastSeen;
- String mDeviceId;
+ String deviceId;
CacheEntry(Set<Advertisement> advs, long hash, String deviceId) {
- mAdvertisements = advs;
- mHash = hash;
- mLastSeen = new Date();
- mDeviceId = deviceId;
+ advertisements = advs;
+ this.hash = hash;
+ lastSeen = new Date();
+ this.deviceId = deviceId;
}
}
- int mNextScanner;
- private Map<UUID, Set<Advertisement>> mKnownServices;
- private Map<Integer, VScanner> mScannersById;
- private Map<UUID, Set<VScanner>> mScannersByUUID;
+ int nextScanner;
+ private Map<UUID, Set<Advertisement>> knownServices;
+ private Map<Integer, VScanner> scannersById;
+ private Map<UUID, Set<VScanner>> scannersByUUID;
- private Duration mMaxAge;
+ private Duration maxAge;
public DeviceCache(Duration maxAge) {
- mCachedDevices = new HashMap<>();
- mKnownIds = new HashMap<>();
- mKnownServices = new HashMap<>();
- mScannersById = new HashMap<>();
- mScannersByUUID = new HashMap<>();
- mMaxAge = maxAge;
- mNextScanner = 0;
+ cachedDevices = new HashMap<>();
+ knownIds = new HashMap<>();
+ knownServices = new HashMap<>();
+ scannersById = new HashMap<>();
+ scannersByUUID = new HashMap<>();
+ this.maxAge = maxAge;
+ nextScanner = 0;
}
public boolean haveSeenHash(long hash, String deviceId) {
synchronized (this) {
- CacheEntry entry = mCachedDevices.get(hash);
+ CacheEntry entry = cachedDevices.get(hash);
if (entry != null) {
- entry.mLastSeen = new Date();
- if (!entry.mDeviceId.equals(deviceId)) {
+ entry.lastSeen = new Date();
+ if (!entry.deviceId.equals(deviceId)) {
// This probably happened becuase a device has changed it's ble mac address.
// We need to update the mac address for this entry.
- mKnownIds.remove(entry.mDeviceId);
- entry.mDeviceId = deviceId;
- mKnownIds.put(deviceId, entry);
+ knownIds.remove(entry.deviceId);
+ entry.deviceId = deviceId;
+ knownIds.put(deviceId, entry);
}
}
return entry != null;
@@ -80,19 +77,19 @@
public void saveDevice(long hash, Set<Advertisement> advs, String deviceId) {
CacheEntry entry = new CacheEntry(advs, hash, deviceId);
synchronized (this) {
- CacheEntry oldEntry = mKnownIds.get(deviceId);
+ CacheEntry oldEntry = knownIds.get(deviceId);
Set<Advertisement> oldValues = null;
if (oldEntry != null) {
- mCachedDevices.remove(oldEntry.mHash);
- mKnownIds.remove(oldEntry.mDeviceId);
- oldValues = oldEntry.mAdvertisements;
+ cachedDevices.remove(oldEntry.hash);
+ knownIds.remove(oldEntry.deviceId);
+ oldValues = oldEntry.advertisements;
} else {
oldValues = new HashSet<>();
}
Sets.SetView<Advertisement> removed = Sets.difference(oldValues, advs);
for (Advertisement adv : removed) {
UUID uuid = UUIDUtil.UuidtUUID(adv.getServiceUuid());
- Set<Advertisement> set = mKnownServices.get(uuid);
+ Set<Advertisement> set = knownServices.get(uuid);
if (set != null) {
set.remove(adv);
@@ -100,7 +97,7 @@
handleUpdate(adv);
if (set.size() == 0) {
- mKnownServices.remove(uuid);
+ knownServices.remove(uuid);
}
}
}
@@ -108,27 +105,27 @@
Sets.SetView<Advertisement> added = Sets.difference(advs, oldValues);
for (Advertisement adv: added) {
UUID uuid = UUIDUtil.UuidtUUID(adv.getServiceUuid());
- Set<Advertisement> set = mKnownServices.get(uuid);
+ Set<Advertisement> set = knownServices.get(uuid);
if (set == null) {
set = new HashSet<>();
- mKnownServices.put(uuid, set);
+ knownServices.put(uuid, set);
}
set.add(adv);
handleUpdate(adv);
}
- mCachedDevices.put(hash, entry);
- CacheEntry oldDeviceEntry = mKnownIds.get(deviceId);
+ cachedDevices.put(hash, entry);
+ CacheEntry oldDeviceEntry = knownIds.get(deviceId);
if (oldDeviceEntry != null) {
// Delete the old hash value.
- mCachedDevices.remove(hash);
+ cachedDevices.remove(hash);
}
- mKnownIds.put(deviceId, entry);
+ knownIds.put(deviceId, entry);
}
}
private void handleUpdate(Advertisement adv) {
UUID uuid = UUIDUtil.UuidtUUID(adv.getServiceUuid());
- Set<VScanner> scanners = mScannersByUUID.get(uuid);
+ Set<VScanner> scanners = scannersByUUID.get(uuid);
if (scanners == null) {
return;
}
@@ -139,14 +136,14 @@
public int addScanner(VScanner scanner) {
synchronized (this) {
- int id = mNextScanner++;
- mScannersById.put(id, scanner);
- Set<VScanner> scanners = mScannersByUUID.get(scanner.getmServiceUUID());
+ int id = nextScanner++;
+ scannersById.put(id, scanner);
+ Set<VScanner> scanners = scannersByUUID.get(scanner.getmServiceUUID());
if (scanners == null) {
scanners = new HashSet<>();
- mScannersByUUID.put(scanner.getmServiceUUID(), scanners);
+ scannersByUUID.put(scanner.getmServiceUUID(), scanners);
}
- Set<Advertisement> knownAdvs = mKnownServices.get(scanner.getmServiceUUID());
+ Set<Advertisement> knownAdvs = knownServices.get(scanner.getmServiceUUID());
if (knownAdvs != null) {
for (Advertisement adv : knownAdvs) {
scanner.getHandler().handleUpdate(adv);
@@ -159,16 +156,16 @@
public void removeScanner(int id) {
synchronized (this) {
- VScanner scanner = mScannersById.get(id);
+ VScanner scanner = scannersById.get(id);
if (scanner != null) {
- Set<VScanner> list = mScannersByUUID.get(scanner.getmServiceUUID());
+ Set<VScanner> list = scannersByUUID.get(scanner.getmServiceUUID());
if (list != null) {
list.remove(scanner);
if (list.size() == 0) {
- mScannersByUUID.remove(scanner.getmServiceUUID());
+ scannersByUUID.remove(scanner.getmServiceUUID());
}
}
- mScannersById.remove(id);
+ scannersById.remove(id);
}
}
}
diff --git a/lib/src/main/java/io/v/impl/google/lib/discovery/EncodingUtil.java b/lib/src/main/java/io/v/impl/google/lib/discovery/EncodingUtil.java
index 2972de5..76c8775 100644
--- a/lib/src/main/java/io/v/impl/google/lib/discovery/EncodingUtil.java
+++ b/lib/src/main/java/io/v/impl/google/lib/discovery/EncodingUtil.java
@@ -18,9 +18,6 @@
import io.v.x.ref.lib.discovery.EncryptionKey;
-/**
- * Created by bjornick on 10/8/15.
- */
public class EncodingUtil {
static final Charset utf8 = Charset.forName("UTF-8");
private static void writeUint(OutputStream out, int value) throws IOException {
@@ -102,19 +99,19 @@
}
static public class KeysAndAlgorithm {
- int mEncryptionAlgorithm;
- List<EncryptionKey> mKeys;
+ int encryptionAlgorithm;
+ List<EncryptionKey> keys;
public int getEncryptionAlgorithm() {
- return mEncryptionAlgorithm;
+ return encryptionAlgorithm;
}
public List<EncryptionKey> getKeys() {
- return mKeys;
+ return keys;
}
KeysAndAlgorithm(int encryptionAlgo, List<EncryptionKey> keys) {
- mEncryptionAlgorithm = encryptionAlgo;
- mKeys = keys;
+ encryptionAlgorithm = encryptionAlgo;
+ this.keys = keys;
}
}
diff --git a/lib/src/main/java/io/v/impl/google/lib/discovery/UUIDUtil.java b/lib/src/main/java/io/v/impl/google/lib/discovery/UUIDUtil.java
index 8b37b3a..f754241 100644
--- a/lib/src/main/java/io/v/impl/google/lib/discovery/UUIDUtil.java
+++ b/lib/src/main/java/io/v/impl/google/lib/discovery/UUIDUtil.java
@@ -12,13 +12,19 @@
import io.v.x.ref.lib.discovery.Uuid;
/**
- * Created by bjornick on 10/7/15.
+ * UUIDUtil is a class for generating v5 UUIDs and converting from java.util.UUID and
+ * io.v.impl.google.lib.discovery.Uuid.
*/
public class UUIDUtil {
public static native UUID UUIDForInterfaceName(String name);
public static native UUID UUIDForAttributeKey(String key);
+ /**
+ * Converts from java.util.UUID to io.v.impl.google.lib.discovery.Uuid.
+ * @param id The java.util.UUID
+ * @return The io.v.impl.google.lib.discovery.Uuid version of id
+ */
public static Uuid UUIDtUuid(UUID id) {
ByteBuffer b = ByteBuffer.allocate(16);
b.putLong(id.getMostSignificantBits());
@@ -26,6 +32,11 @@
return new Uuid(Bytes.asList(b.array()));
}
+ /**
+ * Converts from io.v.impl.google.lib.discovery.Uuid to java.util.UUID
+ * @param id The io.v.impl.google.lib.discovery.Uuid
+ * @return The java.util.UUID version of id
+ */
public static UUID UuidtUUID(Uuid id) {
ByteBuffer b = ByteBuffer.wrap(Bytes.toArray(id));
return new UUID(b.getLong(), b.getLong());
diff --git a/lib/src/main/java/io/v/impl/google/lib/discovery/VScanner.java b/lib/src/main/java/io/v/impl/google/lib/discovery/VScanner.java
index 3664c8f..2172f61 100644
--- a/lib/src/main/java/io/v/impl/google/lib/discovery/VScanner.java
+++ b/lib/src/main/java/io/v/impl/google/lib/discovery/VScanner.java
@@ -8,6 +8,9 @@
import io.v.impl.google.lib.discovery.ScanHandler;
+/**
+ * VScanenr wraps a ServiceUUID and a ScanHandler.
+ */
public class VScanner {
private UUID serviceUUID;
diff --git a/lib/src/main/java/io/v/impl/google/lib/discovery/ble/BleAdvertisementConverter.java b/lib/src/main/java/io/v/impl/google/lib/discovery/ble/BleAdvertisementConverter.java
index 8cbbf5e..a94339d 100644
--- a/lib/src/main/java/io/v/impl/google/lib/discovery/ble/BleAdvertisementConverter.java
+++ b/lib/src/main/java/io/v/impl/google/lib/discovery/ble/BleAdvertisementConverter.java
@@ -22,10 +22,18 @@
import io.v.x.ref.lib.discovery.plugins.ble.Constants;
/**
- * Created by bjornick on 10/7/15.
+ * BleAdvertisementConverter converts from io.v.impl.google.lib.discovery.Advertisement to
+ * the UUID gatt Services and vice-versa.
*/
public class BleAdvertisementConverter {
private static Charset utf8 = Charset.forName("UTF-8");
+
+ /**
+ * Converts from io.v.impl.google.lib.discovery.Advertisement to the ble representation.
+ * @param adv The Vanadium Advertisement
+ * @return Map of Characteristic UUIDs to their values.
+ * @throws IOException
+ */
public static Map<UUID, byte[]> vadvertismentToBleAttr(Advertisement adv)
throws IOException {
Map<UUID, byte[]> attr = new HashMap<>();
@@ -56,6 +64,13 @@
return attr;
}
+ /**
+ * Converts from Map of Characteristic UUIDs to their values to a
+ * io.v.impl.google.lib.discovery.Advertisement.
+ * @param attr The map of characteristic uuids to their values
+ * @return The Vanadium Advertisement based on characteristics.
+ * @throws IOException
+ */
public static Advertisement bleAttrToVAdvertisement(Map<UUID, byte[]> attr)
throws IOException {
Map<String, String> cleanAttrs = new HashMap<String, String>();
diff --git a/projects/discovery_sample/app/src/main/java/io/v/discoverysample/MainActivity.java b/projects/discovery_sample/app/src/main/java/io/v/discoverysample/MainActivity.java
index bf582e5..9e2546c 100644
--- a/projects/discovery_sample/app/src/main/java/io/v/discoverysample/MainActivity.java
+++ b/projects/discovery_sample/app/src/main/java/io/v/discoverysample/MainActivity.java
@@ -10,6 +10,8 @@
import android.widget.ListView;
import android.widget.Toast;
+import java.io.IOException;
+import java.util.ArrayList;
import java.util.List;
import io.v.android.libs.discovery.ble.BlePlugin;
@@ -17,34 +19,44 @@
import io.v.android.v23.V;
import io.v.android.v23.services.blessing.BlessingCreationException;
import io.v.android.v23.services.blessing.BlessingService;
-import io.v.impl.google.lib.discovery.ScanHandler;
import io.v.impl.google.lib.discovery.UUIDUtil;
import io.v.v23.context.CancelableVContext;
import io.v.v23.context.VContext;
+import io.v.v23.discovery.Attributes;
+import io.v.v23.discovery.Service;
import io.v.v23.security.Blessings;
import io.v.v23.verror.VException;
import io.v.v23.vom.VomUtil;
+import io.v.x.ref.lib.discovery.Advertisement;
+import io.v.x.ref.lib.discovery.EncryptionAlgorithm;
public class MainActivity extends Activity {
private static final int BLESSINGS_REQUEST = 1;
private Button chooseBlessingsButton;
- private Button startScanButton;
+ private Button scanButton;
+ private Button advertiseButton;
private Blessings blessings;
private BlePlugin plugin;
private VContext rootCtx;
private CancelableVContext scanCtx;
+ private CancelableVContext advCtx;
+
private boolean isScanning;
+ private boolean isAdvertising;
private ScanHandlerAdapter adapter;
+ private Advertisement advertisement;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
rootCtx = V.init(this);
isScanning = false;
+ isAdvertising = false;
setContentView(R.layout.activity_main);
chooseBlessingsButton = (Button)findViewById(R.id.blessingsButton);
@@ -55,15 +67,37 @@
}
});
- startScanButton = (Button)findViewById(R.id.scanForService);
- startScanButton.setEnabled(false);
- startScanButton.setOnClickListener(new View.OnClickListener() {
+ scanButton = (Button)findViewById(R.id.scanForService);
+ scanButton.setEnabled(false);
+ scanButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
flipScan();
}
});
+ advertiseButton = (Button) findViewById(R.id.advertiseButton);
+ advertiseButton.setEnabled(false);
+ advertiseButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View view) {
+ flipAdvertise();
+ }
+ });
+
+ byte[] instanceId = {0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 11, 12, 13, 14, 15};
+ Attributes attrs = new Attributes();
+ attrs.put("foo", "bar");
+ List<String> addrs = new ArrayList<>();
+ addrs.add("localhost:2000");
+ String interfaceName = "v.io/x/ref.Interface";
+ advertisement = new Advertisement(
+ new Service(instanceId, "Android instance",
+ interfaceName, attrs, addrs),
+ UUIDUtil.UUIDtUuid(UUIDUtil.UUIDForInterfaceName(interfaceName)),
+ new EncryptionAlgorithm(0),
+ null,
+ false);
adapter = new ScanHandlerAdapter(this);
ListView devices = (ListView) findViewById(R.id.list_view);
devices.setAdapter(adapter);
@@ -76,6 +110,28 @@
return true;
}
+ private void flipAdvertise() {
+ if (plugin == null) {
+ plugin = new BlePlugin(this);
+ }
+
+ if (!isAdvertising) {
+ isAdvertising = true;
+
+ advCtx = rootCtx.withCancel();
+ try {
+ plugin.addAdvertisement(advCtx, advertisement);
+ advertiseButton.setText("Stop Advertisement");
+ } catch (IOException e) {
+ advCtx.cancel();
+ isAdvertising = false;
+ }
+ } else {
+ isAdvertising = false;
+ advCtx.cancel();
+ advertiseButton.setText("Advertise");
+ }
+ }
private void flipScan() {
if (plugin == null) {
plugin = new BlePlugin(this);
@@ -86,9 +142,11 @@
UUIDUtil.UUIDForInterfaceName("v.io/x/ref.Interface"),
adapter);
isScanning = true;
+ scanButton.setText("Stop scanning");
} else {
isScanning = false;
scanCtx.cancel();
+ scanButton.setText("Start Scan");
}
}
@@ -112,7 +170,8 @@
Toast.LENGTH_SHORT).show();
// Enable the "start service" button.
- startScanButton.setEnabled(true);
+ scanButton.setEnabled(true);
+ advertiseButton.setEnabled(true);
} catch (BlessingCreationException e) {
String msg = "Couldn't create blessing: " + e.getMessage();
Toast.makeText(this, msg, Toast.LENGTH_LONG).show();
diff --git a/projects/discovery_sample/app/src/main/res/layout/activity_main.xml b/projects/discovery_sample/app/src/main/res/layout/activity_main.xml
index 8ec4d9a..67646aa 100644
--- a/projects/discovery_sample/app/src/main/res/layout/activity_main.xml
+++ b/projects/discovery_sample/app/src/main/res/layout/activity_main.xml
@@ -13,11 +13,18 @@
android:layout_alignParentTop="true"/>
<Button
android:id="@+id/scanForService"
- android:text="Scan for Service"
+ android:text="Start Scan"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_toRightOf="@id/blessingsButton"/>
+ <Button
+ android:id="@+id/advertiseButton"
+ android:text="Advertise"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentTop="true"
+ android:layout_toRightOf="@id/scanForService"/>
<ListView
android:id="@+id/list_view"
android:layout_width="fill_parent"