BluetoothPan.java revision 903ac6f399dcd4f574bf388daa7b5f5907d448d3
1/* 2 * Copyright (C) 2008 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 android.annotation.SdkConstant; 20import android.annotation.SdkConstant.SdkConstantType; 21import android.content.ComponentName; 22import android.content.Context; 23import android.content.Intent; 24import android.content.ServiceConnection; 25import android.os.IBinder; 26import android.os.RemoteException; 27import android.os.ServiceManager; 28import android.util.Log; 29 30import java.util.ArrayList; 31import java.util.List; 32 33/** 34 * This class provides the APIs to control the Bluetooth Pan 35 * Profile. 36 * 37 *<p>BluetoothPan is a proxy object for controlling the Bluetooth 38 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get 39 * the BluetoothPan proxy object. 40 * 41 *<p>Each method is protected with its appropriate permission. 42 *@hide 43 */ 44public final class BluetoothPan implements BluetoothProfile { 45 private static final String TAG = "BluetoothPan"; 46 private static final boolean DBG = true; 47 48 /** 49 * Intent used to broadcast the change in connection state of the Pan 50 * profile. 51 * 52 * <p>This intent will have 4 extras: 53 * <ul> 54 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li> 55 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li> 56 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li> 57 * <li> {@link #EXTRA_LOCAL_ROLE} - Which local role the remote device is 58 * bound to. </li> 59 * </ul> 60 * 61 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of 62 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING}, 63 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}. 64 * 65 * <p> {@link #EXTRA_LOCAL_ROLE} can be one of {@link #LOCAL_NAP_ROLE} or 66 * {@link #LOCAL_PANU_ROLE} 67 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to 68 * receive. 69 */ 70 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION) 71 public static final String ACTION_CONNECTION_STATE_CHANGED = 72 "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED"; 73 74 /** 75 * Extra for {@link #ACTION_CONNECTION_STATE_CHANGED} intent 76 * The local role of the PAN profile that the remote device is bound to. 77 * It can be one of {@link #LOCAL_NAP_ROLE} or {@link #LOCAL_PANU_ROLE}. 78 */ 79 public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE"; 80 81 public static final int PAN_ROLE_NONE = 0; 82 /** 83 * The local device is acting as a Network Access Point. 84 */ 85 public static final int LOCAL_NAP_ROLE = 1; 86 public static final int REMOTE_NAP_ROLE = 1; 87 88 /** 89 * The local device is acting as a PAN User. 90 */ 91 public static final int LOCAL_PANU_ROLE = 2; 92 public static final int REMOTE_PANU_ROLE = 2; 93 94 /** 95 * Return codes for the connect and disconnect Bluez / Dbus calls. 96 * @hide 97 */ 98 public static final int PAN_DISCONNECT_FAILED_NOT_CONNECTED = 1000; 99 100 /** 101 * @hide 102 */ 103 public static final int PAN_CONNECT_FAILED_ALREADY_CONNECTED = 1001; 104 105 /** 106 * @hide 107 */ 108 public static final int PAN_CONNECT_FAILED_ATTEMPT_FAILED = 1002; 109 110 /** 111 * @hide 112 */ 113 public static final int PAN_OPERATION_GENERIC_FAILURE = 1003; 114 115 /** 116 * @hide 117 */ 118 public static final int PAN_OPERATION_SUCCESS = 1004; 119 120 private Context mContext; 121 private ServiceListener mServiceListener; 122 private BluetoothAdapter mAdapter; 123 private IBluetoothPan mPanService; 124 125 /** 126 * Create a BluetoothPan proxy object for interacting with the local 127 * Bluetooth Service which handles the Pan profile 128 * 129 */ 130 /*package*/ BluetoothPan(Context context, ServiceListener l) { 131 mContext = context; 132 mServiceListener = l; 133 mAdapter = BluetoothAdapter.getDefaultAdapter(); 134 try { 135 mAdapter.getBluetoothManager().registerStateChangeCallback(mStateChangeCallback); 136 } catch (RemoteException re) { 137 Log.w(TAG,"Unable to register BluetoothStateChangeCallback",re); 138 } 139 Log.d(TAG, "BluetoothPan() call bindService"); 140 if (!context.bindService(new Intent(IBluetoothPan.class.getName()), 141 mConnection, 0)) { 142 Log.e(TAG, "Could not bind to Bluetooth HID Service"); 143 } 144 Log.d(TAG, "BluetoothPan(), bindService called"); 145 } 146 147 /*package*/ void close() { 148 if (DBG) log("close()"); 149 if (mConnection != null) { 150 mContext.unbindService(mConnection); 151 mConnection = null; 152 } 153 mServiceListener = null; 154 try { 155 mAdapter.getBluetoothManager().unregisterStateChangeCallback(mStateChangeCallback); 156 } catch (RemoteException re) { 157 Log.w(TAG,"Unable to register BluetoothStateChangeCallback",re); 158 } 159 } 160 161 protected void finalize() { 162 close(); 163 } 164 165 private IBluetoothStateChangeCallback mStateChangeCallback = new IBluetoothStateChangeCallback.Stub() { 166 167 @Override 168 public void onBluetoothStateChange(boolean on) throws RemoteException { 169 //Handle enable request to bind again. 170 if (on) { 171 Log.d(TAG, "onBluetoothStateChange(on) call bindService"); 172 if (!mContext.bindService(new Intent(IBluetoothPan.class.getName()), 173 mConnection, 0)) { 174 Log.e(TAG, "Could not bind to Bluetooth HID Service"); 175 } 176 Log.d(TAG, "BluetoothPan(), bindService called"); 177 } else { 178 if (DBG) Log.d(TAG,"Unbinding service..."); 179 synchronized (mConnection) { 180 try { 181 mPanService = null; 182 mContext.unbindService(mConnection); 183 } catch (Exception re) { 184 Log.e(TAG,"",re); 185 } 186 } 187 } 188 } 189 }; 190 191 /** 192 * Initiate connection to a profile of the remote bluetooth device. 193 * 194 * <p> This API returns false in scenarios like the profile on the 195 * device is already connected or Bluetooth is not turned on. 196 * When this API returns true, it is guaranteed that 197 * connection state intent for the profile will be broadcasted with 198 * the state. Users can get the connection state of the profile 199 * from this intent. 200 * 201 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} 202 * permission. 203 * 204 * @param device Remote Bluetooth Device 205 * @return false on immediate error, 206 * true otherwise 207 * @hide 208 */ 209 public boolean connect(BluetoothDevice device) { 210 if (DBG) log("connect(" + device + ")"); 211 if (mPanService != null && isEnabled() && 212 isValidDevice(device)) { 213 try { 214 return mPanService.connect(device); 215 } catch (RemoteException e) { 216 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 217 return false; 218 } 219 } 220 if (mPanService == null) Log.w(TAG, "Proxy not attached to service"); 221 return false; 222 } 223 224 /** 225 * Initiate disconnection from a profile 226 * 227 * <p> This API will return false in scenarios like the profile on the 228 * Bluetooth device is not in connected state etc. When this API returns, 229 * true, it is guaranteed that the connection state change 230 * intent will be broadcasted with the state. Users can get the 231 * disconnection state of the profile from this intent. 232 * 233 * <p> If the disconnection is initiated by a remote device, the state 234 * will transition from {@link #STATE_CONNECTED} to 235 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the 236 * host (local) device the state will transition from 237 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to 238 * state {@link #STATE_DISCONNECTED}. The transition to 239 * {@link #STATE_DISCONNECTING} can be used to distinguish between the 240 * two scenarios. 241 * 242 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} 243 * permission. 244 * 245 * @param device Remote Bluetooth Device 246 * @return false on immediate error, 247 * true otherwise 248 * @hide 249 */ 250 public boolean disconnect(BluetoothDevice device) { 251 if (DBG) log("disconnect(" + device + ")"); 252 if (mPanService != null && isEnabled() && 253 isValidDevice(device)) { 254 try { 255 return mPanService.disconnect(device); 256 } catch (RemoteException e) { 257 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 258 return false; 259 } 260 } 261 if (mPanService == null) Log.w(TAG, "Proxy not attached to service"); 262 return false; 263 } 264 265 /** 266 * {@inheritDoc} 267 */ 268 public List<BluetoothDevice> getConnectedDevices() { 269 if (DBG) log("getConnectedDevices()"); 270 if (mPanService != null && isEnabled()) { 271 try { 272 return mPanService.getConnectedDevices(); 273 } catch (RemoteException e) { 274 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 275 return new ArrayList<BluetoothDevice>(); 276 } 277 } 278 if (mPanService == null) Log.w(TAG, "Proxy not attached to service"); 279 return new ArrayList<BluetoothDevice>(); 280 } 281 282 /** 283 * {@inheritDoc} 284 */ 285 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 286 if (DBG) log("getDevicesMatchingStates()"); 287 if (mPanService != null && isEnabled()) { 288 try { 289 return mPanService.getDevicesMatchingConnectionStates(states); 290 } catch (RemoteException e) { 291 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 292 return new ArrayList<BluetoothDevice>(); 293 } 294 } 295 if (mPanService == null) Log.w(TAG, "Proxy not attached to service"); 296 return new ArrayList<BluetoothDevice>(); 297 } 298 299 /** 300 * {@inheritDoc} 301 */ 302 public int getConnectionState(BluetoothDevice device) { 303 if (DBG) log("getState(" + device + ")"); 304 if (mPanService != null && isEnabled() 305 && isValidDevice(device)) { 306 try { 307 return mPanService.getConnectionState(device); 308 } catch (RemoteException e) { 309 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 310 return BluetoothProfile.STATE_DISCONNECTED; 311 } 312 } 313 if (mPanService == null) Log.w(TAG, "Proxy not attached to service"); 314 return BluetoothProfile.STATE_DISCONNECTED; 315 } 316 317 public void setBluetoothTethering(boolean value) { 318 if (DBG) log("setBluetoothTethering(" + value + ")"); 319 try { 320 mPanService.setBluetoothTethering(value); 321 } catch (RemoteException e) { 322 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 323 } 324 } 325 326 public boolean isTetheringOn() { 327 if (DBG) log("isTetheringOn()"); 328 try { 329 return mPanService.isTetheringOn(); 330 } catch (RemoteException e) { 331 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 332 } 333 return false; 334 } 335 336 private ServiceConnection mConnection = new ServiceConnection() { 337 public void onServiceConnected(ComponentName className, IBinder service) { 338 if (DBG) Log.d(TAG, "BluetoothPAN Proxy object connected"); 339 mPanService = IBluetoothPan.Stub.asInterface(service); 340 341 if (mServiceListener != null) { 342 mServiceListener.onServiceConnected(BluetoothProfile.PAN, 343 BluetoothPan.this); 344 } 345 } 346 public void onServiceDisconnected(ComponentName className) { 347 if (DBG) Log.d(TAG, "BluetoothPAN Proxy object disconnected"); 348 mPanService = null; 349 if (mServiceListener != null) { 350 mServiceListener.onServiceDisconnected(BluetoothProfile.PAN); 351 } 352 } 353 }; 354 355 private boolean isEnabled() { 356 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true; 357 return false; 358 } 359 360 private boolean isValidDevice(BluetoothDevice device) { 361 if (device == null) return false; 362 363 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true; 364 return false; 365 } 366 367 private static void log(String msg) { 368 Log.d(TAG, msg); 369 } 370} 371