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.PendingIntent; 20 import android.app.Service; 21 import android.bluetooth.BluetoothAdapter; 22 import android.bluetooth.BluetoothDevice; 23 import android.bluetooth.BluetoothMapClient; 24 import android.bluetooth.BluetoothProfile; 25 import android.bluetooth.BluetoothUuid; 26 import android.content.BroadcastReceiver; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.IntentFilter; 30 import android.net.Uri; 31 import android.os.ParcelUuid; 32 33 import com.googlecode.android_scripting.Log; 34 import com.googlecode.android_scripting.facade.EventFacade; 35 import com.googlecode.android_scripting.facade.FacadeManager; 36 import com.googlecode.android_scripting.jsonrpc.RpcReceiver; 37 import com.googlecode.android_scripting.rpc.Rpc; 38 import com.googlecode.android_scripting.rpc.RpcParameter; 39 40 import java.util.List; 41 42 public class BluetoothMapClientFacade extends RpcReceiver { 43 static final ParcelUuid[] MAP_UUIDS = { 44 BluetoothUuid.MAP, 45 BluetoothUuid.MNS, 46 BluetoothUuid.MAS, 47 }; 48 public static final String MAP_EVENT = "MapMessageReceived"; 49 public static final String MAP_SMS_SENT_SUCCESS = "SmsSentSuccess"; 50 public static final String MAP_SMS_DELIVER_SUCCESS = "SmsDeliverSuccess"; 51 52 private final Service mService; 53 private final BluetoothAdapter mBluetoothAdapter; 54 private final EventFacade mEventFacade; 55 private final NotificationReceiver mNotificationReceiver; 56 57 private Intent mSendIntent; 58 private Intent mDeliveryIntent; 59 private PendingIntent mSentIntent; 60 private PendingIntent mDeliveredIntent; 61 62 private static boolean sIsMapReady = false; 63 private static BluetoothMapClient sMapProfile = null; 64 BluetoothMapClientFacade(FacadeManager manager)65 public BluetoothMapClientFacade(FacadeManager manager) { 66 super(manager); 67 Log.d("Creating BluetoothMapClientFacade"); 68 mService = manager.getService(); 69 mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter(); 70 mBluetoothAdapter.getProfileProxy(mService, new MapServiceListener(), 71 BluetoothProfile.MAP_CLIENT); 72 mEventFacade = manager.getReceiver(EventFacade.class); 73 74 mNotificationReceiver = new NotificationReceiver(); 75 mSendIntent = new Intent(BluetoothMapClient.ACTION_MESSAGE_SENT_SUCCESSFULLY); 76 mDeliveryIntent = new Intent(BluetoothMapClient.ACTION_MESSAGE_DELIVERED_SUCCESSFULLY); 77 IntentFilter intentFilter = new IntentFilter(); 78 intentFilter.addAction(BluetoothMapClient.ACTION_MESSAGE_RECEIVED); 79 intentFilter.addAction(BluetoothMapClient.ACTION_MESSAGE_SENT_SUCCESSFULLY); 80 intentFilter.addAction(BluetoothMapClient.ACTION_MESSAGE_DELIVERED_SUCCESSFULLY); 81 mService.registerReceiver(mNotificationReceiver, intentFilter); 82 Log.d("notification receiver registered"); 83 } 84 85 class MapServiceListener implements BluetoothProfile.ServiceListener { 86 @Override onServiceConnected(int profile, BluetoothProfile proxy)87 public void onServiceConnected(int profile, BluetoothProfile proxy) { 88 sMapProfile = (BluetoothMapClient) proxy; 89 sIsMapReady = true; 90 } 91 92 @Override onServiceDisconnected(int profile)93 public void onServiceDisconnected(int profile) { 94 sIsMapReady = false; 95 } 96 } 97 mapClientConnect(BluetoothDevice device)98 public Boolean mapClientConnect(BluetoothDevice device) { 99 if (sMapProfile == null) return false; 100 return sMapProfile.connect(device); 101 } 102 mapClientDisconnect(BluetoothDevice device)103 public Boolean mapClientDisconnect(BluetoothDevice device) { 104 if (sMapProfile == null) return false; 105 return sMapProfile.disconnect(device); 106 } 107 108 @Rpc(description = "Connect to an MAP MSE device.") bluetoothMapClientConnect( @pcParametername = "device", description = "Name or MAC address of a bluetooth " + "device.") String device)109 public Boolean bluetoothMapClientConnect( 110 @RpcParameter(name = "device", description = "Name or MAC address of a bluetooth " 111 + "device.") 112 String device) 113 throws Exception { 114 if (sMapProfile == null) return false; 115 BluetoothDevice mDevice = BluetoothFacade.getDevice(mBluetoothAdapter.getBondedDevices(), 116 device); 117 Log.d("Connecting to device " + mDevice.getAliasName()); 118 return sMapProfile.connect(mDevice); 119 } 120 121 @Rpc(description = "Send a (text) message via bluetooth.") mapSendMessage( @pcParametername = "deviceID", description = "Name or MAC address of a device.") String deviceID, @RpcParameter(name = "phoneNumbers", description = "Phone number of contact.") String[] phoneNumbers, @RpcParameter(name = "message", description = "Message to send.") String message)122 public Boolean mapSendMessage( 123 @RpcParameter(name = "deviceID", description = "Name or MAC address of a device.") 124 String deviceID, 125 @RpcParameter(name = "phoneNumbers", description = "Phone number of contact.") 126 String[] phoneNumbers, 127 @RpcParameter(name = "message", description = "Message to send.") String message) { 128 try { 129 BluetoothDevice device = 130 BluetoothFacade.getDevice(sMapProfile.getConnectedDevices(), deviceID); 131 mSentIntent = PendingIntent.getBroadcast(mService, 0, mSendIntent, 132 PendingIntent.FLAG_ONE_SHOT); 133 mDeliveredIntent = PendingIntent.getBroadcast(mService, 0, mDeliveryIntent, 134 PendingIntent.FLAG_ONE_SHOT); 135 Uri[] contacts = new Uri[phoneNumbers.length]; 136 for (int i = 0; i < phoneNumbers.length; i++) { 137 Log.d("PhoneNumber count: " + phoneNumbers.length + " = " + phoneNumbers[i]); 138 contacts[i] = Uri.parse(phoneNumbers[i]); 139 } 140 return sMapProfile.sendMessage(device, contacts, message, mSentIntent, 141 mDeliveredIntent); 142 } catch (Exception e) { 143 Log.d("Error sending message, no such device " + e.toString()); 144 } 145 return false; 146 } 147 mapDisconnect(BluetoothDevice device)148 public Boolean mapDisconnect(BluetoothDevice device) { 149 if (sMapProfile.getPriority(device) > BluetoothProfile.PRIORITY_ON) { 150 sMapProfile.setPriority(device, BluetoothProfile.PRIORITY_ON); 151 } 152 return sMapProfile.disconnect(device); 153 } 154 155 @Rpc(description = "Is Map profile ready.") bluetoothMapClientIsReady()156 public Boolean bluetoothMapClientIsReady() { 157 return sIsMapReady; 158 } 159 160 @Rpc(description = "Disconnect an MAP device.") bluetoothMapClientDisconnect( @pcParametername = "deviceID", description = "Name or MAC address of a device.") String deviceID)161 public Boolean bluetoothMapClientDisconnect( 162 @RpcParameter(name = "deviceID", description = "Name or MAC address of a device.") 163 String deviceID) 164 throws Exception { 165 if (sMapProfile == null) return false; 166 List<BluetoothDevice> connectedMapDevices = sMapProfile.getConnectedDevices(); 167 Log.d("Connected map devices: " + connectedMapDevices); 168 BluetoothDevice mDevice = BluetoothFacade.getDevice(connectedMapDevices, deviceID); 169 if (!connectedMapDevices.isEmpty() && connectedMapDevices.get(0).equals(mDevice)) { 170 if (sMapProfile.getPriority(mDevice) > BluetoothProfile.PRIORITY_ON) { 171 sMapProfile.setPriority(mDevice, BluetoothProfile.PRIORITY_ON); 172 } 173 return sMapProfile.disconnect(mDevice); 174 } else { 175 return false; 176 } 177 } 178 179 @Rpc(description = "Get all the devices connected through MAP.") bluetoothMapClientGetConnectedDevices()180 public List<BluetoothDevice> bluetoothMapClientGetConnectedDevices() { 181 while (!sIsMapReady) ; 182 return sMapProfile.getDevicesMatchingConnectionStates( 183 new int[]{BluetoothProfile.STATE_CONNECTED, 184 BluetoothProfile.STATE_CONNECTING, 185 BluetoothProfile.STATE_DISCONNECTING}); 186 } 187 188 @Override shutdown()189 public void shutdown() { 190 mService.unregisterReceiver(mNotificationReceiver); 191 } 192 193 public class NotificationReceiver extends BroadcastReceiver { 194 @Override onReceive(Context context, Intent intent)195 public void onReceive(Context context, Intent intent) { 196 Log.d("OnReceive" + intent); 197 String action = intent.getAction(); 198 if (action.equals(BluetoothMapClient.ACTION_MESSAGE_RECEIVED)) { 199 mEventFacade.postEvent(MAP_EVENT, 200 intent.getStringExtra(android.content.Intent.EXTRA_TEXT)); 201 } else if (action.equals(BluetoothMapClient.ACTION_MESSAGE_SENT_SUCCESSFULLY)) { 202 mEventFacade.postEvent(MAP_SMS_SENT_SUCCESS, 203 intent.getStringExtra(android.content.Intent.EXTRA_TEXT)); 204 } else if (action.equals(BluetoothMapClient.ACTION_MESSAGE_DELIVERED_SUCCESSFULLY)) { 205 mEventFacade.postEvent(MAP_SMS_DELIVER_SUCCESS, 206 intent.getStringExtra(android.content.Intent.EXTRA_TEXT)); 207 } 208 } 209 } 210 } 211