• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.googlecode.android_scripting.facade.bluetooth;
18 
19 import java.util.ArrayList;
20 import java.util.Collections;
21 import java.util.HashMap;
22 import java.util.List;
23 import java.util.Map;
24 
25 import android.app.Service;
26 import android.bluetooth.BluetoothA2dp;
27 import android.bluetooth.BluetoothA2dpSink;
28 import android.bluetooth.BluetoothAdapter;
29 import android.bluetooth.BluetoothDevice;
30 import android.bluetooth.BluetoothManager;
31 import android.bluetooth.BluetoothHeadset;
32 import android.bluetooth.BluetoothHeadsetClient;
33 import android.bluetooth.BluetoothInputDevice;
34 import android.bluetooth.BluetoothPan;
35 import android.bluetooth.BluetoothUuid;
36 import android.content.BroadcastReceiver;
37 import android.content.Context;
38 import android.content.Intent;
39 import android.content.IntentFilter;
40 import android.os.Bundle;
41 import android.os.ParcelUuid;
42 
43 import com.googlecode.android_scripting.Log;
44 import com.googlecode.android_scripting.facade.EventFacade;
45 import com.googlecode.android_scripting.facade.FacadeManager;
46 import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
47 import com.googlecode.android_scripting.rpc.Rpc;
48 import com.googlecode.android_scripting.rpc.RpcParameter;
49 
50 public class BluetoothConnectionFacade extends RpcReceiver {
51 
52     private final Service mService;
53     private final Context mContext;
54     private final BluetoothAdapter mBluetoothAdapter;
55     private final BluetoothManager mBluetoothManager;
56     private final BluetoothPairingHelper mPairingHelper;
57     private final Map<String, BroadcastReceiver> listeningDevices;
58     private final EventFacade mEventFacade;
59 
60     private final IntentFilter mDiscoverConnectFilter;
61     private final IntentFilter mPairingFilter;
62     private final IntentFilter mBondFilter;
63     private final IntentFilter mA2dpStateChangeFilter;
64     private final IntentFilter mA2dpSinkStateChangeFilter;
65     private final IntentFilter mHidStateChangeFilter;
66     private final IntentFilter mHspStateChangeFilter;
67     private final IntentFilter mHfpClientStateChangeFilter;
68     private final IntentFilter mPanStateChangeFilter;
69 
70     private final Bundle mGoodNews;
71     private final Bundle mBadNews;
72 
73     private BluetoothA2dpFacade mA2dpProfile;
74     private BluetoothA2dpSinkFacade mA2dpSinkProfile;
75     private BluetoothHidFacade mHidProfile;
76     private BluetoothHspFacade mHspProfile;
77     private BluetoothHfpClientFacade mHfpClientProfile;
78     private BluetoothPanFacade mPanProfile;
79 
BluetoothConnectionFacade(FacadeManager manager)80     public BluetoothConnectionFacade(FacadeManager manager) {
81         super(manager);
82         mService = manager.getService();
83         mContext = mService.getApplicationContext();
84         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
85         mBluetoothManager = (BluetoothManager) mContext.getSystemService(
86                 Service.BLUETOOTH_SERVICE);
87         // Use a synchronized map to avoid racing problems
88         listeningDevices = Collections.synchronizedMap(new HashMap<String, BroadcastReceiver>());
89 
90         mEventFacade = manager.getReceiver(EventFacade.class);
91         mPairingHelper = new BluetoothPairingHelper(mEventFacade);
92         mA2dpProfile = manager.getReceiver(BluetoothA2dpFacade.class);
93         mA2dpSinkProfile = manager.getReceiver(BluetoothA2dpSinkFacade.class);
94         mHidProfile = manager.getReceiver(BluetoothHidFacade.class);
95         mHspProfile = manager.getReceiver(BluetoothHspFacade.class);
96         mHfpClientProfile = manager.getReceiver(BluetoothHfpClientFacade.class);
97         mPanProfile = manager.getReceiver(BluetoothPanFacade.class);
98 
99         mDiscoverConnectFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND);
100         mDiscoverConnectFilter.addAction(BluetoothDevice.ACTION_UUID);
101         mDiscoverConnectFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
102 
103         mPairingFilter = new IntentFilter(BluetoothDevice.ACTION_PAIRING_REQUEST);
104         mPairingFilter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST);
105         mPairingFilter.setPriority(999);
106 
107         mBondFilter = new IntentFilter(BluetoothDevice.ACTION_BOND_STATE_CHANGED);
108         mBondFilter.addAction(BluetoothDevice.ACTION_FOUND);
109         mBondFilter.addAction(BluetoothAdapter.ACTION_DISCOVERY_FINISHED);
110 
111         mA2dpStateChangeFilter = new IntentFilter(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
112         mA2dpSinkStateChangeFilter =
113             new IntentFilter(BluetoothA2dpSink.ACTION_CONNECTION_STATE_CHANGED);
114         mHidStateChangeFilter =
115             new IntentFilter(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED);
116         mHspStateChangeFilter = new IntentFilter(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
117         mHfpClientStateChangeFilter =
118             new IntentFilter(BluetoothHeadsetClient.ACTION_CONNECTION_STATE_CHANGED);
119         mPanStateChangeFilter =
120             new IntentFilter(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED);
121 
122         mGoodNews = new Bundle();
123         mGoodNews.putBoolean("Status", true);
124         mBadNews = new Bundle();
125         mBadNews.putBoolean("Status", false);
126     }
127 
unregisterCachedListener(String listenerId)128     private void unregisterCachedListener(String listenerId) {
129         BroadcastReceiver listener = listeningDevices.remove(listenerId);
130         if (listener != null) {
131             mService.unregisterReceiver(listener);
132         }
133     }
134 
135     /**
136      * Connect to a specific device upon its discovery
137      */
138     public class DiscoverConnectReceiver extends BroadcastReceiver {
139         private final String mDeviceID;
140         private BluetoothDevice mDevice;
141 
142         /**
143          * Constructor
144          *
145          * @param deviceID Either the device alias name or mac address.
146          * @param bond If true, bond the device only.
147          */
DiscoverConnectReceiver(String deviceID)148         public DiscoverConnectReceiver(String deviceID) {
149             super();
150             mDeviceID = deviceID;
151         }
152 
153         @Override
onReceive(Context context, Intent intent)154         public void onReceive(Context context, Intent intent) {
155             String action = intent.getAction();
156             // The specified device is found.
157             if (action.equals(BluetoothDevice.ACTION_FOUND)) {
158                 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
159                 if (BluetoothFacade.deviceMatch(device, mDeviceID)) {
160                     Log.d("Found device " + device.getAliasName() + " for connection.");
161                     mBluetoothAdapter.cancelDiscovery();
162                     mDevice = device;
163                 }
164             // After discovery stops.
165             } else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
166                 if (mDevice == null) {
167                     Log.d("Device " + mDeviceID + " not discovered.");
168                     mEventFacade.postEvent("Bond" + mDeviceID, mBadNews);
169                     return;
170                 }
171                 boolean status = mDevice.fetchUuidsWithSdp();
172                 Log.d("Initiated ACL connection: " + status);
173             } else if (action.equals(BluetoothDevice.ACTION_UUID)) {
174                 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
175                 if (BluetoothFacade.deviceMatch(device, mDeviceID)) {
176                     Log.d("Initiating connections.");
177                     connectProfile(device, mDeviceID);
178                     mService.unregisterReceiver(listeningDevices.remove("Connect" + mDeviceID));
179                 }
180             }
181         }
182     }
183 
184     /**
185      * Connect to a specific device upon its discovery
186      */
187     public class DiscoverBondReceiver extends BroadcastReceiver {
188         private final String mDeviceID;
189         private BluetoothDevice mDevice = null;
190         private boolean started = false;
191 
192         /**
193          * Constructor
194          *
195          * @param deviceID Either the device alias name or Mac address.
196          */
DiscoverBondReceiver(String deviceID)197         public DiscoverBondReceiver(String deviceID) {
198             super();
199             mDeviceID = deviceID;
200         }
201 
202         @Override
onReceive(Context context, Intent intent)203         public void onReceive(Context context, Intent intent) {
204             String action = intent.getAction();
205             // The specified device is found.
206             if (action.equals(BluetoothDevice.ACTION_FOUND)) {
207                 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
208                 if (BluetoothFacade.deviceMatch(device, mDeviceID)) {
209                     Log.d("Found device " + device.getAliasName() + " for connection.");
210                     mBluetoothAdapter.cancelDiscovery();
211                     mDevice = device;
212                 }
213             // After discovery stops.
214             } else if (action.equals(BluetoothAdapter.ACTION_DISCOVERY_FINISHED)) {
215                 if (mDevice == null) {
216                     Log.d("Device " + mDeviceID + " was not discovered.");
217                     mEventFacade.postEvent("Bond", mBadNews);
218                     return;
219                 }
220                 // Attempt to initiate bonding.
221                 if (!started) {
222                     Log.d("Bond with " + mDevice.getAliasName());
223                     if (mDevice.createBond()) {
224                         started = true;
225                         Log.d("Bonding started.");
226                     } else {
227                         Log.e("Failed to bond with " + mDevice.getAliasName());
228                         mEventFacade.postEvent("Bond", mBadNews);
229                         mService.unregisterReceiver(listeningDevices.remove("Bond" + mDeviceID));
230                     }
231                 }
232             } else if (action.equals(BluetoothDevice.ACTION_BOND_STATE_CHANGED)) {
233                 Log.d("Bond state changing.");
234                 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
235                 if (BluetoothFacade.deviceMatch(device, mDeviceID)) {
236                     int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, -1);
237                     Log.d("New state is " + state);
238                     if (state == BluetoothDevice.BOND_BONDED) {
239                         Log.d("Bonding with " + mDeviceID + " successful.");
240                         mEventFacade.postEvent("Bond" + mDeviceID, mGoodNews);
241                         mService.unregisterReceiver(listeningDevices.remove("Bond" + mDeviceID));
242                     }
243                 }
244             }
245         }
246     }
247 
248     public class ConnectStateChangeReceiver extends BroadcastReceiver {
249         private final String mDeviceID;
250 
ConnectStateChangeReceiver(String deviceID)251         public ConnectStateChangeReceiver(String deviceID) {
252             mDeviceID = deviceID;
253         }
254 
255         @Override
onReceive(Context context, Intent intent)256         public void onReceive(Context context, Intent intent) {
257             String action = intent.getAction();
258             BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
259             // Check if received the specified device
260             if (!BluetoothFacade.deviceMatch(device, mDeviceID)) {
261                 return;
262             }
263             if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
264                 int state = intent.getIntExtra(BluetoothA2dp.EXTRA_STATE, -1);
265                 if (state == BluetoothA2dp.STATE_CONNECTED) {
266                     Bundle a2dpGoodNews = (Bundle) mGoodNews.clone();
267                     a2dpGoodNews.putString("Type", "a2dp");
268                     mEventFacade.postEvent("A2dpConnect" + mDeviceID, a2dpGoodNews);
269                     unregisterCachedListener("A2dpConnecting" + mDeviceID);
270                 }
271             } else if (action.equals(BluetoothInputDevice.ACTION_CONNECTION_STATE_CHANGED)) {
272                 int state = intent.getIntExtra(BluetoothInputDevice.EXTRA_STATE, -1);
273                 if (state == BluetoothInputDevice.STATE_CONNECTED) {
274                     mEventFacade.postEvent("HidConnect" + mDeviceID, mGoodNews);
275                     unregisterCachedListener("HidConnecting" + mDeviceID);
276                 }
277             } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
278                 int state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, -1);
279                 if (state == BluetoothHeadset.STATE_CONNECTED) {
280                     mEventFacade.postEvent("HspConnect" + mDeviceID, mGoodNews);
281                     unregisterCachedListener("HspConnecting" + mDeviceID);
282                 }
283             } else if (action.equals(BluetoothPan.ACTION_CONNECTION_STATE_CHANGED)) {
284                 int state = intent.getIntExtra(BluetoothPan.EXTRA_STATE, -1);
285                 if (state == BluetoothPan.STATE_CONNECTED) {
286                     mEventFacade.postEvent("PanConnect" + mDeviceID, mGoodNews);
287                     unregisterCachedListener("PanConnecting" + mDeviceID);
288                 }
289             }
290         }
291     }
292 
connectProfile(BluetoothDevice device, String deviceID)293     private void connectProfile(BluetoothDevice device, String deviceID) {
294         mService.registerReceiver(mPairingHelper, mPairingFilter);
295         ParcelUuid[] deviceUuids = device.getUuids();
296         Log.d("Device uuid is " + deviceUuids);
297         if (deviceUuids == null) {
298             mEventFacade.postEvent("BluetoothProfileConnectionEvent", mBadNews);
299         }
300         Log.d("Connecting to " + device.getAliasName());
301         if (BluetoothUuid.containsAnyUuid(BluetoothA2dpFacade.SINK_UUIDS, deviceUuids)) {
302             boolean status = mA2dpProfile.a2dpConnect(device);
303             if (status) {
304                 Log.d("Connecting A2dp...");
305                 ConnectStateChangeReceiver receiver = new ConnectStateChangeReceiver(deviceID);
306                 mService.registerReceiver(receiver, mA2dpStateChangeFilter);
307                 listeningDevices.put("A2dpConnecting" + deviceID, receiver);
308             } else {
309                 Log.d("Failed starting A2dp connection.");
310                 Bundle a2dpBadNews = (Bundle) mBadNews.clone();
311                 a2dpBadNews.putString("Type", "a2dp");
312                 mEventFacade.postEvent("Connect", a2dpBadNews);
313             }
314         }
315         if (BluetoothUuid.containsAnyUuid(BluetoothA2dpSinkFacade.SOURCE_UUIDS, deviceUuids)) {
316             boolean status = mA2dpSinkProfile.a2dpSinkConnect(device);
317             if (status) {
318                 Log.d("Connecting A2dp Sink...");
319                 ConnectStateChangeReceiver receiver = new ConnectStateChangeReceiver(deviceID);
320                 mService.registerReceiver(receiver, mA2dpSinkStateChangeFilter);
321                 listeningDevices.put("A2dpSinkConnecting" + deviceID, receiver);
322             } else {
323                 Log.d("Failed starting A2dp Sink connection.");
324                 Bundle a2dpSinkBadNews = (Bundle) mBadNews.clone();
325                 a2dpSinkBadNews.putString("Type", "a2dpsink");
326                 mEventFacade.postEvent("Connect", a2dpSinkBadNews);
327             }
328         }
329         if (BluetoothUuid.containsAnyUuid(BluetoothHidFacade.UUIDS, deviceUuids)) {
330             boolean status = mHidProfile.hidConnect(device);
331             if (status) {
332                 Log.d("Connecting Hid...");
333                 ConnectStateChangeReceiver receiver = new ConnectStateChangeReceiver(deviceID);
334                 mService.registerReceiver(receiver, mHidStateChangeFilter);
335                 listeningDevices.put("HidConnecting" + deviceID, receiver);
336             } else {
337                 Log.d("Failed starting Hid connection.");
338                 mEventFacade.postEvent("HidConnect" + deviceID, mBadNews);
339             }
340         }
341         if (BluetoothUuid.containsAnyUuid(BluetoothHspFacade.UUIDS, deviceUuids)) {
342             boolean status = mHspProfile.hspConnect(device);
343             if (status) {
344                 Log.d("Connecting Hsp...");
345                 ConnectStateChangeReceiver receiver = new ConnectStateChangeReceiver(deviceID);
346                 mService.registerReceiver(receiver, mHspStateChangeFilter);
347                 listeningDevices.put("HspConnecting" + deviceID, receiver);
348             } else {
349                 Log.d("Failed starting Hsp connection.");
350                 mEventFacade.postEvent("HspConnect" + deviceID, mBadNews);
351             }
352         }
353         if (BluetoothUuid.containsAnyUuid(BluetoothHfpClientFacade.UUIDS, deviceUuids)) {
354             boolean status = mHfpClientProfile.hfpClientConnect(device);
355             if (status) {
356                 Log.d("Connecting HFP Client ...");
357                 ConnectStateChangeReceiver receiver = new ConnectStateChangeReceiver(deviceID);
358                 mService.registerReceiver(receiver, mHfpClientStateChangeFilter);
359                 listeningDevices.put("HfpClientConnecting" + deviceID, receiver);
360             } else {
361                 Log.d("Failed starting Hfp Client connection.");
362                 mEventFacade.postEvent("HfpClientConnect" + deviceID, mBadNews);
363             }
364         }
365         if (BluetoothUuid.containsAnyUuid(BluetoothPanFacade.UUIDS, deviceUuids)) {
366             boolean status = mPanProfile.panConnect(device);
367             if (status) {
368                 Log.d("Connecting Pan...");
369                 ConnectStateChangeReceiver receiver = new ConnectStateChangeReceiver(deviceID);
370                 mService.registerReceiver(receiver, mPanStateChangeFilter);
371                 listeningDevices.put("PanConnecting" + deviceID, receiver);
372             } else {
373                 Log.d("Failed starting Pan connection.");
374                 mEventFacade.postEvent("PanConnect" + deviceID, mBadNews);
375             }
376         }
377         mService.unregisterReceiver(mPairingHelper);
378     }
379 
disconnectProfiles(BluetoothDevice device, String deviceID)380     private void disconnectProfiles(BluetoothDevice device, String deviceID) {
381         Log.d("Disconnecting device " + device);
382         // Blindly disconnect all profiles. We may not have some of them connected so that will be a
383         // null op.
384         mA2dpProfile.a2dpDisconnect(device);
385         mA2dpSinkProfile.a2dpSinkDisconnect(device);
386         mHidProfile.hidDisconnect(device);
387         mHspProfile.hspDisconnect(device);
388         mHfpClientProfile.hfpClientDisconnect(device);
389         mPanProfile.panDisconnect(device);
390     }
391 
392     @Rpc(description = "Start intercepting all bluetooth connection pop-ups.")
bluetoothStartPairingHelper()393     public void bluetoothStartPairingHelper() {
394         mService.registerReceiver(mPairingHelper, mPairingFilter);
395     }
396 
397     @Rpc(description = "Return a list of devices connected through bluetooth")
bluetoothGetConnectedDevices()398     public List<BluetoothDevice> bluetoothGetConnectedDevices() {
399         ArrayList<BluetoothDevice> results = new ArrayList<BluetoothDevice>();
400         for (BluetoothDevice bd : mBluetoothAdapter.getBondedDevices()) {
401             if (bd.isConnected()) {
402                 results.add(bd);
403             }
404         }
405         return results;
406     }
407 
408     @Rpc(description = "Return a list of devices connected through bluetooth LE")
bluetoothGetConnectedLeDevices(Integer profile)409     public List<BluetoothDevice> bluetoothGetConnectedLeDevices(Integer profile) {
410         return mBluetoothManager.getConnectedDevices(profile);
411     }
412 
413     @Rpc(description = "Return true if a bluetooth device is connected.")
bluetoothIsDeviceConnected(String deviceID)414     public Boolean bluetoothIsDeviceConnected(String deviceID) {
415         for (BluetoothDevice bd : mBluetoothAdapter.getBondedDevices()) {
416             if (BluetoothFacade.deviceMatch(bd, deviceID)) {
417                 return bd.isConnected();
418             }
419         }
420         return false;
421     }
422 
423     @Rpc(description = "Connect to a specified device once it's discovered.",
424          returns = "Whether discovery started successfully.")
bluetoothDiscoverAndConnect( @pcParametername = "deviceID", description = "Name or MAC address of a bluetooth device.") String deviceID)425     public Boolean bluetoothDiscoverAndConnect(
426             @RpcParameter(name = "deviceID",
427                           description = "Name or MAC address of a bluetooth device.")
428             String deviceID) {
429         mBluetoothAdapter.cancelDiscovery();
430         if (listeningDevices.containsKey(deviceID)) {
431             Log.d("This device is already in the process of discovery and connecting.");
432             return true;
433         }
434         DiscoverConnectReceiver receiver = new DiscoverConnectReceiver(deviceID);
435         listeningDevices.put("Connect" + deviceID, receiver);
436         mService.registerReceiver(receiver, mDiscoverConnectFilter);
437         return mBluetoothAdapter.startDiscovery();
438     }
439 
440     @Rpc(description = "Bond to a specified device once it's discovered.",
441          returns = "Whether discovery started successfully. ")
bluetoothDiscoverAndBond( @pcParametername = "deviceID", description = "Name or MAC address of a bluetooth device.") String deviceID)442     public Boolean bluetoothDiscoverAndBond(
443             @RpcParameter(name = "deviceID",
444                           description = "Name or MAC address of a bluetooth device.")
445             String deviceID) {
446         mBluetoothAdapter.cancelDiscovery();
447         if (listeningDevices.containsKey(deviceID)) {
448             Log.d("This device is already in the process of discovery and bonding.");
449             return true;
450         }
451         if (BluetoothFacade.deviceExists(mBluetoothAdapter.getBondedDevices(), deviceID)) {
452             Log.d("Device " + deviceID + " is already bonded.");
453             mEventFacade.postEvent("Bond" + deviceID, mGoodNews);
454             return true;
455         }
456         DiscoverBondReceiver receiver = new DiscoverBondReceiver(deviceID);
457         if (listeningDevices.containsKey("Bond" + deviceID)) {
458             mService.unregisterReceiver(listeningDevices.remove("Bond" + deviceID));
459         }
460         listeningDevices.put("Bond" + deviceID, receiver);
461         mService.registerReceiver(receiver, mBondFilter);
462         Log.d("Start discovery for bonding.");
463         return mBluetoothAdapter.startDiscovery();
464     }
465 
466     @Rpc(description = "Unbond a device.",
467          returns = "Whether the device was successfully unbonded.")
bluetoothUnbond( @pcParametername = "deviceID", description = "Name or MAC address of a bluetooth device.") String deviceID)468     public Boolean bluetoothUnbond(
469             @RpcParameter(name = "deviceID",
470                           description = "Name or MAC address of a bluetooth device.")
471             String deviceID) throws Exception {
472         BluetoothDevice mDevice = BluetoothFacade.getDevice(mBluetoothAdapter.getBondedDevices(),
473                 deviceID);
474         return mDevice.removeBond();
475     }
476 
477     @Rpc(description = "Connect to a device that is already bonded.")
bluetoothConnectBonded( @pcParametername = "deviceID", description = "Name or MAC address of a bluetooth device.") String deviceID)478     public void bluetoothConnectBonded(
479             @RpcParameter(name = "deviceID",
480                           description = "Name or MAC address of a bluetooth device.")
481             String deviceID) throws Exception {
482         BluetoothDevice mDevice = BluetoothFacade.getDevice(mBluetoothAdapter.getBondedDevices(),
483                 deviceID);
484         connectProfile(mDevice, deviceID);
485     }
486 
487     // TODO: Split the disconnect RPC by profiles as well for granular control over the ACL
488     @Rpc(description = "Disconnect from a device that is already connected.")
bluetoothDisconnectConnected( @pcParametername = "deviceID", description = "Name or MAC address of a bluetooth device.") String deviceID)489     public void bluetoothDisconnectConnected(
490             @RpcParameter(name = "deviceID",
491                           description = "Name or MAC address of a bluetooth device.")
492             String deviceID) throws Exception {
493         BluetoothDevice mDevice = BluetoothFacade.getDevice(mBluetoothAdapter.getBondedDevices(),
494                 deviceID);
495         disconnectProfiles(mDevice, deviceID);
496     }
497 
498     @Override
shutdown()499     public void shutdown() {
500         for(BroadcastReceiver receiver : listeningDevices.values()) {
501             mService.unregisterReceiver(receiver);
502         }
503         listeningDevices.clear();
504         mService.unregisterReceiver(mPairingHelper);
505     }
506 }
507