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 
17 package com.googlecode.android_scripting.facade.bluetooth;
18 
19 import android.app.Service;
20 import android.bluetooth.BluetoothAdapter;
21 import android.bluetooth.BluetoothDevice;
22 import android.bluetooth.BluetoothHeadsetClient;
23 import android.bluetooth.BluetoothProfile;
24 import android.bluetooth.BluetoothUuid;
25 import android.os.ParcelUuid;
26 
27 import com.googlecode.android_scripting.Log;
28 import com.googlecode.android_scripting.facade.FacadeManager;
29 import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
30 import com.googlecode.android_scripting.rpc.Rpc;
31 import com.googlecode.android_scripting.rpc.RpcDefault;
32 import com.googlecode.android_scripting.rpc.RpcParameter;
33 
34 import java.util.ArrayList;
35 import java.util.List;
36 
37 public class BluetoothHfpClientFacade extends RpcReceiver {
38     static final ParcelUuid[] UUIDS = {
39         BluetoothUuid.HFP_AG,
40     };
41 
42     private final Service mService;
43     private final BluetoothAdapter mBluetoothAdapter;
44 
45     private static boolean sIsHfpClientReady = false;
46     private static BluetoothHeadsetClient sHfpClientProfile = null;
47 
BluetoothHfpClientFacade(FacadeManager manager)48     public BluetoothHfpClientFacade(FacadeManager manager) {
49         super(manager);
50         mService = manager.getService();
51         mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();
52         mBluetoothAdapter.getProfileProxy(mService,
53                 new HfpClientServiceListener(),
54                 BluetoothProfile.HEADSET_CLIENT);
55     }
56 
57     class HfpClientServiceListener implements BluetoothProfile.ServiceListener {
58         @Override
onServiceConnected(int profile, BluetoothProfile proxy)59         public void onServiceConnected(int profile, BluetoothProfile proxy) {
60             sHfpClientProfile = (BluetoothHeadsetClient) proxy;
61             sIsHfpClientReady = true;
62         }
63 
64         @Override
onServiceDisconnected(int profile)65         public void onServiceDisconnected(int profile) {
66             sIsHfpClientReady = false;
67         }
68     }
69 
70     /**
71      * Connect to HfpClient.
72      * @param device - the BluetoothDevice object to connect Hfp client.
73      * @return if the connection was successfull or not.
74      */
hfpClientConnect(BluetoothDevice device)75     public Boolean hfpClientConnect(BluetoothDevice device) {
76         if (sHfpClientProfile == null) return false;
77         return sHfpClientProfile.connect(device);
78     }
79 
80     /**
81      * Disconnect from HfpClient.
82      * @param device - the BluetoothDevice object to disconnect from Hfp client.
83      * @return if the disconnection was successfull or not.
84      */
hfpClientDisconnect(BluetoothDevice device)85     public Boolean hfpClientDisconnect(BluetoothDevice device) {
86         if (sHfpClientProfile == null) return false;
87         return sHfpClientProfile.disconnect(device);
88     }
89 
90     /**
91      * Is Hfp Client profile ready.
92      * @return Hfp Client profile is ready or not.
93      */
94     @Rpc(description = "Is HfpClient profile ready.")
bluetoothHfpClientIsReady()95     public Boolean bluetoothHfpClientIsReady() {
96         return sIsHfpClientReady;
97     }
98 
99     /**
100      * Set priority of the profile.
101      * @param deviceStr - Mac address of a BT device.
102      * @param priority - Priority that needs to be set.
103      */
104     @Rpc(description = "Set priority of the profile")
bluetoothHfpClientSetPriority( @pcParametername = "device", description = "Mac address of a BT device.") String deviceStr, @RpcParameter(name = "priority", description = "Priority that needs to be set.") Integer priority)105     public void bluetoothHfpClientSetPriority(
106             @RpcParameter(name = "device",
107                 description = "Mac address of a BT device.") String deviceStr,
108             @RpcParameter(name = "priority",
109                 description = "Priority that needs to be set.")
110                     Integer priority) throws Exception {
111         if (sHfpClientProfile == null) return;
112         BluetoothDevice device =
113                 BluetoothFacade.getDevice(mBluetoothAdapter.getBondedDevices(),
114                     deviceStr);
115         Log.d("Changing priority of device " + device.getAlias()
116                 + " p: " + priority);
117         sHfpClientProfile.setPriority(device, priority);
118     }
119 
120     /**
121      * Get priority of the profile.
122      * @param deviceStr - Mac address of a BT device.
123      * @return Priority of the device.
124      */
125     @Rpc(description = "Get priority of the profile")
bluetoothHfpClientGetPriority( @pcParametername = "device", description = "Mac address of a BT device.") String deviceStr)126     public Integer bluetoothHfpClientGetPriority(
127             @RpcParameter(name = "device", description =
128                     "Mac address of a BT device.") String deviceStr)
129                     throws Exception {
130         if (sHfpClientProfile == null) return BluetoothProfile.PRIORITY_UNDEFINED;
131         BluetoothDevice device = BluetoothFacade.getDevice(
132                 mBluetoothAdapter.getBondedDevices(), deviceStr);
133         return sHfpClientProfile.getPriority(device);
134     }
135 
136     /**
137      * Connect to an HFP Client device.
138      * @param deviceStr - Name or MAC address of a bluetooth device.
139      * @return Hfp Client was connected or not.
140      */
141     @Rpc(description = "Connect to an HFP Client device.")
bluetoothHfpClientConnect( @pcParametername = "device", description = "Name or MAC address of a bluetooth device.") String deviceStr)142     public Boolean bluetoothHfpClientConnect(
143             @RpcParameter(name = "device",
144                 description = "Name or MAC address of a bluetooth device.")
145                     String deviceStr)
146                         throws Exception {
147         if (sHfpClientProfile == null) return false;
148         try {
149             BluetoothDevice device = BluetoothFacade.getDevice(
150                     BluetoothFacade.DiscoveredDevices, deviceStr);
151             Log.d("Connecting to device " + device.getAlias());
152             return hfpClientConnect(device);
153         } catch (Exception e) {
154             Log.e("bluetoothHfpClientConnect failed on getDevice "
155                     + deviceStr + " with " + e);
156             return false;
157         }
158     }
159 
160     /**
161      * Disconnect an HFP Client device.
162      * @param deviceStr - Name or MAC address of a bluetooth device.
163      * @return Hfp Client was disconnected or not.
164      */
165     @Rpc(description = "Disconnect an HFP Client device.")
bluetoothHfpClientDisconnect( @pcParametername = "device", description = "Name or MAC address of a device.") String deviceStr)166     public Boolean bluetoothHfpClientDisconnect(
167             @RpcParameter(name = "device",
168                 description = "Name or MAC address of a device.")
169                     String deviceStr) {
170         if (sHfpClientProfile == null) return false;
171         Log.d("Connected devices: " + sHfpClientProfile.getConnectedDevices());
172         try {
173             BluetoothDevice device = BluetoothFacade.getDevice(
174                     sHfpClientProfile.getConnectedDevices(), deviceStr);
175             return hfpClientDisconnect(device);
176         } catch (Exception e) {
177             // Do nothing since it is disconnect and this
178             // function should force disconnect.
179             Log.e("bluetoothHfpClientConnect getDevice failed " + e);
180         }
181         return false;
182     }
183 
184     /**
185      * Get all the devices connected through HFP Client.
186      * @return List of all the devices connected through HFP Client.
187      */
188     @Rpc(description = "Get all the devices connected through HFP Client.")
bluetoothHfpClientGetConnectedDevices()189     public List<BluetoothDevice> bluetoothHfpClientGetConnectedDevices() {
190         if (sHfpClientProfile == null) return new ArrayList<BluetoothDevice>();
191         return sHfpClientProfile.getConnectedDevices();
192     }
193 
194     /**
195      * Get the connection status of a device.
196      * @param deviceID - Name or MAC address of a bluetooth device.
197      * @return connection status of the device.
198      */
199     @Rpc(description = "Get the connection status of a device.")
bluetoothHfpClientGetConnectionStatus( @pcParametername = "deviceID", description = "Name or MAC address of a bluetooth device.") String deviceID)200     public Integer bluetoothHfpClientGetConnectionStatus(
201             @RpcParameter(name = "deviceID",
202                 description = "Name or MAC address of a bluetooth device.")
203                     String deviceID) {
204         if (sHfpClientProfile == null) {
205             return BluetoothProfile.STATE_DISCONNECTED;
206         }
207         List<BluetoothDevice> deviceList =
208                 sHfpClientProfile.getConnectedDevices();
209         BluetoothDevice device;
210         try {
211             device = BluetoothFacade.getDevice(deviceList, deviceID);
212         } catch (Exception e) {
213             Log.e(e);
214             return BluetoothProfile.STATE_DISCONNECTED;
215         }
216         return sHfpClientProfile.getConnectionState(device);
217     }
218 
219     /**
220      * Get the audio routing state of specified device.
221      * @param deviceStr the Bluetooth MAC address of remote device
222      * @return Audio State of the device.
223      */
224     @Rpc(description = "Get all the devices connected through HFP Client.")
bluetoothHfpClientGetAudioState( @pcParametername = "device", description = "MAC address of a bluetooth device.") String deviceStr)225     public Integer bluetoothHfpClientGetAudioState(
226             @RpcParameter(name = "device",
227                 description = "MAC address of a bluetooth device.")
228                 String deviceStr) {
229         if (sHfpClientProfile == null) return -1;
230         BluetoothDevice device;
231         try {
232             device =  BluetoothFacade.getDevice(sHfpClientProfile.getConnectedDevices(), deviceStr);
233         } catch (Exception e) {
234             // Do nothing since it is disconnect and this function should force disconnect.
235             Log.e("bluetoothHfpClientConnect getDevice failed " + e);
236             return -1;
237         }
238         return sHfpClientProfile.getAudioState(device);
239     }
240 
getDevice(String deviceStr)241     private BluetoothDevice getDevice(String deviceStr) {
242         if (sHfpClientProfile == null) return null;
243         BluetoothDevice device;
244         try {
245             device =  BluetoothFacade.getDevice(sHfpClientProfile.getConnectedDevices(), deviceStr);
246         } catch (Exception e) {
247             // Do nothing since it is disconnect and this function should force disconnect.
248             Log.e("bluetoothHfpClientConnect getDevice failed " + e);
249             return null;
250         }
251         return device;
252     }
253 
254     /**
255      * Starts Voice Recognition on remote device
256      *
257      * @param deviceStr the Bluetooth MAC address of remote device
258      * @return True if command has been issued successfully
259      */
260     @Rpc(description = "Start Remote device Voice Recognition through HFP Client.")
bluetoothHfpClientStartVoiceRecognition( @pcParametername = "device", description = "MAC address of a bluetooth device.") String deviceStr)261     public boolean bluetoothHfpClientStartVoiceRecognition(
262             @RpcParameter(name = "device",
263                 description = "MAC address of a bluetooth device.")
264                     String deviceStr) {
265         BluetoothDevice device = getDevice(deviceStr);
266         if (device == null) return false;
267         return sHfpClientProfile.startVoiceRecognition(device);
268     }
269 
270     /**
271      * Stops Voice Recognition in the remote device through Bluetooth HFP client
272      *
273      * @param deviceStr the Bluetooth MAC address of remote device
274      * @return True if command has been issued successfully
275      */
276     @Rpc(description = "Stops Remote device Voice Recognition through HFP Client.")
bluetoothHfpClientStopVoiceRecognition( @pcParametername = "device", description = "MAC address of a bluetooth device.") String deviceStr)277     public boolean bluetoothHfpClientStopVoiceRecognition(
278             @RpcParameter(name = "device",
279                 description = "MAC address of a bluetooth device.")
280                     String deviceStr) {
281         BluetoothDevice device = getDevice(deviceStr);
282         if (device == null) return false;
283         return sHfpClientProfile.stopVoiceRecognition(device);
284     }
285 
286     /**
287      * Initiates a connection of audio channel.
288      *
289      * It setup SCO channel with remote connected Handsfree Audio Gateway device.
290      *
291      * @param deviceStr the Bluetooth MAC address of remote device
292      * @return True if command has been issued successfully
293      */
294     @Rpc(description = "Initiates a connection of audio channel.")
bluetoothHfpClientConnectAudio( @pcParametername = "device", description = "MAC address of a bluetooth device.") String deviceStr)295     public boolean bluetoothHfpClientConnectAudio(
296             @RpcParameter(name = "device",
297                 description = "MAC address of a bluetooth device.")
298                 String deviceStr) {
299         BluetoothDevice device = getDevice(deviceStr);
300         if (device == null) return false;
301         return sHfpClientProfile.connectAudio(device);
302     }
303 
304     /**
305      * Dials a call in the remote device through Bluetooth HFP Client
306      *
307      * The last number dial will be placed if the number is an empty string
308      *
309      * @param deviceStr the Bluetooth MAC address of remote device
310      * @param number phone number to dial, the value of null or empty string for last number redial
311      * @return The string of <code>{@link BluetoothHeadsetClientCall} call</code> or null if no
312      *         device was not found.
313      */
314     @Rpc(description = "Dials a call in the remote device through HFP Client.")
bluetoothHfpClientDial( @pcParametername = "device", description = "MAC address of a bluetooth device.") String deviceStr, @RpcParameter(name = "phone_number", description = "phone number to dial, default for last number redial") @RpcDefault("") String number)315     public String bluetoothHfpClientDial(
316             @RpcParameter(name = "device",
317                 description = "MAC address of a bluetooth device.") String deviceStr,
318             @RpcParameter(name = "phone_number", description =
319                 "phone number to dial, default for last number redial")
320                 @RpcDefault("") String number) {
321         BluetoothDevice device = getDevice(deviceStr);
322         if (device == null) return null;
323         return sHfpClientProfile.dial(device, number).toString(true);
324     }
325 
326     /**
327      * Disconnects audio channel.
328      *
329      * It tears down the SCO channel from remote AuG device.
330      *
331      * @param deviceStr the Bluetooth MAC address of remote device
332      * @return True if command has been issued successfully
333      */
334     @Rpc(description = "Disconnects audio channel.")
bluetoothHfpClientDisconnectAudio( @pcParametername = "device", description = "MAC address of a bluetooth device.") String deviceStr)335     public boolean bluetoothHfpClientDisconnectAudio(
336             @RpcParameter(name = "device",
337                 description = "MAC address of a bluetooth device.")
338                 String deviceStr) {
339         BluetoothDevice device = getDevice(deviceStr);
340         if (device == null) return false;
341         return sHfpClientProfile.disconnectAudio(device);
342     }
343 
344     /**
345      * Accepts a call in the remote device through Bluetooth HFP Client
346      *
347      * @param deviceStr the Bluetooth MAC address of remote device
348      * @return True if command has been issued successfully
349      */
350     @Rpc(description = "Accepts a call in the remote device through HFP Client.")
bluetoothHfpClientAcceptCall( @pcParametername = "device", description = "MAC address of a bluetooth device.") String deviceStr)351     public boolean bluetoothHfpClientAcceptCall(
352             @RpcParameter(name = "device",
353                 description = "MAC address of a bluetooth device.")
354                     String deviceStr) {
355         BluetoothDevice device = getDevice(deviceStr);
356         if (device == null) return false;
357         return sHfpClientProfile.acceptCall(device, BluetoothHeadsetClient.CALL_ACCEPT_NONE);
358     }
359 
360     /**
361      * Terminates all calls in the remote device through Bluetooth HFP Client
362      *
363      * @param deviceStr the Bluetooth MAC address of remote device
364      * @return True if command has been issued successfully
365      */
366     @Rpc(description = "Terminates all calls in the remote device through HFP Client.")
bluetoothHfpClientTerminateAllCalls( @pcParametername = "device", description = "MAC address of a bluetooth device.") String deviceStr)367     public boolean bluetoothHfpClientTerminateAllCalls(
368             @RpcParameter(name = "device",
369                 description = "MAC address of a bluetooth device.")
370                     String deviceStr) {
371         BluetoothDevice device = getDevice(deviceStr);
372         if (device == null) return false;
373         return sHfpClientProfile.terminateCall(device, null);
374     }
375 
376     /**
377      * Rejects a call in the remote device through Bluetooth HFP Client
378      *
379      * @param deviceStr the Bluetooth MAC address of remote device
380      * @return True if command has been issued successfully
381      */
382     @Rpc(description = "Rejects a call in the remote device through HFP Client.")
bluetoothHfpClientRejectCall( @pcParametername = "device", description = "MAC address of a bluetooth device.") String deviceStr)383     public boolean bluetoothHfpClientRejectCall(
384             @RpcParameter(name = "device",
385                 description = "MAC address of a bluetooth device.")
386                     String deviceStr) {
387         BluetoothDevice device = getDevice(deviceStr);
388         if (device == null) return false;
389         return sHfpClientProfile.rejectCall(device);
390     }
391 
392     @Override
shutdown()393     public void shutdown() {
394     }
395 }