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 17 package com.android.bluetooth.pbapclient; 18 19 import android.accounts.Account; 20 import android.accounts.AccountManager; 21 import android.bluetooth.BluetoothDevice; 22 import android.bluetooth.BluetoothProfile; 23 import android.bluetooth.IBluetoothPbapClient; 24 import android.bluetooth.IBluetoothHeadsetClient; 25 import android.content.BroadcastReceiver; 26 import android.content.ContentProviderOperation; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.content.IntentFilter; 30 import android.content.OperationApplicationException; 31 import android.os.Bundle; 32 import android.os.Handler; 33 import android.os.HandlerThread; 34 import android.os.Message; 35 import android.os.RemoteException; 36 import android.provider.Settings; 37 import android.util.Log; 38 import android.provider.ContactsContract; 39 40 import com.android.bluetooth.btservice.ProfileService; 41 import com.android.bluetooth.Utils; 42 import com.android.vcard.VCardEntry; 43 44 45 import java.lang.ref.WeakReference; 46 import java.util.Arrays; 47 import java.util.ArrayList; 48 import java.util.List; 49 import java.util.HashMap; 50 51 /** 52 * Provides Bluetooth Phone Book Access Profile Client profile. 53 * 54 * @hide 55 */ 56 public class PbapClientService extends ProfileService { 57 private static final boolean DBG = false; 58 private static final String TAG = "PbapClientService"; 59 private PbapPCEClient mClient; 60 private HandlerThread mHandlerThread; 61 private AccountManager mAccountManager; 62 private static PbapClientService sPbapClientService; 63 private PbapBroadcastReceiver mPbapBroadcastReceiver = new PbapBroadcastReceiver(); 64 65 @Override 66 protected String getName() { 67 return TAG; 68 } 69 70 @Override 71 public IProfileServiceBinder initBinder() { 72 return new BluetoothPbapClientBinder(this); 73 } 74 75 @Override 76 protected boolean start() { 77 IntentFilter filter = new IntentFilter(); 78 filter.addAction(BluetoothDevice.ACTION_ACL_CONNECTED); 79 filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED); 80 try { 81 registerReceiver(mPbapBroadcastReceiver, filter); 82 } catch (Exception e) { 83 Log.w(TAG,"Unable to register pbapclient receiver",e); 84 } 85 mClient = new PbapPCEClient(this); 86 mAccountManager = AccountManager.get(this); 87 setPbapClientService(this); 88 mClient.start(); 89 return true; 90 } 91 92 @Override 93 protected boolean stop() { 94 try { 95 unregisterReceiver(mPbapBroadcastReceiver); 96 } catch (Exception e) { 97 Log.w(TAG,"Unable to unregister sap receiver",e); 98 } 99 mClient.disconnect(null); 100 return true; 101 } 102 103 @Override 104 protected boolean cleanup() { 105 clearPbapClientService(); 106 return true; 107 } 108 109 private class PbapBroadcastReceiver extends BroadcastReceiver { 110 @Override 111 public void onReceive(Context context, Intent intent) { 112 Log.v(TAG, "onReceive"); 113 String action = intent.getAction(); 114 if (action.equals(BluetoothDevice.ACTION_ACL_CONNECTED)) { 115 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 116 if(getPriority(device) >= BluetoothProfile.PRIORITY_ON) { 117 connect(device); 118 } 119 } else if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED)) { 120 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 121 disconnect(device); 122 } 123 } 124 } 125 126 /** 127 * Handler for incoming service calls 128 */ 129 private static class BluetoothPbapClientBinder extends IBluetoothPbapClient.Stub 130 implements IProfileServiceBinder { 131 private PbapClientService mService; 132 133 public BluetoothPbapClientBinder(PbapClientService svc) { 134 mService = svc; 135 } 136 137 @Override 138 public boolean cleanup() { 139 mService = null; 140 return true; 141 } 142 143 private PbapClientService getService() { 144 if (!Utils.checkCaller()) { 145 Log.w(TAG, "PbapClient call not allowed for non-active user"); 146 return null; 147 } 148 149 if (mService != null && mService.isAvailable()) { 150 return mService; 151 } 152 return null; 153 } 154 155 @Override 156 public boolean connect(BluetoothDevice device) { 157 PbapClientService service = getService(); 158 if (service == null) { 159 return false; 160 } 161 return service.connect(device); 162 } 163 164 @Override 165 public boolean disconnect(BluetoothDevice device) { 166 PbapClientService service = getService(); 167 if (service == null) { 168 return false; 169 } 170 return service.disconnect(device); 171 } 172 173 @Override 174 public List<BluetoothDevice> getConnectedDevices() { 175 PbapClientService service = getService(); 176 if (service == null) { 177 return new ArrayList<BluetoothDevice>(0); 178 } 179 return service.getConnectedDevices(); 180 } 181 @Override 182 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 183 PbapClientService service = getService(); 184 if (service == null) { 185 return new ArrayList<BluetoothDevice>(0); 186 } 187 return service.getDevicesMatchingConnectionStates(states); 188 } 189 190 @Override 191 public int getConnectionState(BluetoothDevice device) { 192 PbapClientService service = getService(); 193 if (service == null) { 194 return BluetoothProfile.STATE_DISCONNECTED; 195 } 196 return service.getConnectionState(device); 197 } 198 199 public boolean setPriority(BluetoothDevice device, int priority) { 200 PbapClientService service = getService(); 201 if (service == null) { 202 return false; 203 } 204 return service.setPriority(device, priority); 205 } 206 207 public int getPriority(BluetoothDevice device) { 208 PbapClientService service = getService(); 209 if (service == null) { 210 return BluetoothProfile.PRIORITY_UNDEFINED; 211 } 212 return service.getPriority(device); 213 } 214 215 216 } 217 218 219 // API methods 220 public static synchronized PbapClientService getPbapClientService() { 221 if (sPbapClientService != null && sPbapClientService.isAvailable()) { 222 if (DBG) { 223 Log.d(TAG, "getPbapClientService(): returning " + sPbapClientService); 224 } 225 return sPbapClientService; 226 } 227 if (DBG) { 228 if (sPbapClientService == null) { 229 Log.d(TAG, "getPbapClientService(): service is NULL"); 230 } else if (!(sPbapClientService.isAvailable())) { 231 Log.d(TAG, "getPbapClientService(): service is not available"); 232 } 233 } 234 return null; 235 } 236 237 private static synchronized void setPbapClientService(PbapClientService instance) { 238 if (instance != null && instance.isAvailable()) { 239 if (DBG) { 240 Log.d(TAG, "setPbapClientService(): set to: " + sPbapClientService); 241 } 242 sPbapClientService = instance; 243 } else { 244 if (DBG) { 245 if (sPbapClientService == null) { 246 Log.d(TAG, "setPbapClientService(): service not available"); 247 } else if (!sPbapClientService.isAvailable()) { 248 Log.d(TAG, "setPbapClientService(): service is cleaning up"); 249 } 250 } 251 } 252 } 253 254 private static synchronized void clearPbapClientService() { 255 sPbapClientService = null; 256 } 257 258 public boolean connect(BluetoothDevice device) { 259 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 260 "Need BLUETOOTH ADMIN permission"); 261 Log.d(TAG,"Received request to ConnectPBAPPhonebook " + device.getAddress()); 262 int connectionState = mClient.getConnectionState(); 263 if (connectionState == BluetoothProfile.STATE_CONNECTED || 264 connectionState == BluetoothProfile.STATE_CONNECTING) { 265 return false; 266 } 267 if (getPriority(device)>BluetoothProfile.PRIORITY_OFF) { 268 mClient.connect(device); 269 return true; 270 } 271 return false; 272 } 273 274 boolean disconnect(BluetoothDevice device) { 275 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 276 "Need BLUETOOTH ADMIN permission"); 277 mClient.disconnect(device); 278 return true; 279 } 280 public List<BluetoothDevice> getConnectedDevices() { 281 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 282 int[] desiredStates = {BluetoothProfile.STATE_CONNECTED}; 283 return getDevicesMatchingConnectionStates(desiredStates); 284 } 285 286 private List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 287 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 288 int clientState = mClient.getConnectionState(); 289 Log.d(TAG,"getDevicesMatchingConnectionStates " + Arrays.toString(states) + " == " + clientState); 290 List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>(); 291 for (int state : states) { 292 if (clientState == state) { 293 BluetoothDevice currentDevice = mClient.getDevice(); 294 if (currentDevice != null) { 295 deviceList.add(currentDevice); 296 } 297 } 298 } 299 return deviceList; 300 } 301 302 int getConnectionState(BluetoothDevice device) { 303 enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission"); 304 if (device == mClient.getDevice()) { 305 return mClient.getConnectionState(); 306 } 307 return BluetoothProfile.STATE_DISCONNECTED; 308 } 309 310 public boolean setPriority(BluetoothDevice device, int priority) { 311 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 312 "Need BLUETOOTH_ADMIN permission"); 313 Settings.Global.putInt(getContentResolver(), 314 Settings.Global.getBluetoothPbapClientPriorityKey(device.getAddress()), 315 priority); 316 if (DBG) { 317 Log.d(TAG,"Saved priority " + device + " = " + priority); 318 } 319 return true; 320 } 321 322 public int getPriority(BluetoothDevice device) { 323 enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM, 324 "Need BLUETOOTH_ADMIN permission"); 325 int priority = Settings.Global.getInt(getContentResolver(), 326 Settings.Global.getBluetoothPbapClientPriorityKey(device.getAddress()), 327 BluetoothProfile.PRIORITY_UNDEFINED); 328 return priority; 329 } 330 331 } 332