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 android.bluetooth; 18 19 import android.content.ComponentName; 20 import android.content.Context; 21 import android.content.Intent; 22 import android.content.ServiceConnection; 23 import android.os.Binder; 24 import android.os.IBinder; 25 import android.os.RemoteException; 26 import android.util.Log; 27 28 import java.util.ArrayList; 29 import java.util.List; 30 31 /** 32 * This class provides the APIs to control the Bluetooth PBAP Client Profile. 33 *@hide 34 */ 35 public final class BluetoothPbapClient implements BluetoothProfile { 36 37 private static final String TAG = "BluetoothPbapClient"; 38 private static final boolean DBG = false; 39 private static final boolean VDBG = false; 40 41 public static final String ACTION_CONNECTION_STATE_CHANGED = 42 "android.bluetooth.pbap.profile.action.CONNECTION_STATE_CHANGED"; 43 44 private volatile IBluetoothPbapClient mService; 45 private final Context mContext; 46 private ServiceListener mServiceListener; 47 private BluetoothAdapter mAdapter; 48 49 /** There was an error trying to obtain the state */ 50 public static final int STATE_ERROR = -1; 51 52 public static final int RESULT_FAILURE = 0; 53 public static final int RESULT_SUCCESS = 1; 54 /** Connection canceled before completion. */ 55 public static final int RESULT_CANCELED = 2; 56 57 final private IBluetoothStateChangeCallback mBluetoothStateChangeCallback = 58 new IBluetoothStateChangeCallback.Stub() { 59 public void onBluetoothStateChange(boolean up) { 60 if (DBG) { 61 Log.d(TAG, "onBluetoothStateChange: PBAP CLIENT up=" + up); 62 } 63 if (!up) { 64 if (VDBG) { 65 Log.d(TAG,"Unbinding service..."); 66 } 67 synchronized (mConnection) { 68 try { 69 mService = null; 70 mContext.unbindService(mConnection); 71 } catch (Exception re) { 72 Log.e(TAG,"",re); 73 } 74 } 75 } else { 76 synchronized (mConnection) { 77 try { 78 if (mService == null) { 79 if (VDBG) { 80 Log.d(TAG,"Binding service..."); 81 } 82 doBind(); 83 } 84 } catch (Exception re) { 85 Log.e(TAG,"",re); 86 } 87 } 88 } 89 } 90 }; 91 92 /** 93 * Create a BluetoothPbapClient proxy object. 94 */ BluetoothPbapClient(Context context, ServiceListener l)95 BluetoothPbapClient(Context context, ServiceListener l) { 96 if (DBG) { 97 Log.d(TAG, "Create BluetoothPbapClient proxy object"); 98 } 99 mContext = context; 100 mServiceListener = l; 101 mAdapter = BluetoothAdapter.getDefaultAdapter(); 102 IBluetoothManager mgr = mAdapter.getBluetoothManager(); 103 if (mgr != null) { 104 try { 105 mgr.registerStateChangeCallback(mBluetoothStateChangeCallback); 106 } catch (RemoteException e) { 107 Log.e(TAG,"",e); 108 } 109 } 110 doBind(); 111 } 112 doBind()113 private boolean doBind() { 114 Intent intent = new Intent(IBluetoothPbapClient.class.getName()); 115 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0); 116 intent.setComponent(comp); 117 if (comp == null || !mContext.bindServiceAsUser(intent, mConnection, 0, 118 android.os.Process.myUserHandle())) { 119 Log.e(TAG, "Could not bind to Bluetooth PBAP Client Service with " + intent); 120 return false; 121 } 122 return true; 123 } 124 finalize()125 protected void finalize() throws Throwable { 126 try { 127 close(); 128 } finally { 129 super.finalize(); 130 } 131 } 132 133 /** 134 * Close the connection to the backing service. 135 * Other public functions of BluetoothPbapClient will return default error 136 * results once close() has been called. Multiple invocations of close() 137 * are ok. 138 */ close()139 public synchronized void close() { 140 IBluetoothManager mgr = mAdapter.getBluetoothManager(); 141 if (mgr != null) { 142 try { 143 mgr.unregisterStateChangeCallback(mBluetoothStateChangeCallback); 144 } catch (Exception e) { 145 Log.e(TAG,"",e); 146 } 147 } 148 149 synchronized (mConnection) { 150 if (mService != null) { 151 try { 152 mService = null; 153 mContext.unbindService(mConnection); 154 } catch (Exception re) { 155 Log.e(TAG,"",re); 156 } 157 } 158 } 159 mServiceListener = null; 160 } 161 162 /** 163 * Initiate connection. 164 * Upon successful connection to remote PBAP server the Client will 165 * attempt to automatically download the users phonebook and call log. 166 * 167 * @param device a remote device we want connect to 168 * @return <code>true</code> if command has been issued successfully; 169 * <code>false</code> otherwise; 170 */ connect(BluetoothDevice device)171 public boolean connect(BluetoothDevice device) { 172 if (DBG) { 173 log("connect(" + device + ") for PBAP Client."); 174 } 175 final IBluetoothPbapClient service = mService; 176 if (service != null && isEnabled() && isValidDevice(device)) { 177 try { 178 return service.connect(device); 179 } catch (RemoteException e) { 180 Log.e(TAG, Log.getStackTraceString(new Throwable())); 181 return false; 182 } 183 } 184 if (service == null) { 185 Log.w(TAG, "Proxy not attached to service"); 186 } 187 return false; 188 } 189 190 /** 191 * Initiate disconnect. 192 * 193 * @param device Remote Bluetooth Device 194 * @return false on error, 195 * true otherwise 196 */ disconnect(BluetoothDevice device)197 public boolean disconnect(BluetoothDevice device) { 198 if (DBG) { 199 log("disconnect(" + device + ")" + new Exception() ); 200 } 201 final IBluetoothPbapClient service = mService; 202 if (service != null && isEnabled() && isValidDevice(device)) { 203 try { 204 service.disconnect(device); 205 return true; 206 } catch (RemoteException e) { 207 Log.e(TAG, Log.getStackTraceString(new Throwable())); 208 return false; 209 } 210 } 211 if (service == null) { 212 Log.w(TAG, "Proxy not attached to service"); 213 } 214 return false; 215 } 216 217 /** 218 * Get the list of connected devices. 219 * Currently at most one. 220 * 221 * @return list of connected devices 222 */ 223 @Override getConnectedDevices()224 public List<BluetoothDevice> getConnectedDevices() { 225 if (DBG) { 226 log("getConnectedDevices()"); 227 } 228 final IBluetoothPbapClient service = mService; 229 if (service != null && isEnabled()) { 230 try { 231 return service.getConnectedDevices(); 232 } catch (RemoteException e) { 233 Log.e(TAG, Log.getStackTraceString(new Throwable())); 234 return new ArrayList<BluetoothDevice>(); 235 } 236 } 237 if (service == null) { 238 Log.w(TAG, "Proxy not attached to service"); 239 } 240 return new ArrayList<BluetoothDevice>(); 241 } 242 243 /** 244 * Get the list of devices matching specified states. Currently at most one. 245 * 246 * @return list of matching devices 247 */ 248 @Override getDevicesMatchingConnectionStates(int[] states)249 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 250 if (DBG) { 251 log("getDevicesMatchingStates()"); 252 } 253 final IBluetoothPbapClient service = mService; 254 if (service != null && isEnabled()) { 255 try { 256 return service.getDevicesMatchingConnectionStates(states); 257 } catch (RemoteException e) { 258 Log.e(TAG, Log.getStackTraceString(new Throwable())); 259 return new ArrayList<BluetoothDevice>(); 260 } 261 } 262 if (service == null) { 263 Log.w(TAG, "Proxy not attached to service"); 264 } 265 return new ArrayList<BluetoothDevice>(); 266 } 267 268 /** 269 * Get connection state of device 270 * 271 * @return device connection state 272 */ 273 @Override getConnectionState(BluetoothDevice device)274 public int getConnectionState(BluetoothDevice device) { 275 if (DBG) { 276 log("getConnectionState(" + device + ")"); 277 } 278 final IBluetoothPbapClient service = mService; 279 if (service != null && isEnabled() && isValidDevice(device)) { 280 try { 281 return service.getConnectionState(device); 282 } catch (RemoteException e) { 283 Log.e(TAG, Log.getStackTraceString(new Throwable())); 284 return BluetoothProfile.STATE_DISCONNECTED; 285 } 286 } 287 if (service == null) { 288 Log.w(TAG, "Proxy not attached to service"); 289 } 290 return BluetoothProfile.STATE_DISCONNECTED; 291 } 292 293 private final ServiceConnection mConnection = new ServiceConnection() { 294 public void onServiceConnected(ComponentName className, IBinder service) { 295 if (DBG) { 296 log("Proxy object connected"); 297 } 298 mService = IBluetoothPbapClient.Stub.asInterface(Binder.allowBlocking(service)); 299 if (mServiceListener != null) { 300 mServiceListener.onServiceConnected(BluetoothProfile.PBAP_CLIENT, BluetoothPbapClient.this); 301 } 302 } 303 public void onServiceDisconnected(ComponentName className) { 304 if (DBG) { 305 log("Proxy object disconnected"); 306 } 307 mService = null; 308 if (mServiceListener != null) { 309 mServiceListener.onServiceDisconnected(BluetoothProfile.PBAP_CLIENT); 310 } 311 } 312 }; 313 log(String msg)314 private static void log(String msg) { 315 Log.d(TAG, msg); 316 } 317 isEnabled()318 private boolean isEnabled() { 319 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 320 if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) { 321 return true; 322 } 323 log("Bluetooth is Not enabled"); 324 return false; 325 } 326 isValidDevice(BluetoothDevice device)327 private static boolean isValidDevice(BluetoothDevice device) { 328 return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); 329 } 330 331 /** 332 * Set priority of the profile 333 * 334 * <p> The device should already be paired. 335 * Priority can be one of {@link #PRIORITY_ON} or 336 * {@link #PRIORITY_OFF}, 337 * 338 * @param device Paired bluetooth device 339 * @param priority Priority of this profile 340 * @return true if priority is set, false on error 341 */ setPriority(BluetoothDevice device, int priority)342 public boolean setPriority(BluetoothDevice device, int priority) { 343 if (DBG) { 344 log("setPriority(" + device + ", " + priority + ")"); 345 } 346 final IBluetoothPbapClient service = mService; 347 if (service != null && isEnabled() && isValidDevice(device)) { 348 if (priority != BluetoothProfile.PRIORITY_OFF 349 && priority != BluetoothProfile.PRIORITY_ON) { 350 return false; 351 } 352 try { 353 return service.setPriority(device, priority); 354 } catch (RemoteException e) { 355 Log.e(TAG, Log.getStackTraceString(new Throwable())); 356 return false; 357 } 358 } 359 if (service == null) { 360 Log.w(TAG, "Proxy not attached to service"); 361 } 362 return false; 363 } 364 365 /** 366 * Get the priority of the profile. 367 * 368 * <p> The priority can be any of: 369 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF}, 370 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} 371 * 372 * @param device Bluetooth device 373 * @return priority of the device 374 */ getPriority(BluetoothDevice device)375 public int getPriority(BluetoothDevice device) { 376 if (VDBG) { 377 log("getPriority(" + device + ")"); 378 } 379 final IBluetoothPbapClient service = mService; 380 if (service != null && isEnabled() && isValidDevice(device)) { 381 try { 382 return service.getPriority(device); 383 } catch (RemoteException e) { 384 Log.e(TAG, Log.getStackTraceString(new Throwable())); 385 return PRIORITY_OFF; 386 } 387 } 388 if (service == null) { 389 Log.w(TAG, "Proxy not attached to service"); 390 } 391 return PRIORITY_OFF; 392 } 393 } 394