1/* 2 * Copyright (C) 2016 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 17package com.android.bluetooth.mapclient; 18 19import android.Manifest; 20import android.app.PendingIntent; 21import android.bluetooth.BluetoothAdapter; 22import android.bluetooth.BluetoothDevice; 23import android.bluetooth.BluetoothProfile; 24import android.bluetooth.IBluetoothMapClient; 25import android.net.Uri; 26import android.provider.Settings; 27import android.util.Log; 28 29import com.android.bluetooth.Utils; 30import com.android.bluetooth.btservice.ProfileService; 31 32import java.util.ArrayList; 33import java.util.Arrays; 34import java.util.List; 35import java.util.Set; 36 37public class MapClientService extends ProfileService { 38 private static final String TAG = "MapClientService"; 39 40 static final boolean DBG = false; 41 static final boolean VDBG = false; 42 43 private static final int MAXIMUM_CONNECTED_DEVICES = 1; 44 45 private static final String BLUETOOTH_PERM = android.Manifest.permission.BLUETOOTH; 46 47 MceStateMachine mMceStateMachine; 48 private MnsService mMnsServer; 49 private BluetoothAdapter mAdapter; 50 private static MapClientService sMapClientService; 51 52 53 public static synchronized MapClientService getMapClientService() { 54 if (sMapClientService != null && sMapClientService.isAvailable()) { 55 if (DBG) Log.d(TAG, "getMapClientService(): returning " + sMapClientService); 56 return sMapClientService; 57 } 58 if (DBG) { 59 if (sMapClientService == null) { 60 Log.d(TAG, "getMapClientService(): service is NULL"); 61 } else if (!(sMapClientService.isAvailable())) { 62 Log.d(TAG, "getMapClientService(): service is not available"); 63 } 64 } 65 return null; 66 } 67 68 private static synchronized void setService(MapClientService instance) { 69 if (instance != null && instance.isAvailable()) { 70 if (DBG) Log.d(TAG, "setMapMceService(): replacing old instance: " + sMapClientService); 71 sMapClientService = instance; 72 } else { 73 if (DBG) { 74 if (sMapClientService == null) { 75 Log.d(TAG, "setA2dpService(): service not available"); 76 } else if (!sMapClientService.isAvailable()) { 77 Log.d(TAG, "setA2dpService(): service is cleaning up"); 78 } 79 } 80 } 81 } 82 83 public synchronized boolean connect(BluetoothDevice device) { 84 Log.d(TAG, "MAP Mce connect " + device.toString()); 85 return mMceStateMachine.connect(device); 86 } 87 88 public synchronized boolean disconnect(BluetoothDevice device) { 89 Log.d(TAG, "MAP Mce disconnect " + device.toString()); 90 return mMceStateMachine.disconnect(device); 91 } 92 93 public List<BluetoothDevice> getConnectedDevices() { 94 return getDevicesMatchingConnectionStates(new int[]{BluetoothAdapter.STATE_CONNECTED}); 95 } 96 97 public synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 98 Log.d(TAG, "getDevicesMatchingConnectionStates" + Arrays.toString(states)); 99 List<BluetoothDevice> deviceList = new ArrayList<>(); 100 Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices(); 101 int connectionState; 102 for (BluetoothDevice device : bondedDevices) { 103 connectionState = getConnectionState(device); 104 Log.d(TAG, "Device: " + device + "State: " + connectionState); 105 for (int i = 0; i < states.length; i++) { 106 if (connectionState == states[i]) { 107 deviceList.add(device); 108 } 109 } 110 } 111 Log.d(TAG, deviceList.toString()); 112 return deviceList; 113 } 114 115 public synchronized int getConnectionState(BluetoothDevice device) { 116 if (mMceStateMachine != null && device.equals(mMceStateMachine.getDevice())) { 117 return mMceStateMachine.getState(); 118 } else { 119 return BluetoothProfile.STATE_DISCONNECTED; 120 } 121 } 122 123 public boolean setPriority(BluetoothDevice device, int priority) { 124 Settings.Global.putInt(getContentResolver(), 125 Settings.Global.getBluetoothMapClientPriorityKey(device.getAddress()), 126 priority); 127 if (VDBG) Log.v(TAG, "Saved priority " + device + " = " + priority); 128 return true; 129 } 130 131 public int getPriority(BluetoothDevice device) { 132 int priority = Settings.Global.getInt(getContentResolver(), 133 Settings.Global.getBluetoothMapClientPriorityKey(device.getAddress()), 134 BluetoothProfile.PRIORITY_UNDEFINED); 135 return priority; 136 } 137 138 public synchronized boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message, 139 PendingIntent sentIntent, PendingIntent deliveredIntent) { 140 if (mMceStateMachine != null && device.equals(mMceStateMachine.getDevice())) { 141 return mMceStateMachine.sendMapMessage(contacts, message, sentIntent, deliveredIntent); 142 } else { 143 return false; 144 } 145 146 } 147 148 @Override 149 protected IProfileServiceBinder initBinder() { 150 return new Binder(this); 151 } 152 153 @Override 154 protected boolean start() { 155 if (DBG) Log.d(TAG, "start()"); 156 setService(this); 157 158 if (mMnsServer == null) { 159 mMnsServer = new MnsService(this); 160 } 161 if (mMceStateMachine == null) { 162 mMceStateMachine = new MceStateMachine(this); 163 } 164 165 mAdapter = BluetoothAdapter.getDefaultAdapter(); 166 mStartError = false; 167 return !mStartError; 168 } 169 170 @Override 171 protected synchronized boolean stop() { 172 if (DBG) Log.d(TAG, "stop()"); 173 if (mMnsServer != null) { 174 mMnsServer.stop(); 175 } 176 if (mMceStateMachine.getState() == BluetoothAdapter.STATE_CONNECTED) { 177 mMceStateMachine.disconnect(mMceStateMachine.getDevice()); 178 } 179 mMceStateMachine.doQuit(); 180 return true; 181 } 182 183 public boolean cleanup() { 184 if (DBG) Log.d(TAG, "cleanup()"); 185 return true; 186 } 187 188 public synchronized boolean getUnreadMessages(BluetoothDevice device) { 189 if (mMceStateMachine != null && device.equals(mMceStateMachine.getDevice())) { 190 return mMceStateMachine.getUnreadMessages(); 191 } else { 192 return false; 193 } 194 } 195 196 @Override 197 public synchronized void dump(StringBuilder sb) { 198 super.dump(sb); 199 println(sb, "StateMachine: " + mMceStateMachine.toString()); 200 } 201 202 //Binder object: Must be static class or memory leak may occur 203 /** 204 * This class implements the IClient interface - or actually it validates the 205 * preconditions for calling the actual functionality in the MapClientService, and calls it. 206 */ 207 private static class Binder extends IBluetoothMapClient.Stub 208 implements IProfileServiceBinder { 209 private MapClientService mService; 210 211 Binder(MapClientService service) { 212 if (VDBG) Log.v(TAG, "Binder()"); 213 mService = service; 214 } 215 216 private MapClientService getService() { 217 if (!Utils.checkCaller()) { 218 Log.w(TAG, "MAP call not allowed for non-active user"); 219 return null; 220 } 221 222 if (mService != null && mService.isAvailable()) { 223 mService.enforceCallingOrSelfPermission(BLUETOOTH_PERM, 224 "Need BLUETOOTH permission"); 225 return mService; 226 } 227 return null; 228 } 229 230 public boolean cleanup() { 231 mService = null; 232 return true; 233 } 234 235 public boolean isConnected(BluetoothDevice device) { 236 if (VDBG) Log.v(TAG, "isConnected()"); 237 MapClientService service = getService(); 238 if (service == null) return false; 239 return service.getConnectionState(device) == BluetoothProfile.STATE_CONNECTED; 240 } 241 242 public boolean connect(BluetoothDevice device) { 243 if (VDBG) Log.v(TAG, "connect()"); 244 MapClientService service = getService(); 245 if (service == null) return false; 246 return service.connect(device); 247 } 248 249 public boolean disconnect(BluetoothDevice device) { 250 if (VDBG) Log.v(TAG, "disconnect()"); 251 MapClientService service = getService(); 252 if (service == null) return false; 253 return service.disconnect(device); 254 } 255 256 public List<BluetoothDevice> getConnectedDevices() { 257 if (VDBG) Log.v(TAG, "getConnectedDevices()"); 258 MapClientService service = getService(); 259 if (service == null) return new ArrayList<BluetoothDevice>(0); 260 return service.getConnectedDevices(); 261 } 262 263 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 264 if (VDBG) Log.v(TAG, "getDevicesMatchingConnectionStates()"); 265 MapClientService service = getService(); 266 if (service == null) return new ArrayList<BluetoothDevice>(0); 267 return service.getDevicesMatchingConnectionStates(states); 268 } 269 270 public int getConnectionState(BluetoothDevice device) { 271 if (VDBG) Log.v(TAG, "getConnectionState()"); 272 MapClientService service = getService(); 273 if (service == null) return BluetoothProfile.STATE_DISCONNECTED; 274 return service.getConnectionState(device); 275 } 276 277 public boolean setPriority(BluetoothDevice device, int priority) { 278 MapClientService service = getService(); 279 if (service == null) return false; 280 return service.setPriority(device, priority); 281 } 282 283 public int getPriority(BluetoothDevice device) { 284 MapClientService service = getService(); 285 if (service == null) return BluetoothProfile.PRIORITY_UNDEFINED; 286 return service.getPriority(device); 287 } 288 289 public boolean sendMessage(BluetoothDevice device, Uri[] contacts, String message, 290 PendingIntent sentIntent, PendingIntent deliveredIntent) { 291 MapClientService service = getService(); 292 if (service == null) return false; 293 Log.d(TAG, "Checking Permission of sendMessage"); 294 mService.enforceCallingOrSelfPermission(Manifest.permission.SEND_SMS, 295 "Need SEND_SMS permission"); 296 297 return service.sendMessage(device, contacts, message, sentIntent, deliveredIntent); 298 } 299 300 public boolean getUnreadMessages(BluetoothDevice device) { 301 MapClientService service = getService(); 302 if (service == null) return false; 303 mService.enforceCallingOrSelfPermission(Manifest.permission.READ_SMS, 304 "Need READ_SMS permission"); 305 return service.getUnreadMessages(device); 306 } 307 } 308} 309