BluetoothLeScanFacade.java revision 233c7a0cab1f3461d3f29b086894dd3358a52014
1/*
2 * Copyright (C) 2016 Google Inc.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * 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, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16
17package com.googlecode.android_scripting.facade.bluetooth;
18
19import java.util.ArrayList;
20import java.util.HashMap;
21import java.util.List;
22import java.util.UUID;
23import java.util.concurrent.Callable;
24
25import android.app.Service;
26import android.bluetooth.BluetoothAdapter;
27import android.bluetooth.BluetoothDevice;
28import android.bluetooth.le.BluetoothLeScanner;
29import android.bluetooth.le.ScanCallback;
30import android.bluetooth.le.ScanFilter;
31import android.bluetooth.BluetoothAdapter.LeScanCallback;
32import android.bluetooth.le.ScanFilter.Builder;
33import android.bluetooth.le.ScanResult;
34import android.bluetooth.le.ScanSettings;
35import android.os.Bundle;
36import android.os.ParcelUuid;
37
38import com.googlecode.android_scripting.ConvertUtils;
39import com.googlecode.android_scripting.Log;
40import com.googlecode.android_scripting.MainThread;
41import com.googlecode.android_scripting.facade.EventFacade;
42import com.googlecode.android_scripting.facade.FacadeManager;
43import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
44import com.googlecode.android_scripting.rpc.Rpc;
45import com.googlecode.android_scripting.rpc.RpcOptional;
46import com.googlecode.android_scripting.rpc.RpcParameter;
47
48/**
49 * BluetoothLe Scan functions.
50 */
51
52public class BluetoothLeScanFacade extends RpcReceiver {
53
54    private final EventFacade mEventFacade;
55
56    private BluetoothAdapter mBluetoothAdapter;
57    private static int ScanCallbackCount;
58    private static int FilterListCount;
59    private static int LeScanCallbackCount;
60    private static int ScanSettingsCount;
61    private final Service mService;
62    private final BluetoothLeScanner mScanner;
63    private android.bluetooth.le.ScanSettings.Builder mScanSettingsBuilder;
64    private Builder mScanFilterBuilder;
65    private final HashMap<Integer, myScanCallback> mScanCallbackList;
66    private final HashMap<Integer, myLeScanCallback> mLeScanCallbackList;
67    private final HashMap<Integer, ArrayList<ScanFilter>> mScanFilterList;
68    private final HashMap<Integer, ScanSettings> mScanSettingsList;
69
70    public BluetoothLeScanFacade(FacadeManager manager) {
71        super(manager);
72        mService = manager.getService();
73        mBluetoothAdapter = MainThread.run(mService,
74                new Callable<BluetoothAdapter>() {
75                    @Override
76                    public BluetoothAdapter call() throws Exception {
77                        return BluetoothAdapter.getDefaultAdapter();
78                    }
79                });
80        mScanner = mBluetoothAdapter.getBluetoothLeScanner();
81        mEventFacade = manager.getReceiver(EventFacade.class);
82        mScanFilterList = new HashMap<Integer, ArrayList<ScanFilter>>();
83        mLeScanCallbackList = new HashMap<Integer, myLeScanCallback>();
84        mScanSettingsList = new HashMap<Integer, ScanSettings>();
85        mScanCallbackList = new HashMap<Integer, myScanCallback>();
86        mScanFilterBuilder = new Builder();
87        mScanSettingsBuilder = new android.bluetooth.le.ScanSettings.Builder();
88    }
89
90    /**
91     * Constructs a myScanCallback obj and returns its index
92     *
93     * @return Integer myScanCallback.index
94     */
95    @Rpc(description = "Generate a new myScanCallback Object")
96    public Integer bleGenScanCallback() {
97        ScanCallbackCount += 1;
98        int index = ScanCallbackCount;
99        myScanCallback mScan = new myScanCallback(index);
100        mScanCallbackList.put(mScan.index, mScan);
101        return mScan.index;
102    }
103
104    /**
105     * Constructs a myLeScanCallback obj and returns its index
106     *
107     * @return Integer myScanCallback.index
108     */
109    @Rpc(description = "Generate a new myScanCallback Object")
110    public Integer bleGenLeScanCallback() {
111        LeScanCallbackCount += 1;
112        int index = LeScanCallbackCount;
113        myLeScanCallback mScan = new myLeScanCallback(index);
114        mLeScanCallbackList.put(mScan.index, mScan);
115        return mScan.index;
116    }
117
118    /**
119     * Constructs a new filter list array and returns its index
120     *
121     * @return Integer index
122     */
123    @Rpc(description = "Generate a new Filter list")
124    public Integer bleGenFilterList() {
125        FilterListCount += 1;
126        int index = FilterListCount;
127        mScanFilterList.put(index, new ArrayList<ScanFilter>());
128        return index;
129    }
130
131    /**
132     * Constructs a new filter list array and returns its index
133     *
134     * @return Integer index
135     */
136    @Rpc(description = "Generate a new Filter list")
137    public Integer bleBuildScanFilter(
138            @RpcParameter(name = "filterIndex")
139            Integer filterIndex
140            ) {
141        mScanFilterList.get(filterIndex).add(mScanFilterBuilder.build());
142        mScanFilterBuilder = new Builder();
143        return mScanFilterList.get(filterIndex).size()-1;
144    }
145
146    /**
147     * Constructs a new scan setting and returns its index
148     *
149     * @return Integer index
150     */
151    @Rpc(description = "Generate a new scan settings Object")
152    public Integer bleBuildScanSetting() {
153        ScanSettingsCount += 1;
154        int index = ScanSettingsCount;
155        mScanSettingsList.put(index, mScanSettingsBuilder.build());
156        mScanSettingsBuilder = new android.bluetooth.le.ScanSettings.Builder();
157        return index;
158    }
159
160    /**
161     * Stops a ble scan
162     *
163     * @param index the id of the myScan whose ScanCallback to stop
164     * @throws Exception
165     */
166    @Rpc(description = "Stops an ongoing ble advertisement scan")
167    public void bleStopBleScan(
168            @RpcParameter(name = "index")
169            Integer index) throws Exception {
170        Log.d("bluetooth_le_scan mScanCallback " + index);
171        if (mScanCallbackList.get(index) != null) {
172            myScanCallback mScanCallback = mScanCallbackList.get(index);
173            mScanner.stopScan(mScanCallback);
174        } else {
175            throw new Exception("Invalid index input:" + Integer.toString(index));
176        }
177    }
178
179    /**
180     * Stops a classic ble scan
181     *
182     * @param index the id of the myScan whose LeScanCallback to stop
183     * @throws Exception
184     */
185    @Rpc(description = "Stops an ongoing classic ble scan")
186    public void bleStopClassicBleScan(
187            @RpcParameter(name = "index")
188            Integer index) throws Exception {
189        Log.d("bluetooth_le_scan mLeScanCallback " + index);
190        if (mLeScanCallbackList.get(index) != null) {
191            myLeScanCallback mLeScanCallback = mLeScanCallbackList.get(index);
192            mBluetoothAdapter.stopLeScan(mLeScanCallback);
193        } else {
194            throw new Exception("Invalid index input:" + Integer.toString(index));
195        }
196    }
197
198    /**
199     * Starts a ble scan
200     *
201     * @param index the id of the myScan whose ScanCallback to start
202     * @throws Exception
203     */
204    @Rpc(description = "Starts a ble advertisement scan")
205    public void bleStartBleScan(
206            @RpcParameter(name = "filterListIndex")
207            Integer filterListIndex,
208            @RpcParameter(name = "scanSettingsIndex")
209            Integer scanSettingsIndex,
210            @RpcParameter(name = "callbackIndex")
211            Integer callbackIndex
212            ) throws Exception {
213        Log.d("bluetooth_le_scan starting a background scan");
214        ArrayList<ScanFilter> mScanFilters = new ArrayList<ScanFilter>();
215        mScanFilters.add(new ScanFilter.Builder().build());
216        ScanSettings mScanSettings = new ScanSettings.Builder().build();
217        if (mScanFilterList.get(filterListIndex) != null) {
218            mScanFilters = mScanFilterList.get(filterListIndex);
219        } else {
220            throw new Exception("Invalid filterListIndex input:"
221                    + Integer.toString(filterListIndex));
222        }
223        if (mScanSettingsList.get(scanSettingsIndex) != null) {
224            mScanSettings = mScanSettingsList.get(scanSettingsIndex);
225        } else if (!mScanSettingsList.isEmpty()) {
226            throw new Exception("Invalid scanSettingsIndex input:"
227                    + Integer.toString(scanSettingsIndex));
228        }
229        if (mScanCallbackList.get(callbackIndex) != null) {
230            mScanner.startScan(mScanFilters, mScanSettings, mScanCallbackList.get(callbackIndex));
231        } else {
232            throw new Exception("Invalid filterListIndex input:"
233                    + Integer.toString(filterListIndex));
234        }
235    }
236
237    /**
238     * Starts a classic ble scan
239     *
240     * @param index the id of the myScan whose ScanCallback to start
241     * @throws Exception
242     */
243    @Rpc(description = "Starts a classic ble advertisement scan")
244    public boolean bleStartClassicBleScan(
245            @RpcParameter(name = "leCallbackIndex")
246            Integer leCallbackIndex
247            ) throws Exception {
248        Log.d("bluetooth_le_scan starting a background scan");
249        boolean result = false;
250        if (mLeScanCallbackList.get(leCallbackIndex) != null) {
251            result = mBluetoothAdapter.startLeScan(mLeScanCallbackList.get(leCallbackIndex));
252        } else {
253            throw new Exception("Invalid leCallbackIndex input:"
254                    + Integer.toString(leCallbackIndex));
255        }
256        return result;
257    }
258
259    /**
260     * Starts a classic ble scan with service Uuids
261     *
262     * @param index the id of the myScan whose ScanCallback to start
263     * @throws Exception
264     */
265    @Rpc(description = "Starts a classic ble advertisement scan with service Uuids")
266    public boolean bleStartClassicBleScanWithServiceUuids(
267            @RpcParameter(name = "leCallbackIndex")
268            Integer leCallbackIndex,
269            @RpcParameter(name = "serviceUuids")
270            String[] serviceUuidList
271            ) throws Exception {
272        Log.d("bluetooth_le_scan starting a background scan");
273        UUID[] serviceUuids = new UUID[serviceUuidList.length];
274        for (int i = 0; i < serviceUuidList.length; i++) {
275            serviceUuids[i] = UUID.fromString(serviceUuidList[i]);
276        }
277        boolean result = false;
278        if (mLeScanCallbackList.get(leCallbackIndex) != null) {
279            result = mBluetoothAdapter.startLeScan(serviceUuids,
280                    mLeScanCallbackList.get(leCallbackIndex));
281        } else {
282            throw new Exception("Invalid leCallbackIndex input:"
283                    + Integer.toString(leCallbackIndex));
284        }
285        return result;
286    }
287
288    /**
289     * Trigger onBatchScanResults
290     *
291     * @throws Exception
292     */
293    @Rpc(description = "Gets the results of the ble ScanCallback")
294    public void bleFlushPendingScanResults(
295            @RpcParameter(name = "callbackIndex")
296            Integer callbackIndex
297            ) throws Exception {
298        if (mScanCallbackList.get(callbackIndex) != null) {
299            mBluetoothAdapter
300                    .getBluetoothLeScanner().flushPendingScanResults(
301                            mScanCallbackList.get(callbackIndex));
302        } else {
303            throw new Exception("Invalid callbackIndex input:"
304                    + Integer.toString(callbackIndex));
305        }
306    }
307
308    /**
309     * Set scanSettings for ble scan. Note: You have to set all variables at once.
310     *
311     * @param callbackType Bluetooth LE scan callback type
312     * @param reportDelaySeconds Time of delay for reporting the scan result
313     * @param scanMode Bluetooth LE scan mode.
314     * @param scanResultType Bluetooth LE scan result type
315     * @throws Exception
316     */
317
318    /**
319     * Set the scan setting's callback type
320     * @param callbackType Bluetooth LE scan callback type
321     */
322    @Rpc(description = "Set the scan setting's callback type")
323    public void bleSetScanSettingsCallbackType(
324            @RpcParameter(name = "callbackType")
325            Integer callbackType) {
326        mScanSettingsBuilder.setCallbackType(callbackType);
327    }
328
329    /**
330     * Set the scan setting's report delay millis
331     * @param reportDelayMillis Time of delay for reporting the scan result
332     */
333    @Rpc(description = "Set the scan setting's report delay millis")
334    public void bleSetScanSettingsReportDelayMillis(
335            @RpcParameter(name = "reportDelayMillis")
336            Long reportDelayMillis) {
337        mScanSettingsBuilder.setReportDelay(reportDelayMillis);
338    }
339
340    /**
341     * Set the scan setting's scan mode
342     * @param scanMode Bluetooth LE scan mode.
343     */
344    @Rpc(description = "Set the scan setting's scan mode")
345    public void bleSetScanSettingsScanMode(
346            @RpcParameter(name = "scanMode")
347            Integer scanMode) {
348        mScanSettingsBuilder.setScanMode(scanMode);
349    }
350
351    /**
352     * Set the scan setting's scan result type
353     * @param scanResultType Bluetooth LE scan result type
354     */
355    @Rpc(description = "Set the scan setting's scan result type")
356    public void bleSetScanSettingsResultType(
357            @RpcParameter(name = "scanResultType")
358            Integer scanResultType) {
359        mScanSettingsBuilder.setScanResultType(scanResultType);
360    }
361    /**
362     * Get ScanSetting's callback type
363     *
364     * @param index the ScanSetting object to use
365     * @return the ScanSetting's callback type
366     * @throws Exception
367     */
368    @Rpc(description = "Get ScanSetting's callback type")
369    public Integer bleGetScanSettingsCallbackType(
370            @RpcParameter(name = "index")
371            Integer index
372            ) throws Exception {
373        if (mScanSettingsList.get(index) != null) {
374            ScanSettings mScanSettings = mScanSettingsList.get(index);
375            return mScanSettings.getCallbackType();
376        } else {
377            throw new Exception("Invalid index input:" + Integer.toString(index));
378        }
379    }
380
381    /**
382     * Get ScanSetting's report delay in milli seconds
383     *
384     * @param index the ScanSetting object to useSystemClock
385     * @return the ScanSetting's report delay in milliseconds
386     * @throws Exception
387     */
388    @Rpc(description = "Get ScanSetting's report delay milliseconds")
389    public Long bleGetScanSettingsReportDelayMillis(
390            @RpcParameter(name = "index")
391            Integer index) throws Exception {
392        if (mScanSettingsList.get(index) != null) {
393            ScanSettings mScanSettings = mScanSettingsList.get(index);
394            return mScanSettings.getReportDelayMillis();
395        } else {
396            throw new Exception("Invalid index input:" + Integer.toString(index));
397        }
398    }
399
400    /**
401     * Get ScanSetting's scan mode
402     *
403     * @param index the ScanSetting object to use
404     * @return the ScanSetting's scan mode
405     * @throws Exception
406     */
407    @Rpc(description = "Get ScanSetting's scan mode")
408    public Integer bleGetScanSettingsScanMode(
409            @RpcParameter(name = "index")
410            Integer index) throws Exception {
411        if (mScanSettingsList.get(index) != null) {
412            ScanSettings mScanSettings = mScanSettingsList.get(index);
413            return mScanSettings.getScanMode();
414        } else {
415            throw new Exception("Invalid index input:" + Integer.toString(index));
416        }
417    }
418
419    /**
420     * Get ScanSetting's scan result type
421     *
422     * @param index the ScanSetting object to use
423     * @return the ScanSetting's scan result type
424     * @throws Exception
425     */
426    @Rpc(description = "Get ScanSetting's scan result type")
427    public Integer bleGetScanSettingsScanResultType(
428            @RpcParameter(name = "index")
429            Integer index) throws Exception {
430        if (mScanSettingsList.get(index) != null) {
431            ScanSettings mScanSettings = mScanSettingsList.get(index);
432            return mScanSettings.getScanResultType();
433        } else {
434            throw new Exception("Invalid index input:" + Integer.toString(index));
435        }
436    }
437
438    /**
439     * Get ScanFilter's Manufacturer Id
440     *
441     * @param index the ScanFilter object to use
442     * @return the ScanFilter's manufacturer id
443     * @throws Exception
444     */
445    @Rpc(description = "Get ScanFilter's Manufacturer Id")
446    public Integer bleGetScanFilterManufacturerId(
447            @RpcParameter(name = "index")
448            Integer index,
449            @RpcParameter(name = "filterIndex")
450            Integer filterIndex)
451            throws Exception {
452        if (mScanFilterList.get(index) != null) {
453            if (mScanFilterList.get(index).get(filterIndex) != null) {
454                return mScanFilterList.get(index)
455                        .get(filterIndex).getManufacturerId();
456            } else {
457                throw new Exception("Invalid filterIndex input:" + Integer.toString(filterIndex));
458            }
459        } else {
460            throw new Exception("Invalid index input:" + Integer.toString(index));
461        }
462    }
463
464    /**
465     * Get ScanFilter's device address
466     *
467     * @param index the ScanFilter object to use
468     * @return the ScanFilter's device address
469     * @throws Exception
470     */
471    @Rpc(description = "Get ScanFilter's device address")
472    public String bleGetScanFilterDeviceAddress(
473            @RpcParameter(name = "index")
474            Integer index,
475            @RpcParameter(name = "filterIndex")
476            Integer filterIndex)
477            throws Exception {
478        if (mScanFilterList.get(index) != null) {
479            if (mScanFilterList.get(index).get(filterIndex) != null) {
480                return mScanFilterList.get(index).get(filterIndex).getDeviceAddress();
481            } else {
482                throw new Exception("Invalid filterIndex input:" + Integer.toString(filterIndex));
483            }
484        } else {
485            throw new Exception("Invalid index input:" + Integer.toString(index));
486        }
487    }
488
489    /**
490     * Get ScanFilter's device name
491     *
492     * @param index the ScanFilter object to use
493     * @return the ScanFilter's device name
494     * @throws Exception
495     */
496    @Rpc(description = "Get ScanFilter's device name")
497    public String bleGetScanFilterDeviceName(
498            @RpcParameter(name = "index")
499            Integer index,
500            @RpcParameter(name = "filterIndex")
501            Integer filterIndex)
502            throws Exception {
503        if (mScanFilterList.get(index) != null) {
504            if (mScanFilterList.get(index).get(filterIndex) != null) {
505                return mScanFilterList.get(index).get(filterIndex).getDeviceName();
506            } else {
507                throw new Exception("Invalid filterIndex input:" + Integer.toString(filterIndex));
508            }
509        } else {
510            throw new Exception("Invalid index input:" + Integer.toString(index));
511        }
512    }
513
514    /**
515     * Get ScanFilter's manufacturer data
516     *
517     * @param index the ScanFilter object to use
518     * @return the ScanFilter's manufacturer data
519     * @throws Exception
520     */
521    @Rpc(description = "Get ScanFilter's manufacturer data")
522    public String bleGetScanFilterManufacturerData(
523            @RpcParameter(name = "index")
524            Integer index,
525            @RpcParameter(name = "filterIndex")
526            Integer filterIndex)
527            throws Exception {
528        if (mScanFilterList.get(index) != null) {
529            if (mScanFilterList.get(index).get(filterIndex) != null) {
530                return ConvertUtils.convertByteArrayToString(mScanFilterList.get(index)
531                        .get(filterIndex).getManufacturerData());
532            } else {
533                throw new Exception("Invalid filterIndex input:" + Integer.toString(filterIndex));
534            }
535        } else {
536            throw new Exception("Invalid index input:" + Integer.toString(index));
537        }
538    }
539
540    /**
541     * Get ScanFilter's manufacturer data mask
542     *
543     * @param index the ScanFilter object to use
544     * @return the ScanFilter's manufacturer data mask
545     * @throws Exception
546     */
547    @Rpc(description = "Get ScanFilter's manufacturer data mask")
548    public String bleGetScanFilterManufacturerDataMask(
549            @RpcParameter(name = "index")
550            Integer index,
551            @RpcParameter(name = "filterIndex")
552            Integer filterIndex)
553            throws Exception {
554        if (mScanFilterList.get(index) != null) {
555            if (mScanFilterList.get(index).get(filterIndex) != null) {
556                return ConvertUtils.convertByteArrayToString(mScanFilterList.get(index)
557                        .get(filterIndex).getManufacturerDataMask());
558            } else {
559                throw new Exception("Invalid filterIndex input:" + Integer.toString(filterIndex));
560            }
561        } else {
562            throw new Exception("Invalid index input:" + Integer.toString(index));
563        }
564    }
565
566    /**
567     * Get ScanFilter's service data
568     *
569     * @param index the ScanFilter object to use
570     * @return the ScanFilter's service data
571     * @throws Exception
572     */
573    @Rpc(description = "Get ScanFilter's service data")
574    public String bleGetScanFilterServiceData(
575            @RpcParameter(name = "index")
576            Integer index,
577            @RpcParameter(name = "filterIndex")
578            Integer filterIndex)
579            throws Exception {
580        if (mScanFilterList.get(index) != null) {
581            if (mScanFilterList.get(index).get(filterIndex) != null) {
582                return ConvertUtils.convertByteArrayToString(mScanFilterList
583                        .get(index).get(filterIndex).getServiceData());
584            } else {
585                throw new Exception("Invalid filterIndex input:" + Integer.toString(filterIndex));
586            }
587        } else {
588            throw new Exception("Invalid index input:" + Integer.toString(index));
589        }
590    }
591
592    /**
593     * Get ScanFilter's service data mask
594     *
595     * @param index the ScanFilter object to use
596     * @return the ScanFilter's service data mask
597     * @throws Exception
598     */
599    @Rpc(description = "Get ScanFilter's service data mask")
600    public String bleGetScanFilterServiceDataMask(
601            @RpcParameter(name = "index")
602            Integer index,
603            @RpcParameter(name = "filterIndex")
604            Integer filterIndex)
605            throws Exception {
606        if (mScanFilterList.get(index) != null) {
607            if (mScanFilterList.get(index).get(filterIndex) != null) {
608                return ConvertUtils.convertByteArrayToString(mScanFilterList.get(index)
609                        .get(filterIndex).getServiceDataMask());
610            } else {
611                throw new Exception("Invalid filterIndex input:" + Integer.toString(filterIndex));
612            }
613        } else {
614            throw new Exception("Invalid index input:" + Integer.toString(index));
615        }
616    }
617
618    /**
619     * Get ScanFilter's service uuid
620     *
621     * @param index the ScanFilter object to use
622     * @return the ScanFilter's service uuid
623     * @throws Exception
624     */
625    @Rpc(description = "Get ScanFilter's service uuid")
626    public String bleGetScanFilterServiceUuid(
627            @RpcParameter(name = "index")
628            Integer index,
629            @RpcParameter(name = "filterIndex")
630            Integer filterIndex)
631            throws Exception {
632        if (mScanFilterList.get(index) != null) {
633            if (mScanFilterList.get(index).get(filterIndex) != null) {
634                if (mScanFilterList.get(index).get(filterIndex).getServiceUuid() != null) {
635                    return mScanFilterList.get(index).get(filterIndex).getServiceUuid().toString();
636                } else {
637                    throw new Exception("No Service Uuid set for filter:"
638                            + Integer.toString(filterIndex));
639                }
640            } else {
641                throw new Exception("Invalid filterIndex input:" + Integer.toString(filterIndex));
642            }
643        } else {
644            throw new Exception("Invalid index input:" + Integer.toString(index));
645        }
646    }
647
648    /**
649     * Get ScanFilter's service uuid mask
650     *
651     * @param index the ScanFilter object to use
652     * @return the ScanFilter's service uuid mask
653     * @throws Exception
654     */
655    @Rpc(description = "Get ScanFilter's service uuid mask")
656    public String bleGetScanFilterServiceUuidMask(
657            @RpcParameter(name = "index")
658            Integer index,
659            @RpcParameter(name = "filterIndex")
660            Integer filterIndex)
661            throws Exception {
662        if (mScanFilterList.get(index) != null) {
663            if (mScanFilterList.get(index).get(filterIndex) != null) {
664                if (mScanFilterList.get(index).get(filterIndex).getServiceUuidMask() != null) {
665                    return mScanFilterList.get(index).get(filterIndex).getServiceUuidMask()
666                            .toString();
667                } else {
668                    throw new Exception("No Service Uuid Mask set for filter:"
669                            + Integer.toString(filterIndex));
670                }
671            } else {
672                throw new Exception("Invalid filterIndex input:" + Integer.toString(filterIndex));
673            }
674        } else {
675            throw new Exception("Invalid index input:" + Integer.toString(index));
676        }
677    }
678
679    /**
680     * Add filter "macAddress" to existing ScanFilter
681     *
682     * @param macAddress the macAddress to filter against
683     * @throws Exception
684     */
685    @Rpc(description = "Add filter \"macAddress\" to existing ScanFilter")
686    public void bleSetScanFilterDeviceAddress(
687            @RpcParameter(name = "macAddress")
688            String macAddress
689            ) {
690            mScanFilterBuilder.setDeviceAddress(macAddress);
691    }
692
693    /**
694     * Add filter "manufacturereDataId and/or manufacturerData" to existing ScanFilter
695     *
696     * @param manufacturerDataId the manufacturer data id to filter against
697     * @param manufacturerDataMask the manufacturere data mask to filter against
698     * @throws Exception
699     */
700    @Rpc(description = "Add filter \"manufacturereDataId and/or manufacturerData\" to existing ScanFilter")
701    public void bleSetScanFilterManufacturerData(
702            @RpcParameter(name = "manufacturerDataId")
703            Integer manufacturerDataId,
704            @RpcParameter(name = "manufacturerData")
705            String manufacturerData,
706            @RpcParameter(name = "manufacturerDataMask")
707            @RpcOptional
708            String manufacturerDataMask
709            ){
710        if (manufacturerDataMask != null) {
711            mScanFilterBuilder.setManufacturerData(manufacturerDataId,
712                    ConvertUtils.convertStringToByteArray(manufacturerData),
713                    ConvertUtils.convertStringToByteArray(manufacturerDataMask));
714        } else {
715            mScanFilterBuilder.setManufacturerData(manufacturerDataId,
716                    ConvertUtils.convertStringToByteArray(manufacturerData));
717        }
718    }
719
720    /**
721     * Add filter "serviceData and serviceDataMask" to existing ScanFilter
722     *
723     * @param serviceData the service data to filter against
724     * @param serviceDataMask the servie data mask to filter against
725     * @throws Exception
726     */
727    @Rpc(description = "Add filter \"serviceData and serviceDataMask\" to existing ScanFilter ")
728    public void bleSetScanFilterServiceData(
729            @RpcParameter(name = "serviceUuid")
730            String serviceUuid,
731            @RpcParameter(name = "serviceData")
732            String serviceData,
733            @RpcParameter(name = "serviceDataMask")
734            @RpcOptional
735            String serviceDataMask
736            ) {
737        if (serviceDataMask != null) {
738            mScanFilterBuilder
739                    .setServiceData(
740                            ParcelUuid.fromString(serviceUuid),
741                            ConvertUtils.convertStringToByteArray(serviceData),
742                            ConvertUtils.convertStringToByteArray(
743                                serviceDataMask));
744        } else {
745            mScanFilterBuilder.setServiceData(ParcelUuid.fromString(serviceUuid),
746                    ConvertUtils.convertStringToByteArray(serviceData));
747        }
748    }
749
750    /**
751     * Add filter "serviceUuid and/or serviceMask" to existing ScanFilter
752     *
753     * @param serviceUuid the service uuid to filter against
754     * @param serviceMask the service mask to filter against
755     * @throws Exception
756     */
757    @Rpc(description = "Add filter \"serviceUuid and/or serviceMask\" to existing ScanFilter")
758    public void bleSetScanFilterServiceUuid(
759            @RpcParameter(name = "serviceUuid")
760            String serviceUuid,
761            @RpcParameter(name = "serviceMask")
762            @RpcOptional
763            String serviceMask
764            ) {
765        if (serviceMask != null) {
766            mScanFilterBuilder
767                    .setServiceUuid(ParcelUuid.fromString(serviceUuid),
768                            ParcelUuid.fromString(serviceMask));
769        } else {
770            mScanFilterBuilder.setServiceUuid(ParcelUuid.fromString(serviceUuid));
771        }
772    }
773
774    /**
775     * Add filter "device name" to existing ScanFilter
776     *
777     * @param name the device name to filter against
778     * @throws Exception
779     */
780    @Rpc(description = "Sets the scan filter's device name")
781    public void bleSetScanFilterDeviceName(
782            @RpcParameter(name = "name")
783            String name
784            ) {
785            mScanFilterBuilder.setDeviceName(name);
786    }
787
788    @Rpc(description = "Set the scan setting's match mode")
789    public void bleSetScanSettingsMatchMode(
790            @RpcParameter(name = "mode") Integer mode) {
791        mScanSettingsBuilder.setMatchMode(mode);
792    }
793
794    @Rpc(description = "Get the scan setting's match mode")
795    public int bleGetScanSettingsMatchMode(
796            @RpcParameter(name = "scanSettingsIndex") Integer scanSettingsIndex
797            ) {
798        return mScanSettingsList.get(scanSettingsIndex).getMatchMode();
799    }
800
801    @Rpc(description = "Set the scan setting's number of matches")
802    public void bleSetScanSettingsNumOfMatches(
803            @RpcParameter(name = "matches") Integer matches) {
804        mScanSettingsBuilder.setNumOfMatches(matches);
805    }
806
807    @Rpc(description = "Get the scan setting's number of matches")
808    public int bleGetScanSettingsNumberOfMatches(
809            @RpcParameter(name = "scanSettingsIndex")
810            Integer scanSettingsIndex) {
811        return  mScanSettingsList.get(scanSettingsIndex).getNumOfMatches();
812    }
813
814    private class myScanCallback extends ScanCallback {
815        public Integer index;
816        String mEventType;
817        private final Bundle mResults;
818
819        public myScanCallback(Integer idx) {
820            index = idx;
821            mEventType = "BleScan";
822            mResults = new Bundle();
823        }
824
825        @Override
826        public void onScanFailed(int errorCode) {
827            String errorString = "UNKNOWN_ERROR_CODE";
828            if (errorCode == ScanCallback.SCAN_FAILED_ALREADY_STARTED) {
829                errorString = "SCAN_FAILED_ALREADY_STARTED";
830            } else if (errorCode == ScanCallback.SCAN_FAILED_APPLICATION_REGISTRATION_FAILED) {
831                errorString = "SCAN_FAILED_APPLICATION_REGISTRATION_FAILED";
832            } else if (errorCode == ScanCallback.SCAN_FAILED_FEATURE_UNSUPPORTED) {
833                errorString = "SCAN_FAILED_FEATURE_UNSUPPORTED";
834            } else if (errorCode == ScanCallback.SCAN_FAILED_INTERNAL_ERROR) {
835                errorString = "SCAN_FAILED_INTERNAL_ERROR";
836            }
837            Log.d("bluetooth_le_scan change onScanFailed " + mEventType + " " + index + " error "
838                    + errorString);
839            mResults.putInt("ID", index);
840            mResults.putString("Type", "onScanFailed");
841            mResults.putInt("ErrorCode", errorCode);
842            mResults.putString("Error", errorString);
843            mEventFacade.postEvent(mEventType + index + "onScanFailed",
844                    mResults.clone());
845            mResults.clear();
846        }
847
848        @Override
849        public void onScanResult(int callbackType, ScanResult result) {
850            Log.d("bluetooth_le_scan change onUpdate " + mEventType + " " + index);
851            mResults.putInt("ID", index);
852            mResults.putInt("CallbackType", callbackType);
853            mResults.putString("Type", "onScanResult");
854            mResults.putParcelable("Result", result);
855            mEventFacade.postEvent(mEventType + index + "onScanResults", mResults.clone());
856            mResults.clear();
857        }
858
859        @Override
860        public void onBatchScanResults(List<ScanResult> results) {
861            Log.d("reportResult " + mEventType + " " + index);
862            mResults.putLong("Timestamp", System.currentTimeMillis() / 1000);
863            mResults.putInt("ID", index);
864            mResults.putString("Type", "onBatchScanResults");
865            mResults.putParcelableList("Results", results);
866            mEventFacade.postEvent(mEventType + index + "onBatchScanResult", mResults.clone());
867            mResults.clear();
868        }
869    }
870
871    private class myLeScanCallback implements LeScanCallback {
872        public Integer index;
873        String mEventType;
874        private final Bundle mResults;
875
876        public myLeScanCallback(Integer idx) {
877            index = idx;
878            mEventType = "ClassicBleScan";
879            mResults = new Bundle();
880        }
881
882        @Override
883        public void onLeScan(BluetoothDevice device, int rssi, byte[] scanRecord) {
884            Log.d("bluetooth_classic_le_scan " + mEventType + " " + index);
885            mResults.putParcelable("Device", device);
886            mResults.putInt("Rssi", rssi);
887            mResults.putString("ScanRecord", ConvertUtils.convertByteArrayToString(scanRecord));
888            mResults.putString("Type", "onLeScan");
889            mEventFacade.postEvent(mEventType + index + "onLeScan", mResults.clone());
890            mResults.clear();
891        }
892    }
893
894    @Override
895    public void shutdown() {
896      if (mBluetoothAdapter.getState() == BluetoothAdapter.STATE_ON) {
897          for (myScanCallback mScanCallback : mScanCallbackList.values()) {
898              if (mScanCallback != null) {
899                  try {
900                    mBluetoothAdapter.getBluetoothLeScanner()
901                      .stopScan(mScanCallback);
902                  } catch (NullPointerException e) {
903                    Log.e("Failed to stop ble scan callback.", e);
904                  }
905              }
906          }
907          for (myLeScanCallback mLeScanCallback : mLeScanCallbackList.values()) {
908              if (mLeScanCallback != null) {
909                  try {
910                      mBluetoothAdapter.stopLeScan(mLeScanCallback);
911                  } catch (NullPointerException e) {
912                    Log.e("Failed to stop classic ble scan callback.", e);
913                  }
914              }
915          }
916      }
917      mScanCallbackList.clear();
918      mScanFilterList.clear();
919      mScanSettingsList.clear();
920      mLeScanCallbackList.clear();
921    }
922}
923