2 * Copyright (C) 2008 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package android.bluetooth;
19 import android.annotation.SdkConstant;
20 import android.annotation.SdkConstant.SdkConstantType;
21 import android.content.ComponentName;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.ServiceConnection;
25 import android.os.IBinder;
26 import android.os.RemoteException;
27 import android.os.ServiceManager;
28 import android.util.Log;
30 import java.util.ArrayList;
31 import java.util.List;
34 * This class provides the APIs to control the Bluetooth Pan
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.
41 *<p>Each method is protected with its appropriate permission.
44 public final class BluetoothPan implements BluetoothProfile {
45 private static final String TAG = "BluetoothPan";
46 private static final boolean DBG = true;
47 private static final boolean VDBG = false;
50 * Intent used to broadcast the change in connection state of the Pan
53 * <p>This intent will have 4 extras:
55 * <li> {@link #EXTRA_STATE} - The current state of the profile. </li>
56 * <li> {@link #EXTRA_PREVIOUS_STATE}- The previous state of the profile.</li>
57 * <li> {@link BluetoothDevice#EXTRA_DEVICE} - The remote device. </li>
58 * <li> {@link #EXTRA_LOCAL_ROLE} - Which local role the remote device is
62 * <p>{@link #EXTRA_STATE} or {@link #EXTRA_PREVIOUS_STATE} can be any of
63 * {@link #STATE_DISCONNECTED}, {@link #STATE_CONNECTING},
64 * {@link #STATE_CONNECTED}, {@link #STATE_DISCONNECTING}.
66 * <p> {@link #EXTRA_LOCAL_ROLE} can be one of {@link #LOCAL_NAP_ROLE} or
67 * {@link #LOCAL_PANU_ROLE}
68 * <p>Requires {@link android.Manifest.permission#BLUETOOTH} permission to
71 @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
72 public static final String ACTION_CONNECTION_STATE_CHANGED =
73 "android.bluetooth.pan.profile.action.CONNECTION_STATE_CHANGED";
76 * Extra for {@link #ACTION_CONNECTION_STATE_CHANGED} intent
77 * The local role of the PAN profile that the remote device is bound to.
78 * It can be one of {@link #LOCAL_NAP_ROLE} or {@link #LOCAL_PANU_ROLE}.
80 public static final String EXTRA_LOCAL_ROLE = "android.bluetooth.pan.extra.LOCAL_ROLE";
82 public static final int PAN_ROLE_NONE = 0;
84 * The local device is acting as a Network Access Point.
86 public static final int LOCAL_NAP_ROLE = 1;
87 public static final int REMOTE_NAP_ROLE = 1;
90 * The local device is acting as a PAN User.
92 public static final int LOCAL_PANU_ROLE = 2;
93 public static final int REMOTE_PANU_ROLE = 2;
96 * Return codes for the connect and disconnect Bluez / Dbus calls.
99 public static final int PAN_DISCONNECT_FAILED_NOT_CONNECTED = 1000;
104 public static final int PAN_CONNECT_FAILED_ALREADY_CONNECTED = 1001;
109 public static final int PAN_CONNECT_FAILED_ATTEMPT_FAILED = 1002;
114 public static final int PAN_OPERATION_GENERIC_FAILURE = 1003;
119 public static final int PAN_OPERATION_SUCCESS = 1004;
121 private Context mContext;
122 private ServiceListener mServiceListener;
123 private BluetoothAdapter mAdapter;
124 private IBluetoothPan mPanService;
127 * Create a BluetoothPan proxy object for interacting with the local
128 * Bluetooth Service which handles the Pan profile
131 /*package*/ BluetoothPan(Context context, ServiceListener l) {
133 mServiceListener = l;
134 mAdapter = BluetoothAdapter.getDefaultAdapter();
136 mAdapter.getBluetoothManager().registerStateChangeCallback(mStateChangeCallback);
137 } catch (RemoteException re) {
138 Log.w(TAG,"Unable to register BluetoothStateChangeCallback",re);
140 if (VDBG) Log.d(TAG, "BluetoothPan() call bindService");
142 if (VDBG) Log.d(TAG, "BluetoothPan(), bindService called");
146 Intent intent = new Intent(IBluetoothPan.class.getName());
147 ComponentName comp = intent.resolveSystemService(mContext.getPackageManager(), 0);
148 intent.setComponent(comp);
149 if (comp == null || !mContext.bindService(intent, mConnection, 0)) {
150 Log.e(TAG, "Could not bind to Bluetooth Pan Service with " + intent);
156 /*package*/ void close() {
157 if (VDBG) log("close()");
159 IBluetoothManager mgr = mAdapter.getBluetoothManager();
162 mgr.unregisterStateChangeCallback(mStateChangeCallback);
163 } catch (RemoteException re) {
164 Log.w(TAG,"Unable to unregister BluetoothStateChangeCallback",re);
168 synchronized (mConnection) {
169 if (mPanService != null) {
172 mContext.unbindService(mConnection);
173 } catch (Exception re) {
178 mServiceListener = null;
181 protected void finalize() {
185 final private IBluetoothStateChangeCallback mStateChangeCallback = new IBluetoothStateChangeCallback.Stub() {
188 public void onBluetoothStateChange(boolean on) throws RemoteException {
189 //Handle enable request to bind again.
191 Log.d(TAG, "onBluetoothStateChange(on) call bindService");
193 if (VDBG) Log.d(TAG, "BluetoothPan(), bindService called");
195 if (VDBG) Log.d(TAG,"Unbinding service...");
196 synchronized (mConnection) {
199 mContext.unbindService(mConnection);
200 } catch (Exception re) {
209 * Initiate connection to a profile of the remote bluetooth device.
211 * <p> This API returns false in scenarios like the profile on the
212 * device is already connected or Bluetooth is not turned on.
213 * When this API returns true, it is guaranteed that
214 * connection state intent for the profile will be broadcasted with
215 * the state. Users can get the connection state of the profile
218 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
221 * @param device Remote Bluetooth Device
222 * @return false on immediate error,
226 public boolean connect(BluetoothDevice device) {
227 if (DBG) log("connect(" + device + ")");
228 if (mPanService != null && isEnabled() &&
229 isValidDevice(device)) {
231 return mPanService.connect(device);
232 } catch (RemoteException e) {
233 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
237 if (mPanService == null) Log.w(TAG, "Proxy not attached to service");
242 * Initiate disconnection from a profile
244 * <p> This API will return false in scenarios like the profile on the
245 * Bluetooth device is not in connected state etc. When this API returns,
246 * true, it is guaranteed that the connection state change
247 * intent will be broadcasted with the state. Users can get the
248 * disconnection state of the profile from this intent.
250 * <p> If the disconnection is initiated by a remote device, the state
251 * will transition from {@link #STATE_CONNECTED} to
252 * {@link #STATE_DISCONNECTED}. If the disconnect is initiated by the
253 * host (local) device the state will transition from
254 * {@link #STATE_CONNECTED} to state {@link #STATE_DISCONNECTING} to
255 * state {@link #STATE_DISCONNECTED}. The transition to
256 * {@link #STATE_DISCONNECTING} can be used to distinguish between the
259 * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
262 * @param device Remote Bluetooth Device
263 * @return false on immediate error,
267 public boolean disconnect(BluetoothDevice device) {
268 if (DBG) log("disconnect(" + device + ")");
269 if (mPanService != null && isEnabled() &&
270 isValidDevice(device)) {
272 return mPanService.disconnect(device);
273 } catch (RemoteException e) {
274 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
278 if (mPanService == null) Log.w(TAG, "Proxy not attached to service");
285 public List<BluetoothDevice> getConnectedDevices() {
286 if (VDBG) log("getConnectedDevices()");
287 if (mPanService != null && isEnabled()) {
289 return mPanService.getConnectedDevices();
290 } catch (RemoteException e) {
291 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
292 return new ArrayList<BluetoothDevice>();
295 if (mPanService == null) Log.w(TAG, "Proxy not attached to service");
296 return new ArrayList<BluetoothDevice>();
302 public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
303 if (VDBG) log("getDevicesMatchingStates()");
304 if (mPanService != null && isEnabled()) {
306 return mPanService.getDevicesMatchingConnectionStates(states);
307 } catch (RemoteException e) {
308 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
309 return new ArrayList<BluetoothDevice>();
312 if (mPanService == null) Log.w(TAG, "Proxy not attached to service");
313 return new ArrayList<BluetoothDevice>();
319 public int getConnectionState(BluetoothDevice device) {
320 if (VDBG) log("getState(" + device + ")");
321 if (mPanService != null && isEnabled()
322 && isValidDevice(device)) {
324 return mPanService.getConnectionState(device);
325 } catch (RemoteException e) {
326 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
327 return BluetoothProfile.STATE_DISCONNECTED;
330 if (mPanService == null) Log.w(TAG, "Proxy not attached to service");
331 return BluetoothProfile.STATE_DISCONNECTED;
334 public void setBluetoothTethering(boolean value) {
335 if (DBG) log("setBluetoothTethering(" + value + ")");
337 mPanService.setBluetoothTethering(value);
338 } catch (RemoteException e) {
339 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
343 public boolean isTetheringOn() {
344 if (VDBG) log("isTetheringOn()");
346 return mPanService.isTetheringOn();
347 } catch (RemoteException e) {
348 Log.e(TAG, "Stack:" + Log.getStackTraceString(new Throwable()));
353 private final ServiceConnection mConnection = new ServiceConnection() {
354 public void onServiceConnected(ComponentName className, IBinder service) {
355 if (DBG) Log.d(TAG, "BluetoothPAN Proxy object connected");
356 mPanService = IBluetoothPan.Stub.asInterface(service);
358 if (mServiceListener != null) {
359 mServiceListener.onServiceConnected(BluetoothProfile.PAN,
363 public void onServiceDisconnected(ComponentName className) {
364 if (DBG) Log.d(TAG, "BluetoothPAN Proxy object disconnected");
366 if (mServiceListener != null) {
367 mServiceListener.onServiceDisconnected(BluetoothProfile.PAN);
372 private boolean isEnabled() {
373 if (mAdapter.getState() == BluetoothAdapter.STATE_ON) return true;
377 private boolean isValidDevice(BluetoothDevice device) {
378 if (device == null) return false;
380 if (BluetoothAdapter.checkBluetoothAddress(device.getAddress())) return true;
384 private static void log(String msg) {