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