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 17 package android.bluetooth; 18 19 import android.annotation.SdkConstant; 20 import android.annotation.SdkConstant.SdkConstantType; 21 import android.annotation.UnsupportedAppUsage; 22 import android.content.Context; 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 Pan 33 * Profile. 34 * 35 * <p>BluetoothPan is a proxy object for controlling the Bluetooth 36 * Service via IPC. Use {@link BluetoothAdapter#getProfileProxy} to get 37 * the BluetoothPan proxy object. 38 * 39 * <p>Each method is protected with its appropriate permission. 40 * 41 * @hide 42 */ 43 public final class BluetoothPan implements BluetoothProfile { 44 private static final String TAG = "BluetoothPan"; 45 private static final boolean DBG = true; 46 private static final boolean VDBG = false; 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 * 97 * @hide 98 */ 99 public static final int PAN_DISCONNECT_FAILED_NOT_CONNECTED = 1000; 100 101 /** 102 * @hide 103 */ 104 public static final int PAN_CONNECT_FAILED_ALREADY_CONNECTED = 1001; 105 106 /** 107 * @hide 108 */ 109 public static final int PAN_CONNECT_FAILED_ATTEMPT_FAILED = 1002; 110 111 /** 112 * @hide 113 */ 114 public static final int PAN_OPERATION_GENERIC_FAILURE = 1003; 115 116 /** 117 * @hide 118 */ 119 public static final int PAN_OPERATION_SUCCESS = 1004; 120 121 private BluetoothAdapter mAdapter; 122 private final BluetoothProfileConnector<IBluetoothPan> mProfileConnector = 123 new BluetoothProfileConnector(this, BluetoothProfile.PAN, 124 "BluetoothPan", IBluetoothPan.class.getName()) { 125 @Override 126 public IBluetoothPan getServiceInterface(IBinder service) { 127 return IBluetoothPan.Stub.asInterface(Binder.allowBlocking(service)); 128 } 129 }; 130 131 132 /** 133 * Create a BluetoothPan proxy object for interacting with the local 134 * Bluetooth Service which handles the Pan profile 135 */ 136 @UnsupportedAppUsage BluetoothPan(Context context, ServiceListener listener)137 /*package*/ BluetoothPan(Context context, ServiceListener listener) { 138 mAdapter = BluetoothAdapter.getDefaultAdapter(); 139 mProfileConnector.connect(context, listener); 140 } 141 142 @UnsupportedAppUsage close()143 /*package*/ void close() { 144 if (VDBG) log("close()"); 145 mProfileConnector.disconnect(); 146 } 147 getService()148 private IBluetoothPan getService() { 149 return mProfileConnector.getService(); 150 } 151 152 finalize()153 protected void finalize() { 154 close(); 155 } 156 157 /** 158 * Initiate connection to a profile of the remote bluetooth device. 159 * 160 * <p> This API returns false in scenarios like the profile on the 161 * device is already connected or Bluetooth is not turned on. 162 * When this API returns true, it is guaranteed that 163 * connection state intent for the profile will be broadcasted with 164 * the state. Users can get the connection state of the profile 165 * from this intent. 166 * 167 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} 168 * permission. 169 * 170 * @param device Remote Bluetooth Device 171 * @return false on immediate error, true otherwise 172 * @hide 173 */ 174 @UnsupportedAppUsage connect(BluetoothDevice device)175 public boolean connect(BluetoothDevice device) { 176 if (DBG) log("connect(" + device + ")"); 177 final IBluetoothPan service = getService(); 178 if (service != null && isEnabled() && isValidDevice(device)) { 179 try { 180 return service.connect(device); 181 } catch (RemoteException e) { 182 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 183 return false; 184 } 185 } 186 if (service == null) Log.w(TAG, "Proxy not attached to service"); 187 return false; 188 } 189 190 /** 191 * Initiate disconnection from a profile 192 * 193 * <p> This API will return false in scenarios like the profile on the 194 * Bluetooth device is not in connected state etc. When this API returns, 195 * true, it is guaranteed that the connection state change 196 * intent will be broadcasted with the state. Users can get the 197 * disconnection state of the profile from this intent. 198 * 199 * <p> If the disconnection is initiated by a remote device, the state 200 * will transition from {@link #STATE_CONNECTED} to 201 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the 202 * host (local) device the state will transition from 203 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to 204 * state {@link #STATE_DISCONNECTED}. The transition to 205 * {@link #STATE_DISCONNECTING} can be used to distinguish between the 206 * two scenarios. 207 * 208 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN} 209 * permission. 210 * 211 * @param device Remote Bluetooth Device 212 * @return false on immediate error, true otherwise 213 * @hide 214 */ 215 @UnsupportedAppUsage disconnect(BluetoothDevice device)216 public boolean disconnect(BluetoothDevice device) { 217 if (DBG) log("disconnect(" + device + ")"); 218 final IBluetoothPan service = getService(); 219 if (service != null && isEnabled() && isValidDevice(device)) { 220 try { 221 return service.disconnect(device); 222 } catch (RemoteException e) { 223 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 224 return false; 225 } 226 } 227 if (service == null) Log.w(TAG, "Proxy not attached to service"); 228 return false; 229 } 230 231 /** 232 * {@inheritDoc} 233 */ 234 @Override getConnectedDevices()235 public List<BluetoothDevice> getConnectedDevices() { 236 if (VDBG) log("getConnectedDevices()"); 237 final IBluetoothPan service = getService(); 238 if (service != null && isEnabled()) { 239 try { 240 return service.getConnectedDevices(); 241 } catch (RemoteException e) { 242 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 243 return new ArrayList<BluetoothDevice>(); 244 } 245 } 246 if (service == null) Log.w(TAG, "Proxy not attached to service"); 247 return new ArrayList<BluetoothDevice>(); 248 } 249 250 /** 251 * {@inheritDoc} 252 */ 253 @Override getDevicesMatchingConnectionStates(int[] states)254 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 255 if (VDBG) log("getDevicesMatchingStates()"); 256 final IBluetoothPan service = getService(); 257 if (service != null && isEnabled()) { 258 try { 259 return service.getDevicesMatchingConnectionStates(states); 260 } catch (RemoteException e) { 261 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 262 return new ArrayList<BluetoothDevice>(); 263 } 264 } 265 if (service == null) Log.w(TAG, "Proxy not attached to service"); 266 return new ArrayList<BluetoothDevice>(); 267 } 268 269 /** 270 * {@inheritDoc} 271 */ 272 @Override getConnectionState(BluetoothDevice device)273 public int getConnectionState(BluetoothDevice device) { 274 if (VDBG) log("getState(" + device + ")"); 275 final IBluetoothPan service = getService(); 276 if (service != null && isEnabled() && isValidDevice(device)) { 277 try { 278 return service.getConnectionState(device); 279 } catch (RemoteException e) { 280 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 281 return BluetoothProfile.STATE_DISCONNECTED; 282 } 283 } 284 if (service == null) Log.w(TAG, "Proxy not attached to service"); 285 return BluetoothProfile.STATE_DISCONNECTED; 286 } 287 288 @UnsupportedAppUsage setBluetoothTethering(boolean value)289 public void setBluetoothTethering(boolean value) { 290 if (DBG) log("setBluetoothTethering(" + value + ")"); 291 final IBluetoothPan service = getService(); 292 if (service != null && isEnabled()) { 293 try { 294 service.setBluetoothTethering(value); 295 } catch (RemoteException e) { 296 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 297 } 298 } 299 } 300 301 @UnsupportedAppUsage isTetheringOn()302 public boolean isTetheringOn() { 303 if (VDBG) log("isTetheringOn()"); 304 final IBluetoothPan service = getService(); 305 if (service != null && isEnabled()) { 306 try { 307 return service.isTetheringOn(); 308 } catch (RemoteException e) { 309 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable())); 310 } 311 } 312 return false; 313 } 314 315 @UnsupportedAppUsage isEnabled()316 private boolean isEnabled() { 317 return mAdapter.getState() == BluetoothAdapter.STATE_ON; 318 } 319 320 @UnsupportedAppUsage isValidDevice(BluetoothDevice device)321 private static boolean isValidDevice(BluetoothDevice device) { 322 return device != null && BluetoothAdapter.checkBluetoothAddress(device.getAddress()); 323 } 324 325 @UnsupportedAppUsage log(String msg)326 private static void log(String msg) { 327 Log.d(TAG, msg); 328 } 329 } 330