| /* |
| * Copyright (C) 2013 The Android Open Source Project |
| * |
| * 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 com.android.cts.verifier.bluetooth; |
| |
| import android.app.Service; |
| import android.bluetooth.BluetoothAdapter; |
| import android.bluetooth.BluetoothManager; |
| import android.bluetooth.le.AdvertiseSettings; |
| import android.bluetooth.le.BluetoothLeScanner; |
| import android.bluetooth.le.ScanCallback; |
| import android.bluetooth.le.ScanFilter; |
| import android.bluetooth.le.ScanSettings; |
| import android.bluetooth.le.ScanRecord; |
| import android.bluetooth.le.ScanResult; |
| import android.content.Context; |
| import android.content.Intent; |
| import android.os.Handler; |
| import android.os.IBinder; |
| import android.os.ParcelUuid; |
| import android.util.Log; |
| import android.widget.Toast; |
| |
| import java.nio.ByteBuffer; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.UUID; |
| |
| public class BleScannerService extends Service { |
| |
| public static final boolean DEBUG = true; |
| public static final String TAG = "BleScannerService"; |
| |
| public static final int COMMAND_POWER_LEVEL = 1; |
| public static final int COMMAND_SCAN_WITH_FILTER = 2; |
| public static final int COMMAND_SCAN_WITHOUT_FILTER = 3; |
| |
| public static final String BLE_PRIVACY_NEW_MAC_RECEIVE = |
| "com.android.cts.verifier.bluetooth.BLE_PRIVACY_NEW_MAC_RECEIVE"; |
| public static final String BLE_MAC_ADDRESS = |
| "com.android.cts.verifier.bluetooth.BLE_MAC_ADDRESS"; |
| public static final String BLE_POWER_LEVEL = |
| "com.android.cts.verifier.bluetooth.BLE_POWER_LEVEL"; |
| public static final String BLE_SCAN_RESP = |
| "com.android.cts.verifier.bluetooth.BLE_SCAN_RESP"; |
| public static final String BLE_SCAN_RESULT = |
| "com.android.cts.verifier.bluetooth.BLE_SCAN_RESULT"; |
| |
| public static final String EXTRA_COMMAND = |
| "com.google.cts.verifier.bluetooth.EXTRA_COMMAND"; |
| public static final String EXTRA_MAC_ADDRESS = |
| "com.google.cts.verifier.bluetooth.EXTRA_MAC_ADDRESS"; |
| public static final String EXTRA_RSSI = |
| "com.google.cts.verifier.bluetooth.EXTRA_RSSI"; |
| public static final String EXTRA_POWER_LEVEL = |
| "com.google.cts.verifier.bluetooth.EXTRA_POWER_LEVEL"; |
| public static final String EXTRA_POWER_LEVEL_BIT = |
| "com.google.cts.verifier.bluetooth.EXTRA_POWER_LEVEL_BIT"; |
| public static final String EXTRA_UUID = |
| "com.google.cts.verifier.bluetooth.EXTRA_UUID"; |
| public static final String EXTRA_DATA = |
| "com.google.cts.verifier.bluetooth.EXTRA_DATA"; |
| |
| private static final byte MANUFACTURER_TEST_ID = (byte)0x07; |
| |
| private BluetoothManager mBluetoothManager; |
| private BluetoothAdapter mAdapter; |
| private BluetoothLeScanner mScanner; |
| private ScanCallback mCallback; |
| private Handler mHandler; |
| private String mOldMac; |
| |
| @Override |
| public void onCreate() { |
| super.onCreate(); |
| |
| mCallback = new BLEScanCallback(); |
| mHandler = new Handler(); |
| mOldMac = null; |
| |
| mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE); |
| mAdapter = mBluetoothManager.getAdapter(); |
| mScanner = mAdapter.getBluetoothLeScanner(); |
| } |
| |
| @Override |
| public int onStartCommand(Intent intent, int flags, int startId) { |
| if (mScanner != null) { |
| List<ScanFilter> filters = new ArrayList<ScanFilter>(); |
| ScanSettings.Builder settingBuilder = new ScanSettings.Builder(); |
| |
| int command = intent.getIntExtra(EXTRA_COMMAND, -1); |
| switch (command) { |
| case COMMAND_POWER_LEVEL: |
| filters.add(new ScanFilter.Builder() |
| .setManufacturerData(MANUFACTURER_TEST_ID, |
| new byte[]{MANUFACTURER_TEST_ID, 0}) |
| .setServiceData(new ParcelUuid(BleAdvertiserService.POWER_LEVEL_UUID), |
| BleAdvertiserService.POWER_LEVEL_DATA, |
| BleAdvertiserService.POWER_LEVEL_MASK) |
| .build()); |
| settingBuilder.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY); |
| break; |
| case COMMAND_SCAN_WITH_FILTER: |
| mScanner.stopScan(mCallback); |
| filters.add(new ScanFilter.Builder() |
| .setManufacturerData(MANUFACTURER_TEST_ID, |
| new byte[]{MANUFACTURER_TEST_ID, 0}) |
| .setServiceData(new ParcelUuid(BleAdvertiserService.SCANNABLE_UUID), |
| BleAdvertiserService.SCANNABLE_DATA) |
| .build()); |
| settingBuilder.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY); |
| break; |
| case COMMAND_SCAN_WITHOUT_FILTER: |
| mScanner.stopScan(mCallback); |
| settingBuilder.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY); |
| break; |
| } |
| mOldMac = null; |
| mScanner.startScan(filters, settingBuilder.build(), mCallback); |
| } |
| return START_NOT_STICKY; |
| } |
| |
| @Override |
| public IBinder onBind(Intent intent) { |
| return null; |
| } |
| |
| @Override |
| public void onDestroy() { |
| super.onDestroy(); |
| mScanner.stopScan(mCallback); |
| } |
| |
| private void showMessage(final String msg) { |
| mHandler.post(new Runnable() { |
| public void run() { |
| Toast.makeText(BleScannerService.this, msg, Toast.LENGTH_SHORT).show(); |
| } |
| }); |
| } |
| |
| private class BLEScanCallback extends ScanCallback { |
| @Override |
| public void onScanResult(int callBackType, ScanResult result) { |
| if (callBackType != ScanSettings.CALLBACK_TYPE_ALL_MATCHES) { |
| Log.e(TAG, "onScanResult fail. callBackType is not CALLBACK_TYPE_ALL_MATCHES"); |
| return; |
| } |
| |
| ScanRecord record = result.getScanRecord(); |
| String mac = result.getDevice().getAddress(); |
| Map<ParcelUuid, byte[]> serviceData = record.getServiceData(); |
| |
| if (serviceData.get(new ParcelUuid(BleAdvertiserService.POWER_LEVEL_UUID)) != null) { |
| byte[] data = |
| serviceData.get(new ParcelUuid(BleAdvertiserService.POWER_LEVEL_UUID)); |
| if (data.length == 3) { |
| Intent powerIntent = new Intent(BLE_POWER_LEVEL); |
| powerIntent.putExtra(EXTRA_MAC_ADDRESS, result.getDevice().getAddress()); |
| powerIntent.putExtra(EXTRA_POWER_LEVEL, record.getTxPowerLevel()); |
| powerIntent.putExtra(EXTRA_RSSI, new Integer(result.getRssi()).toString()); |
| powerIntent.putExtra(EXTRA_POWER_LEVEL_BIT, (int)data[2]); |
| sendBroadcast(powerIntent); |
| |
| // Check privacy mac. |
| if (data[2] == AdvertiseSettings.ADVERTISE_TX_POWER_HIGH) { |
| String newMac = result.getDevice().getAddress(); |
| if (mOldMac == null) { |
| mOldMac = newMac; |
| } else if (!mOldMac.equals(mac)) { |
| mOldMac = newMac; |
| Intent newIntent = new Intent(BLE_PRIVACY_NEW_MAC_RECEIVE); |
| sendBroadcast(newIntent); |
| } |
| } |
| } |
| } |
| |
| if (serviceData.get(new ParcelUuid(BleAdvertiserService.SCAN_RESP_UUID)) != null) { |
| Intent responseIntent = new Intent(BLE_SCAN_RESP); |
| sendBroadcast(responseIntent); |
| } |
| |
| byte[] data = null; |
| String uuid = ""; |
| if (serviceData.containsKey(new ParcelUuid(BleAdvertiserService.SCANNABLE_UUID))) { |
| uuid = BleAdvertiserService.SCANNABLE_UUID.toString(); |
| data = serviceData.get(new ParcelUuid(BleAdvertiserService.SCANNABLE_UUID)); |
| } |
| if (serviceData.containsKey(new ParcelUuid(BleAdvertiserService.UNSCANNABLE_UUID))) { |
| uuid = BleAdvertiserService.UNSCANNABLE_UUID.toString(); |
| data = serviceData.get(new ParcelUuid(BleAdvertiserService.UNSCANNABLE_UUID)); |
| } |
| if (uuid.length() > 0) { |
| Intent scanIntent = new Intent(BLE_SCAN_RESULT); |
| scanIntent.putExtra(EXTRA_UUID, uuid); |
| String dataStr = "{"; |
| for (byte x : data) { |
| dataStr = dataStr + " " + x; |
| } |
| dataStr = dataStr + "}"; |
| scanIntent.putExtra(EXTRA_DATA, dataStr); |
| sendBroadcast(scanIntent); |
| } |
| } |
| |
| public void onScanFailed(int errorCode) { |
| Log.e(TAG, "Scan fail. Error code: " + new Integer(errorCode).toString()); |
| } |
| |
| } |
| } |