1 /*
2  * Copyright (C) 2013 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.cts.verifier.bluetooth;
18 
19 import android.app.Service;
20 import android.bluetooth.BluetoothAdapter;
21 import android.bluetooth.BluetoothManager;
22 import android.bluetooth.le.AdvertiseSettings;
23 import android.bluetooth.le.BluetoothLeScanner;
24 import android.bluetooth.le.ScanCallback;
25 import android.bluetooth.le.ScanFilter;
26 import android.bluetooth.le.ScanRecord;
27 import android.bluetooth.le.ScanResult;
28 import android.bluetooth.le.ScanSettings;
29 import android.content.Context;
30 import android.content.Intent;
31 import android.os.Handler;
32 import android.os.IBinder;
33 import android.os.ParcelUuid;
34 import android.util.Log;
35 import android.widget.Toast;
36 
37 import java.util.ArrayList;
38 import java.util.List;
39 import java.util.Map;
40 
41 public class BleScannerService extends Service {
42 
43     public static final boolean DEBUG = true;
44     public static final String TAG = "BleScannerService";
45 
46     public static final int COMMAND_POWER_LEVEL = 1;
47     public static final int COMMAND_SCAN_WITH_FILTER = 2;
48     public static final int COMMAND_SCAN_WITHOUT_FILTER = 3;
49 
50     public static final String BLE_PRIVACY_NEW_MAC_RECEIVE =
51             "com.android.cts.verifier.bluetooth.BLE_PRIVACY_NEW_MAC_RECEIVE";
52     public static final String BLE_MAC_ADDRESS =
53             "com.android.cts.verifier.bluetooth.BLE_MAC_ADDRESS";
54     public static final String BLE_POWER_LEVEL =
55             "com.android.cts.verifier.bluetooth.BLE_POWER_LEVEL";
56     public static final String BLE_SCAN_RESP =
57             "com.android.cts.verifier.bluetooth.BLE_SCAN_RESP";
58     public static final String BLE_SCAN_RESULT =
59             "com.android.cts.verifier.bluetooth.BLE_SCAN_RESULT";
60 
61     public static final String EXTRA_COMMAND =
62             "com.google.cts.verifier.bluetooth.EXTRA_COMMAND";
63     public static final String EXTRA_MAC_ADDRESS =
64             "com.google.cts.verifier.bluetooth.EXTRA_MAC_ADDRESS";
65     public static final String EXTRA_RSSI =
66             "com.google.cts.verifier.bluetooth.EXTRA_RSSI";
67     public static final String EXTRA_POWER_LEVEL =
68             "com.google.cts.verifier.bluetooth.EXTRA_POWER_LEVEL";
69     public static final String EXTRA_POWER_LEVEL_BIT =
70             "com.google.cts.verifier.bluetooth.EXTRA_POWER_LEVEL_BIT";
71     public static final String EXTRA_UUID =
72             "com.google.cts.verifier.bluetooth.EXTRA_UUID";
73     public static final String EXTRA_DATA =
74             "com.google.cts.verifier.bluetooth.EXTRA_DATA";
75 
76     private static final byte MANUFACTURER_TEST_ID = (byte)0x07;
77 
78     private BluetoothManager mBluetoothManager;
79     private BluetoothAdapter mAdapter;
80     private BluetoothLeScanner mScanner;
81     private ScanCallback mCallback;
82     private Handler mHandler;
83     private String mOldMac;
84 
85     @Override
onCreate()86     public void onCreate() {
87         super.onCreate();
88 
89         mCallback = new BLEScanCallback();
90         mHandler = new Handler();
91         mOldMac = null;
92 
93         mBluetoothManager = (BluetoothManager) getSystemService(Context.BLUETOOTH_SERVICE);
94         mAdapter = mBluetoothManager.getAdapter();
95         mScanner = mAdapter.getBluetoothLeScanner();
96     }
97 
98     @Override
onStartCommand(Intent intent, int flags, int startId)99     public int onStartCommand(Intent intent, int flags, int startId) {
100         if (mScanner != null) {
101             List<ScanFilter> filters = new ArrayList<ScanFilter>();
102             ScanSettings.Builder settingBuilder = new ScanSettings.Builder();
103 
104             int command = intent.getIntExtra(EXTRA_COMMAND, -1);
105             switch (command) {
106                 case COMMAND_POWER_LEVEL:
107                     filters.add(new ScanFilter.Builder()
108                         .setManufacturerData(MANUFACTURER_TEST_ID,
109                             new byte[]{MANUFACTURER_TEST_ID, 0})
110                         .setServiceData(new ParcelUuid(BleAdvertiserService.POWER_LEVEL_UUID),
111                             BleAdvertiserService.POWER_LEVEL_DATA,
112                             BleAdvertiserService.POWER_LEVEL_MASK)
113                         .build());
114                     settingBuilder.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY);
115                     break;
116                 case COMMAND_SCAN_WITH_FILTER:
117                     mScanner.stopScan(mCallback);
118                     filters.add(new ScanFilter.Builder()
119                         .setManufacturerData(MANUFACTURER_TEST_ID,
120                             new byte[]{MANUFACTURER_TEST_ID, 0})
121                         .setServiceData(new ParcelUuid(BleAdvertiserService.SCANNABLE_UUID),
122                             BleAdvertiserService.SCANNABLE_DATA)
123                         .build());
124                     settingBuilder.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY);
125                     break;
126                 case COMMAND_SCAN_WITHOUT_FILTER:
127                     mScanner.stopScan(mCallback);
128                     settingBuilder.setScanMode(ScanSettings.SCAN_MODE_LOW_LATENCY);
129                     break;
130             }
131             mOldMac = null;
132             mScanner.startScan(filters, settingBuilder.build(), mCallback);
133         }
134         return START_NOT_STICKY;
135     }
136 
137     @Override
onBind(Intent intent)138     public IBinder onBind(Intent intent) {
139         return null;
140     }
141 
142     @Override
onDestroy()143     public void onDestroy() {
144         super.onDestroy();
145         if (mScanner != null) {
146             mScanner.stopScan(mCallback);
147         }
148     }
149 
showMessage(final String msg)150     private void showMessage(final String msg) {
151         mHandler.post(new Runnable() {
152             public void run() {
153                 Toast.makeText(BleScannerService.this, msg, Toast.LENGTH_SHORT).show();
154             }
155         });
156     }
157 
158     private class BLEScanCallback extends ScanCallback {
159         @Override
onScanResult(int callBackType, ScanResult result)160         public void onScanResult(int callBackType, ScanResult result) {
161             if (callBackType != ScanSettings.CALLBACK_TYPE_ALL_MATCHES) {
162                 Log.e(TAG, "onScanResult fail. callBackType is not CALLBACK_TYPE_ALL_MATCHES");
163                 return;
164             }
165 
166             ScanRecord record = result.getScanRecord();
167             String mac = result.getDevice().getAddress();
168             Map<ParcelUuid, byte[]> serviceData = record.getServiceData();
169 
170             if (serviceData.get(new ParcelUuid(BleAdvertiserService.POWER_LEVEL_UUID)) != null) {
171                 byte[] data =
172                         serviceData.get(new ParcelUuid(BleAdvertiserService.POWER_LEVEL_UUID));
173                 if (data.length == BleAdvertiserService.POWER_LEVEL_DATA.length) {
174                     Intent powerIntent = new Intent(BLE_POWER_LEVEL);
175                     powerIntent.putExtra(EXTRA_MAC_ADDRESS, result.getDevice().getAddress());
176                     powerIntent.putExtra(EXTRA_POWER_LEVEL, record.getTxPowerLevel());
177                     powerIntent.putExtra(EXTRA_RSSI, new Integer(result.getRssi()).toString());
178                     powerIntent.putExtra(EXTRA_POWER_LEVEL_BIT, (int)data[2]);
179                     sendBroadcast(powerIntent);
180 
181                     // Check privacy mac.
182                     if (data[2] == AdvertiseSettings.ADVERTISE_TX_POWER_HIGH) {
183                         String newMac = result.getDevice().getAddress();
184                         if (mOldMac == null) {
185                             mOldMac = newMac;
186                         } else if (!mOldMac.equals(mac)) {
187                             mOldMac = newMac;
188                             Intent newIntent = new Intent(BLE_PRIVACY_NEW_MAC_RECEIVE);
189                             sendBroadcast(newIntent);
190                         }
191                     }
192                 }
193             }
194 
195             if (serviceData.get(new ParcelUuid(BleAdvertiserService.SCAN_RESP_UUID)) != null) {
196                 Intent responseIntent = new Intent(BLE_SCAN_RESP);
197                 sendBroadcast(responseIntent);
198             }
199 
200             byte[] data = null;
201             String uuid = "";
202             if (serviceData.containsKey(new ParcelUuid(BleAdvertiserService.SCANNABLE_UUID))) {
203                 uuid = BleAdvertiserService.SCANNABLE_UUID.toString();
204                 data = serviceData.get(new ParcelUuid(BleAdvertiserService.SCANNABLE_UUID));
205             }
206             if (serviceData.containsKey(new ParcelUuid(BleAdvertiserService.UNSCANNABLE_UUID))) {
207                 uuid = BleAdvertiserService.UNSCANNABLE_UUID.toString();
208                 data = serviceData.get(new ParcelUuid(BleAdvertiserService.UNSCANNABLE_UUID));
209             }
210             if (uuid.length() > 0) {
211                 Intent scanIntent = new Intent(BLE_SCAN_RESULT);
212                 scanIntent.putExtra(EXTRA_UUID, uuid);
213                 String dataStr = "{";
214                 for (byte x : data) {
215                     dataStr = dataStr + " " + x;
216                 }
217                 dataStr = dataStr + "}";
218                 scanIntent.putExtra(EXTRA_DATA, dataStr);
219                 sendBroadcast(scanIntent);
220             }
221         }
222 
onScanFailed(int errorCode)223         public void onScanFailed(int errorCode) {
224             Log.e(TAG, "Scan fail. Error code: " + new Integer(errorCode).toString());
225         }
226 
227     }
228 }
229