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