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.Context; 20 import android.os.Binder; 21 import android.os.IBinder; 22 import android.os.RemoteException; 23 import android.util.Log; 24 25 import java.util.ArrayList; 26 import java.util.List; 27 28 /** 29 * This class provides the APIs to control the Bluetooth PBAP Client Profile. 30 * 31 * @hide 32 */ 33 public 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.pbapclient.profile.action.CONNECTION_STATE_CHANGED"; 41 42 /** There was an error trying to obtain the state */ 43 public static final int STATE_ERROR = -1; 44 45 public static final int RESULT_FAILURE = 0; 46 public static final int RESULT_SUCCESS = 1; 47 /** Connection canceled before completion. */ 48 public static final int RESULT_CANCELED = 2; 49 50 private BluetoothAdapter mAdapter; 51 private final BluetoothProfileConnector<IBluetoothPbapClient> mProfileConnector = 52 new BluetoothProfileConnector(this, BluetoothProfile.PBAP_CLIENT, 53 "BluetoothPbapClient", IBluetoothPbapClient.class.getName()) { 54 @Override 55 public IBluetoothPbapClient getServiceInterface(IBinder service) { 56 return IBluetoothPbapClient.Stub.asInterface(Binder.allowBlocking(service)); 57 } 58 }; 59 60 /** 61 * Create a BluetoothPbapClient proxy object. 62 */ BluetoothPbapClient(Context context, ServiceListener listener)63 BluetoothPbapClient(Context context, ServiceListener listener) { 64 if (DBG) { 65 Log.d(TAG, "Create BluetoothPbapClient proxy object"); 66 } 67 mAdapter = BluetoothAdapter.getDefaultAdapter(); 68 mProfileConnector.connect(context, listener); 69 } 70 finalize()71 protected void finalize() throws Throwable { 72 try { 73 close(); 74 } finally { 75 super.finalize(); 76 } 77 } 78 79 /** 80 * Close the connection to the backing service. 81 * Other public functions of BluetoothPbapClient will return default error 82 * results once close() has been called. Multiple invocations of close() 83 * are ok. 84 */ close()85 public synchronized void close() { 86 mProfileConnector.disconnect(); 87 } 88 getService()89 private IBluetoothPbapClient getService() { 90 return mProfileConnector.getService(); 91 } 92 93 /** 94 * Initiate connection. 95 * Upon successful connection to remote PBAP server the Client will 96 * attempt to automatically download the users phonebook and call log. 97 * 98 * @param device a remote device we want connect to 99 * @return <code>true</code> if command has been issued successfully; <code>false</code> 100 * otherwise; 101 */ connect(BluetoothDevice device)102 public boolean connect(BluetoothDevice device) { 103 if (DBG) { 104 log("connect(" + device + ") for PBAP Client."); 105 } 106 final IBluetoothPbapClient service = getService(); 107 if (service != null && isEnabled() && isValidDevice(device)) { 108 try { 109 return service.connect(device); 110 } catch (RemoteException e) { 111 Log.e(TAG, Log.getStackTraceString(new Throwable())); 112 return false; 113 } 114 } 115 if (service == null) { 116 Log.w(TAG, "Proxy not attached to service"); 117 } 118 return false; 119 } 120 121 /** 122 * Initiate disconnect. 123 * 124 * @param device Remote Bluetooth Device 125 * @return false on error, true otherwise 126 */ disconnect(BluetoothDevice device)127 public boolean disconnect(BluetoothDevice device) { 128 if (DBG) { 129 log("disconnect(" + device + ")" + new Exception()); 130 } 131 final IBluetoothPbapClient service = getService(); 132 if (service != null && isEnabled() && isValidDevice(device)) { 133 try { 134 service.disconnect(device); 135 return true; 136 } catch (RemoteException e) { 137 Log.e(TAG, Log.getStackTraceString(new Throwable())); 138 return false; 139 } 140 } 141 if (service == null) { 142 Log.w(TAG, "Proxy not attached to service"); 143 } 144 return false; 145 } 146 147 /** 148 * Get the list of connected devices. 149 * Currently at most one. 150 * 151 * @return list of connected devices 152 */ 153 @Override getConnectedDevices()154 public List<BluetoothDevice> getConnectedDevices() { 155 if (DBG) { 156 log("getConnectedDevices()"); 157 } 158 final IBluetoothPbapClient service = getService(); 159 if (service != null && isEnabled()) { 160 try { 161 return service.getConnectedDevices(); 162 } catch (RemoteException e) { 163 Log.e(TAG, Log.getStackTraceString(new Throwable())); 164 return new ArrayList<BluetoothDevice>(); 165 } 166 } 167 if (service == null) { 168 Log.w(TAG, "Proxy not attached to service"); 169 } 170 return new ArrayList<BluetoothDevice>(); 171 } 172 173 /** 174 * Get the list of devices matching specified states. Currently at most one. 175 * 176 * @return list of matching devices 177 */ 178 @Override getDevicesMatchingConnectionStates(int[] states)179 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 180 if (DBG) { 181 log("getDevicesMatchingStates()"); 182 } 183 final IBluetoothPbapClient service = getService(); 184 if (service != null && isEnabled()) { 185 try { 186 return service.getDevicesMatchingConnectionStates(states); 187 } catch (RemoteException e) { 188 Log.e(TAG, Log.getStackTraceString(new Throwable())); 189 return new ArrayList<BluetoothDevice>(); 190 } 191 } 192 if (service == null) { 193 Log.w(TAG, "Proxy not attached to service"); 194 } 195 return new ArrayList<BluetoothDevice>(); 196 } 197 198 /** 199 * Get connection state of device 200 * 201 * @return device connection state 202 */ 203 @Override getConnectionState(BluetoothDevice device)204 public int getConnectionState(BluetoothDevice device) { 205 if (DBG) { 206 log("getConnectionState(" + device + ")"); 207 } 208 final IBluetoothPbapClient service = getService(); 209 if (service != null && isEnabled() && isValidDevice(device)) { 210 try { 211 return service.getConnectionState(device); 212 } catch (RemoteException e) { 213 Log.e(TAG, Log.getStackTraceString(new Throwable())); 214 return BluetoothProfile.STATE_DISCONNECTED; 215 } 216 } 217 if (service == null) { 218 Log.w(TAG, "Proxy not attached to service"); 219 } 220 return BluetoothProfile.STATE_DISCONNECTED; 221 } 222 log(String msg)223 private static void log(String msg) { 224 Log.d(TAG, msg); 225 } 226 isEnabled()227 private boolean isEnabled() { 228 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 229 if (adapter != null && adapter.getState() == BluetoothAdapter.STATE_ON) { 230 return true; 231 } 232 log("Bluetooth is Not enabled"); 233 return false; 234 } 235 isValidDevice(BluetoothDevice device)236 private static boolean isValidDevice(BluetoothDevice device) { 237 return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); 238 } 239 240 /** 241 * Set priority of the profile 242 * 243 * <p> The device should already be paired. 244 * Priority can be one of {@link #PRIORITY_ON} or 245 * {@link #PRIORITY_OFF}, 246 * 247 * @param device Paired bluetooth device 248 * @param priority Priority of this profile 249 * @return true if priority is set, false on error 250 */ setPriority(BluetoothDevice device, int priority)251 public boolean setPriority(BluetoothDevice device, int priority) { 252 if (DBG) { 253 log("setPriority(" + device + ", " + priority + ")"); 254 } 255 final IBluetoothPbapClient service = getService(); 256 if (service != null && isEnabled() && isValidDevice(device)) { 257 if (priority != BluetoothProfile.PRIORITY_OFF 258 && priority != BluetoothProfile.PRIORITY_ON) { 259 return false; 260 } 261 try { 262 return service.setPriority(device, priority); 263 } catch (RemoteException e) { 264 Log.e(TAG, Log.getStackTraceString(new Throwable())); 265 return false; 266 } 267 } 268 if (service == null) { 269 Log.w(TAG, "Proxy not attached to service"); 270 } 271 return false; 272 } 273 274 /** 275 * Get the priority of the profile. 276 * 277 * <p> The priority can be any of: 278 * {@link #PRIORITY_AUTO_CONNECT}, {@link #PRIORITY_OFF}, 279 * {@link #PRIORITY_ON}, {@link #PRIORITY_UNDEFINED} 280 * 281 * @param device Bluetooth device 282 * @return priority of the device 283 */ getPriority(BluetoothDevice device)284 public int getPriority(BluetoothDevice device) { 285 if (VDBG) { 286 log("getPriority(" + device + ")"); 287 } 288 final IBluetoothPbapClient service = getService(); 289 if (service != null && isEnabled() && isValidDevice(device)) { 290 try { 291 return service.getPriority(device); 292 } catch (RemoteException e) { 293 Log.e(TAG, Log.getStackTraceString(new Throwable())); 294 return PRIORITY_OFF; 295 } 296 } 297 if (service == null) { 298 Log.w(TAG, "Proxy not attached to service"); 299 } 300 return PRIORITY_OFF; 301 } 302 } 303