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.getConnectionPolicy(device) > BluetoothProfile.CONNECTION_POLICY_ALLOWED) { 161 sMapProfile.setConnectionPolicy(device, BluetoothProfile.CONNECTION_POLICY_ALLOWED); 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.getConnectionPolicy(mDevice) 186 > BluetoothProfile.CONNECTION_POLICY_ALLOWED) { 187 sMapProfile.setConnectionPolicy( 188 mDevice, BluetoothProfile.CONNECTION_POLICY_ALLOWED); 189 } 190 return sMapProfile.disconnect(mDevice); 191 } else { 192 return false; 193 } 194 } 195 196 @Rpc(description = "Get all the devices connected through MAP.") bluetoothMapClientGetConnectedDevices()197 public List<BluetoothDevice> bluetoothMapClientGetConnectedDevices() { 198 while (!sIsMapReady) ; 199 return sMapProfile.getDevicesMatchingConnectionStates( 200 new int[]{BluetoothProfile.STATE_CONNECTED, 201 BluetoothProfile.STATE_CONNECTING, 202 BluetoothProfile.STATE_DISCONNECTING}); 203 } 204 205 @Override shutdown()206 public void shutdown() { 207 mService.unregisterReceiver(mNotificationReceiver); 208 } 209 210 public class NotificationReceiver extends BroadcastReceiver { 211 @Override onReceive(Context context, Intent intent)212 public void onReceive(Context context, Intent intent) { 213 Log.d("OnReceive" + intent); 214 String action = intent.getAction(); 215 if (action.equals(BluetoothMapClient.ACTION_MESSAGE_RECEIVED)) { 216 mEventFacade.postEvent(MAP_EVENT, 217 intent.getStringExtra(android.content.Intent.EXTRA_TEXT)); 218 } else if (action.equals( 219 BluetoothMapClient.ACTION_MESSAGE_SENT_SUCCESSFULLY)) { 220 mEventFacade.postEvent(MAP_SMS_SENT_SUCCESS, 221 intent.getStringExtra( 222 android.content.Intent.EXTRA_TEXT)); 223 } else if (action.equals( 224 BluetoothMapClient.ACTION_MESSAGE_DELIVERED_SUCCESSFULLY)) { 225 mEventFacade.postEvent(MAP_SMS_DELIVER_SUCCESS, 226 intent.getStringExtra(android.content.Intent.EXTRA_TEXT)); 227 } 228 } 229 } 230 } 231