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( 76 BluetoothMapClient.ACTION_MESSAGE_SENT_SUCCESSFULLY); 77 mDeliveryIntent = new Intent( 78 BluetoothMapClient.ACTION_MESSAGE_DELIVERED_SUCCESSFULLY); 79 IntentFilter intentFilter = new IntentFilter(); 80 intentFilter.addAction(BluetoothMapClient.ACTION_MESSAGE_RECEIVED); 81 intentFilter.addAction( 82 BluetoothMapClient.ACTION_MESSAGE_SENT_SUCCESSFULLY); 83 intentFilter.addAction( 84 BluetoothMapClient.ACTION_MESSAGE_DELIVERED_SUCCESSFULLY); 85 mService.registerReceiver(mNotificationReceiver, intentFilter); 86 Log.d("notification receiver registered"); 87 } 88 89 class MapServiceListener implements BluetoothProfile.ServiceListener { 90 @Override onServiceConnected(int profile, BluetoothProfile proxy)91 public void onServiceConnected(int profile, BluetoothProfile proxy) { 92 sMapProfile = (BluetoothMapClient) proxy; 93 sIsMapReady = true; 94 } 95 96 @Override onServiceDisconnected(int profile)97 public void onServiceDisconnected(int profile) { 98 sIsMapReady = false; 99 } 100 } 101 mapClientConnect(BluetoothDevice device)102 public Boolean mapClientConnect(BluetoothDevice device) { 103 if (sMapProfile == null) return false; 104 return sMapProfile.connect(device); 105 } 106 mapClientDisconnect(BluetoothDevice device)107 public Boolean mapClientDisconnect(BluetoothDevice device) { 108 if (sMapProfile == null) return false; 109 return sMapProfile.disconnect(device); 110 } 111 112 @Rpc(description = "Connect to an MAP MSE device.") bluetoothMapClientConnect( @pcParametername = "device", description = "Name or MAC address of a bluetooth " + "device.") String device)113 public Boolean bluetoothMapClientConnect( 114 @RpcParameter(name = "device", 115 description = "Name or MAC address of a bluetooth " 116 + "device.") 117 String device) 118 throws Exception { 119 if (sMapProfile == null) return false; 120 BluetoothDevice mDevice = BluetoothFacade.getDevice( 121 mBluetoothAdapter.getBondedDevices(), device); 122 Log.d("Connecting to device " + mDevice.getAlias()); 123 return sMapProfile.connect(mDevice); 124 } 125 126 @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)127 public Boolean mapSendMessage( 128 @RpcParameter(name = "deviceID", 129 description = "Name or MAC address of a device.") 130 String deviceID, 131 @RpcParameter(name = "phoneNumbers", 132 description = "Phone number of contact.") 133 String[] phoneNumbers, 134 @RpcParameter(name = "message", 135 description = "Message to send.") String message) { 136 try { 137 BluetoothDevice device = 138 BluetoothFacade.getDevice( 139 sMapProfile.getConnectedDevices(), deviceID); 140 mSentIntent = PendingIntent.getBroadcast(mService, 0, mSendIntent, 141 PendingIntent.FLAG_ONE_SHOT); 142 mDeliveredIntent = PendingIntent.getBroadcast( 143 mService, 0, mDeliveryIntent, 144 PendingIntent.FLAG_ONE_SHOT); 145 Uri[] contacts = new Uri[phoneNumbers.length]; 146 for (int i = 0; i < phoneNumbers.length; i++) { 147 Log.d("PhoneNumber count: " + phoneNumbers.length + " = " 148 + phoneNumbers[i]); 149 contacts[i] = Uri.parse(phoneNumbers[i]); 150 } 151 return sMapProfile.sendMessage(device, contacts, message, mSentIntent, 152 mDeliveredIntent); 153 } catch (Exception e) { 154 Log.d("Error sending message, no such device " + e.toString()); 155 } 156 return false; 157 } 158 mapDisconnect(BluetoothDevice device)159 public Boolean mapDisconnect(BluetoothDevice device) { 160 if (sMapProfile.getPriority(device) > BluetoothProfile.PRIORITY_ON) { 161 sMapProfile.setPriority(device, BluetoothProfile.PRIORITY_ON); 162 } 163 return sMapProfile.disconnect(device); 164 } 165 166 @Rpc(description = "Is Map profile ready.") bluetoothMapClientIsReady()167 public Boolean bluetoothMapClientIsReady() { 168 return sIsMapReady; 169 } 170 171 @Rpc(description = "Disconnect an MAP device.") bluetoothMapClientDisconnect( @pcParametername = "deviceID", description = "Name or MAC address of a device.") String deviceID)172 public Boolean bluetoothMapClientDisconnect( 173 @RpcParameter(name = "deviceID", 174 description = "Name or MAC address of a device.") 175 String deviceID) 176 throws Exception { 177 if (sMapProfile == null) return false; 178 List<BluetoothDevice> connectedMapDevices = 179 sMapProfile.getConnectedDevices(); 180 Log.d("Connected map devices: " + connectedMapDevices); 181 BluetoothDevice mDevice = BluetoothFacade.getDevice( 182 connectedMapDevices, deviceID); 183 if (!connectedMapDevices.isEmpty() 184 && connectedMapDevices.get(0).equals(mDevice)) { 185 if (sMapProfile.getPriority(mDevice) > BluetoothProfile.PRIORITY_ON) { 186 sMapProfile.setPriority(mDevice, BluetoothProfile.PRIORITY_ON); 187 } 188 return sMapProfile.disconnect(mDevice); 189 } else { 190 return false; 191 } 192 } 193 194 @Rpc(description = "Get all the devices connected through MAP.") bluetoothMapClientGetConnectedDevices()195 public List<BluetoothDevice> bluetoothMapClientGetConnectedDevices() { 196 while (!sIsMapReady) ; 197 return sMapProfile.getDevicesMatchingConnectionStates( 198 new int[]{BluetoothProfile.STATE_CONNECTED, 199 BluetoothProfile.STATE_CONNECTING, 200 BluetoothProfile.STATE_DISCONNECTING}); 201 } 202 203 @Override shutdown()204 public void shutdown() { 205 mService.unregisterReceiver(mNotificationReceiver); 206 } 207 208 public class NotificationReceiver extends BroadcastReceiver { 209 @Override onReceive(Context context, Intent intent)210 public void onReceive(Context context, Intent intent) { 211 Log.d("OnReceive" + intent); 212 String action = intent.getAction(); 213 if (action.equals(BluetoothMapClient.ACTION_MESSAGE_RECEIVED)) { 214 mEventFacade.postEvent(MAP_EVENT, 215 intent.getStringExtra(android.content.Intent.EXTRA_TEXT)); 216 } else if (action.equals( 217 BluetoothMapClient.ACTION_MESSAGE_SENT_SUCCESSFULLY)) { 218 mEventFacade.postEvent(MAP_SMS_SENT_SUCCESS, 219 intent.getStringExtra( 220 android.content.Intent.EXTRA_TEXT)); 221 } else if (action.equals( 222 BluetoothMapClient.ACTION_MESSAGE_DELIVERED_SUCCESSFULLY)) { 223 mEventFacade.postEvent(MAP_SMS_DELIVER_SUCCESS, 224 intent.getStringExtra(android.content.Intent.EXTRA_TEXT)); 225 } 226 } 227 } 228 } 229