/* * Copyright 2015 Google Inc. All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.physical_web.physicalweb; import org.physical_web.collection.EddystoneBeacon; import org.physical_web.collection.UrlDevice; import org.physical_web.physicalweb.ble.ScanRecord; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothManager; import android.content.Context; import android.os.ParcelUuid; import android.os.Parcelable; import android.webkit.URLUtil; import java.util.List; class BleUrlDeviceDiscoverer extends UrlDeviceDiscoverer implements BluetoothAdapter.LeScanCallback { private static final String TAG = BleUrlDeviceDiscoverer.class.getSimpleName(); private static final ParcelUuid URIBEACON_SERVICE_UUID = ParcelUuid.fromString("0000FED8-0000-1000-8000-00805F9B34FB"); private static final ParcelUuid EDDYSTONE_URL_SERVICE_UUID = ParcelUuid.fromString("0000FEAA-0000-1000-8000-00805F9B34FB"); private BluetoothAdapter mBluetoothAdapter; private Parcelable[] mScanFilterUuids; private Context mContext; public BleUrlDeviceDiscoverer(Context context) { mContext = context; final BluetoothManager bluetoothManager = (BluetoothManager) mContext.getSystemService( Context.BLUETOOTH_SERVICE); mBluetoothAdapter = bluetoothManager.getAdapter(); mScanFilterUuids = new ParcelUuid[]{URIBEACON_SERVICE_UUID, EDDYSTONE_URL_SERVICE_UUID}; } private boolean leScanMatches(ScanRecord scanRecord) { if (mScanFilterUuids == null) { return true; } List services = scanRecord.getServiceUuids(); if (services != null) { for (Parcelable uuid : mScanFilterUuids) { if (services.contains(uuid)) { return true; } } } return false; } @Override public void onLeScan(final BluetoothDevice device, final int rssi, final byte[] scanBytes) { ScanRecord scanRecord = ScanRecord.parseFromBytes(scanBytes); if (!leScanMatches(scanRecord)) { return; } byte[] urlServiceData = scanRecord.getServiceData(EDDYSTONE_URL_SERVICE_UUID); byte[] uriServiceData = scanRecord.getServiceData(URIBEACON_SERVICE_UUID); if (Utils.isFatBeaconEnabled(mContext) && EddystoneBeacon.isFatBeacon(urlServiceData)) { String title = EddystoneBeacon.getFatBeaconTitle(urlServiceData); if (title.isEmpty()) { return; } UrlDevice urlDevice = createUrlDeviceBuilder(TAG + device.getAddress(), device.getAddress()) .setTitle(title) .setDescription(mContext.getString(R.string.fatbeacon_description)) .setDeviceType(Utils.FAT_BEACON_DEVICE_TYPE) .build(); reportUrlDevice(urlDevice); return; } EddystoneBeacon beacon = EddystoneBeacon.parseFromServiceData(urlServiceData, uriServiceData); if (beacon == null || !URLUtil.isNetworkUrl(beacon.getUrl())) { return; } UrlDevice urlDevice = createUrlDeviceBuilder(TAG + device.getAddress() + beacon.getUrl(), beacon.getUrl()) .setRssi(rssi) .setTxPower(beacon.getTxPowerLevel()) .setDeviceType(Utils.BLE_DEVICE_TYPE) .build(); Utils.updateRegion(urlDevice); reportUrlDevice(urlDevice); } @Override @SuppressWarnings("deprecation") public synchronized void startScanImpl() { mBluetoothAdapter.startLeScan(this); } @Override @SuppressWarnings("deprecation") public synchronized void stopScanImpl() { mBluetoothAdapter.stopLeScan(this); } }