summaryrefslogtreecommitdiffstats
path: root/core/java/android/bluetooth
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/bluetooth')
-rw-r--r--core/java/android/bluetooth/BluetoothA2dp.java188
-rw-r--r--core/java/android/bluetooth/BluetoothAdapter.java849
-rw-r--r--core/java/android/bluetooth/BluetoothAudioGateway.java60
-rw-r--r--core/java/android/bluetooth/BluetoothClass.java276
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.aidl19
-rw-r--r--core/java/android/bluetooth/BluetoothDevice.java943
-rw-r--r--core/java/android/bluetooth/BluetoothDevicePicker.java66
-rw-r--r--core/java/android/bluetooth/BluetoothHeadset.java103
-rw-r--r--core/java/android/bluetooth/BluetoothInputStream.java44
-rw-r--r--core/java/android/bluetooth/BluetoothIntent.java145
-rw-r--r--core/java/android/bluetooth/BluetoothOutputStream.java34
-rw-r--r--core/java/android/bluetooth/BluetoothPbap.java257
-rw-r--r--core/java/android/bluetooth/BluetoothServerSocket.java134
-rw-r--r--core/java/android/bluetooth/BluetoothSocket.java355
-rw-r--r--core/java/android/bluetooth/BluetoothUuid.java153
-rw-r--r--core/java/android/bluetooth/HeadsetBase.java38
-rw-r--r--core/java/android/bluetooth/IBluetooth.aidl (renamed from core/java/android/bluetooth/IBluetoothDevice.aidl)42
-rw-r--r--core/java/android/bluetooth/IBluetoothA2dp.aidl16
-rw-r--r--core/java/android/bluetooth/IBluetoothCallback.aidl (renamed from core/java/android/bluetooth/IBluetoothDeviceCallback.aidl)8
-rw-r--r--core/java/android/bluetooth/IBluetoothHeadset.aidl12
-rw-r--r--core/java/android/bluetooth/IBluetoothPbap.aidl (renamed from core/java/android/bluetooth/BluetoothError.java)30
-rw-r--r--core/java/android/bluetooth/ScoSocket.java2
-rw-r--r--core/java/android/bluetooth/package.html112
23 files changed, 2876 insertions, 1010 deletions
diff --git a/core/java/android/bluetooth/BluetoothA2dp.java b/core/java/android/bluetooth/BluetoothA2dp.java
index 2ea45d5..e8a69d8 100644
--- a/core/java/android/bluetooth/BluetoothA2dp.java
+++ b/core/java/android/bluetooth/BluetoothA2dp.java
@@ -25,7 +25,10 @@ import android.os.RemoteException;
import android.os.IBinder;
import android.util.Log;
-import java.util.List;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.Set;
+import java.util.HashSet;
/**
* Public API for controlling the Bluetooth A2DP Profile Service.
@@ -39,32 +42,30 @@ import java.util.List;
*
* Currently the BluetoothA2dp service runs in the system server and this
* proxy object will be immediately bound to the service on construction.
- * However this may change in future releases, and error codes such as
- * BluetoothError.ERROR_IPC_NOT_READY will be returned from this API when the
- * proxy object is not yet attached.
*
* Currently this class provides methods to connect to A2DP audio sinks.
*
* @hide
*/
-public class BluetoothA2dp {
+public final class BluetoothA2dp {
private static final String TAG = "BluetoothA2dp";
private static final boolean DBG = false;
- /** int extra for SINK_STATE_CHANGED_ACTION */
- public static final String SINK_STATE =
- "android.bluetooth.a2dp.intent.SINK_STATE";
- /** int extra for SINK_STATE_CHANGED_ACTION */
- public static final String SINK_PREVIOUS_STATE =
- "android.bluetooth.a2dp.intent.SINK_PREVIOUS_STATE";
+ /** int extra for ACTION_SINK_STATE_CHANGED */
+ public static final String EXTRA_SINK_STATE =
+ "android.bluetooth.a2dp.extra.SINK_STATE";
+ /** int extra for ACTION_SINK_STATE_CHANGED */
+ public static final String EXTRA_PREVIOUS_SINK_STATE =
+ "android.bluetooth.a2dp.extra.PREVIOUS_SINK_STATE";
/** Indicates the state of an A2DP audio sink has changed.
- * This intent will always contain SINK_STATE, SINK_PREVIOUS_STATE and
- * BluetoothIntent.ADDRESS extras.
+ * This intent will always contain EXTRA_SINK_STATE,
+ * EXTRA_PREVIOUS_SINK_STATE and BluetoothDevice.EXTRA_DEVICE
+ * extras.
*/
@SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String SINK_STATE_CHANGED_ACTION =
- "android.bluetooth.a2dp.intent.action.SINK_STATE_CHANGED";
+ public static final String ACTION_SINK_STATE_CHANGED =
+ "android.bluetooth.a2dp.action.SINK_STATE_CHANGED";
public static final int STATE_DISCONNECTED = 0;
public static final int STATE_CONNECTING = 1;
@@ -79,6 +80,7 @@ public class BluetoothA2dp {
/** Default priority for a2dp devices that should not allow incoming
* connections */
public static final int PRIORITY_OFF = 0;
+
private final IBluetoothA2dp mService;
private final Context mContext;
@@ -89,84 +91,123 @@ public class BluetoothA2dp {
*/
public BluetoothA2dp(Context c) {
mContext = c;
+
IBinder b = ServiceManager.getService(BluetoothA2dpService.BLUETOOTH_A2DP_SERVICE);
- if (b == null) {
- throw new RuntimeException("Bluetooth A2DP service not available!");
+ if (b != null) {
+ mService = IBluetoothA2dp.Stub.asInterface(b);
+ } else {
+ Log.w(TAG, "Bluetooth A2DP service not available!");
+
+ // Instead of throwing an exception which prevents people from going
+ // into Wireless settings in the emulator. Let it crash later when it is actually used.
+ mService = null;
}
- mService = IBluetoothA2dp.Stub.asInterface(b);
}
/** Initiate a connection to an A2DP sink.
* Listen for SINK_STATE_CHANGED_ACTION to find out when the
* connection is completed.
- * @param address Remote BT address.
- * @return Result code, negative indicates an immediate error.
+ * @param device Remote BT device.
+ * @return false on immediate error, true otherwise
* @hide
*/
- public int connectSink(String address) {
- if (DBG) log("connectSink(" + address + ")");
+ public boolean connectSink(BluetoothDevice device) {
+ if (DBG) log("connectSink(" + device + ")");
try {
- return mService.connectSink(address);
+ return mService.connectSink(device);
} catch (RemoteException e) {
- Log.w(TAG, "", e);
- return BluetoothError.ERROR_IPC;
+ Log.e(TAG, "", e);
+ return false;
}
}
/** Initiate disconnect from an A2DP sink.
* Listen for SINK_STATE_CHANGED_ACTION to find out when
* disconnect is completed.
- * @param address Remote BT address.
- * @return Result code, negative indicates an immediate error.
+ * @param device Remote BT device.
+ * @return false on immediate error, true otherwise
+ * @hide
+ */
+ public boolean disconnectSink(BluetoothDevice device) {
+ if (DBG) log("disconnectSink(" + device + ")");
+ try {
+ return mService.disconnectSink(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ return false;
+ }
+ }
+
+ /** Initiate suspend from an A2DP sink.
+ * Listen for SINK_STATE_CHANGED_ACTION to find out when
+ * suspend is completed.
+ * @param device Remote BT device.
+ * @return false on immediate error, true otherwise
+ * @hide
+ */
+ public boolean suspendSink(BluetoothDevice device) {
+ try {
+ return mService.suspendSink(device);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ return false;
+ }
+ }
+
+ /** Initiate resume from an suspended A2DP sink.
+ * Listen for SINK_STATE_CHANGED_ACTION to find out when
+ * resume is completed.
+ * @param device Remote BT device.
+ * @return false on immediate error, true otherwise
* @hide
*/
- public int disconnectSink(String address) {
- if (DBG) log("disconnectSink(" + address + ")");
+ public boolean resumeSink(BluetoothDevice device) {
try {
- return mService.disconnectSink(address);
+ return mService.resumeSink(device);
} catch (RemoteException e) {
- Log.w(TAG, "", e);
- return BluetoothError.ERROR_IPC;
+ Log.e(TAG, "", e);
+ return false;
}
}
/** Check if a specified A2DP sink is connected.
- * @param address Remote BT address.
+ * @param device Remote BT device.
* @return True if connected (or playing), false otherwise and on error.
* @hide
*/
- public boolean isSinkConnected(String address) {
- if (DBG) log("isSinkConnected(" + address + ")");
- int state = getSinkState(address);
+ public boolean isSinkConnected(BluetoothDevice device) {
+ if (DBG) log("isSinkConnected(" + device + ")");
+ int state = getSinkState(device);
return state == STATE_CONNECTED || state == STATE_PLAYING;
}
/** Check if any A2DP sink is connected.
- * @return a List of connected A2DP sinks, or null on error.
+ * @return a unmodifiable set of connected A2DP sinks, or null on error.
* @hide
*/
- public List<String> listConnectedSinks() {
- if (DBG) log("listConnectedSinks()");
+ public Set<BluetoothDevice> getConnectedSinks() {
+ if (DBG) log("getConnectedSinks()");
try {
- return mService.listConnectedSinks();
+ return Collections.unmodifiableSet(
+ new HashSet<BluetoothDevice>(Arrays.asList(mService.getConnectedSinks())));
} catch (RemoteException e) {
- Log.w(TAG, "", e);
+ Log.e(TAG, "", e);
return null;
}
}
/** Get the state of an A2DP sink
- * @param address Remote BT address.
- * @return State code, or negative on error
+ * @param device Remote BT device.
+ * @return State code, one of STATE_
* @hide
*/
- public int getSinkState(String address) {
- if (DBG) log("getSinkState(" + address + ")");
+ public int getSinkState(BluetoothDevice device) {
+ if (DBG) log("getSinkState(" + device + ")");
try {
- return mService.getSinkState(address);
+ return mService.getSinkState(device);
} catch (RemoteException e) {
- Log.w(TAG, "", e);
- return BluetoothError.ERROR_IPC;
+ Log.e(TAG, "", e);
+ return BluetoothA2dp.STATE_DISCONNECTED;
}
}
@@ -177,58 +218,33 @@ public class BluetoothA2dp {
* Sinks with priority greater than zero will accept incoming connections
* (if no sink is currently connected).
* Priority for unpaired sink must be PRIORITY_NONE.
- * @param address Paired sink
+ * @param device Paired sink
* @param priority Integer priority, for example PRIORITY_AUTO or
* PRIORITY_NONE
- * @return Result code, negative indicates an error
+ * @return true if priority is set, false on error
*/
- public int setSinkPriority(String address, int priority) {
- if (DBG) log("setSinkPriority(" + address + ", " + priority + ")");
+ public boolean setSinkPriority(BluetoothDevice device, int priority) {
+ if (DBG) log("setSinkPriority(" + device + ", " + priority + ")");
try {
- return mService.setSinkPriority(address, priority);
+ return mService.setSinkPriority(device, priority);
} catch (RemoteException e) {
- Log.w(TAG, "", e);
- return BluetoothError.ERROR_IPC;
+ Log.e(TAG, "", e);
+ return false;
}
}
/**
* Get priority of a2dp sink.
- * @param address Sink
+ * @param device Sink
* @return non-negative priority, or negative error code on error.
*/
- public int getSinkPriority(String address) {
- if (DBG) log("getSinkPriority(" + address + ")");
+ public int getSinkPriority(BluetoothDevice device) {
+ if (DBG) log("getSinkPriority(" + device + ")");
try {
- return mService.getSinkPriority(address);
+ return mService.getSinkPriority(device);
} catch (RemoteException e) {
- Log.w(TAG, "", e);
- return BluetoothError.ERROR_IPC;
- }
- }
-
- /**
- * Check class bits for possible A2DP Sink support.
- * This is a simple heuristic that tries to guess if a device with the
- * given class bits might be a A2DP Sink. It is not accurate for all
- * devices. It tries to err on the side of false positives.
- * @return True if this device might be a A2DP sink
- */
- public static boolean doesClassMatchSink(int btClass) {
- if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) {
- return true;
- }
- // By the A2DP spec, sinks must indicate the RENDER service.
- // However we found some that do not (Chordette). So lets also
- // match on some other class bits.
- switch (BluetoothClass.Device.getDevice(btClass)) {
- case BluetoothClass.Device.AUDIO_VIDEO_HIFI_AUDIO:
- case BluetoothClass.Device.AUDIO_VIDEO_HEADPHONES:
- case BluetoothClass.Device.AUDIO_VIDEO_LOUDSPEAKER:
- case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
- return true;
- default:
- return false;
+ Log.e(TAG, "", e);
+ return PRIORITY_OFF;
}
}
diff --git a/core/java/android/bluetooth/BluetoothAdapter.java b/core/java/android/bluetooth/BluetoothAdapter.java
new file mode 100644
index 0000000..bd5b07c
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothAdapter.java
@@ -0,0 +1,849 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.Random;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Represents the local device Bluetooth adapter. The {@link BluetoothAdapter}
+ * lets you perform fundamental Bluetooth tasks, such as initiate
+ * device discovery, query a list of bonded (paired) devices,
+ * instantiate a {@link BluetoothDevice} using a known MAC address, and create
+ * a {@link BluetoothServerSocket} to listen for connection requests from other
+ * devices.
+ *
+ * <p>To get a {@link BluetoothAdapter} representing the local Bluetooth
+ * adapter, call the static {@link #getDefaultAdapter} method.
+ * Fundamentally, this is your starting point for all
+ * Bluetooth actions. Once you have the local adapter, you can get a set of
+ * {@link BluetoothDevice} objects representing all paired devices with
+ * {@link #getBondedDevices()}; start device discovery with
+ * {@link #startDiscovery()}; or create a {@link BluetoothServerSocket} to
+ * listen for incoming connection requests with
+ * {@link #listenUsingRfcommWithServiceRecord(String,UUID)}.
+ *
+ * <p class="note"><strong>Note:</strong>
+ * Most methods require the {@link android.Manifest.permission#BLUETOOTH}
+ * permission and some also require the
+ * {@link android.Manifest.permission#BLUETOOTH_ADMIN} permission.
+ *
+ * {@see BluetoothDevice}
+ * {@see BluetoothServerSocket}
+ */
+public final class BluetoothAdapter {
+ private static final String TAG = "BluetoothAdapter";
+ private static final boolean DBG = false;
+
+ /**
+ * Sentinel error value for this class. Guaranteed to not equal any other
+ * integer constant in this class. Provided as a convenience for functions
+ * that require a sentinel error value, for example:
+ * <p><code>Intent.getIntExtra(BluetoothAdapter.EXTRA_STATE,
+ * BluetoothAdapter.ERROR)</code>
+ */
+ public static final int ERROR = Integer.MIN_VALUE;
+
+ /**
+ * Broadcast Action: The state of the local Bluetooth adapter has been
+ * changed.
+ * <p>For example, Bluetooth has been turned on or off.
+ * <p>Always contains the extra fields {@link #EXTRA_STATE} and {@link
+ * #EXTRA_PREVIOUS_STATE} containing the new and old states
+ * respectively.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_STATE_CHANGED =
+ "android.bluetooth.adapter.action.STATE_CHANGED";
+
+ /**
+ * Used as an int extra field in {@link #ACTION_STATE_CHANGED}
+ * intents to request the current power state. Possible values are:
+ * {@link #STATE_OFF},
+ * {@link #STATE_TURNING_ON},
+ * {@link #STATE_ON},
+ * {@link #STATE_TURNING_OFF},
+ */
+ public static final String EXTRA_STATE =
+ "android.bluetooth.adapter.extra.STATE";
+ /**
+ * Used as an int extra field in {@link #ACTION_STATE_CHANGED}
+ * intents to request the previous power state. Possible values are:
+ * {@link #STATE_OFF},
+ * {@link #STATE_TURNING_ON},
+ * {@link #STATE_ON},
+ * {@link #STATE_TURNING_OFF},
+ */
+ public static final String EXTRA_PREVIOUS_STATE =
+ "android.bluetooth.adapter.extra.PREVIOUS_STATE";
+
+ /**
+ * Indicates the local Bluetooth adapter is off.
+ */
+ public static final int STATE_OFF = 10;
+ /**
+ * Indicates the local Bluetooth adapter is turning on. However local
+ * clients should wait for {@link #STATE_ON} before attempting to
+ * use the adapter.
+ */
+ public static final int STATE_TURNING_ON = 11;
+ /**
+ * Indicates the local Bluetooth adapter is on, and ready for use.
+ */
+ public static final int STATE_ON = 12;
+ /**
+ * Indicates the local Bluetooth adapter is turning off. Local clients
+ * should immediately attempt graceful disconnection of any remote links.
+ */
+ public static final int STATE_TURNING_OFF = 13;
+
+ /**
+ * Activity Action: Show a system activity that requests discoverable mode.
+ * <p>This activity will also request the user to turn on Bluetooth if it
+ * is not currently enabled.
+ * <p>Discoverable mode is equivalent to {@link
+ * #SCAN_MODE_CONNECTABLE_DISCOVERABLE}. It allows remote devices to see
+ * this Bluetooth adapter when they perform a discovery.
+ * <p>For privacy, Android is not by default discoverable.
+ * <p>The sender can optionally use extra field {@link
+ * #EXTRA_DISCOVERABLE_DURATION} to request the duration of
+ * discoverability. Currently the default duration is 120 seconds, and
+ * maximum duration is capped at 300 seconds for each request.
+ * <p>Notification of the result of this activity is posted using the
+ * {@link android.app.Activity#onActivityResult} callback. The
+ * <code>resultCode</code>
+ * will be the duration (in seconds) of discoverability or
+ * {@link android.app.Activity#RESULT_CANCELED} if the user rejected
+ * discoverability or an error has occurred.
+ * <p>Applications can also listen for {@link #ACTION_SCAN_MODE_CHANGED}
+ * for global notification whenever the scan mode changes.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_REQUEST_DISCOVERABLE =
+ "android.bluetooth.adapter.action.REQUEST_DISCOVERABLE";
+
+ /**
+ * Used as an optional int extra field in {@link
+ * #ACTION_REQUEST_DISCOVERABLE} intents to request a specific duration
+ * for discoverability in seconds. The current default is 120 seconds, and
+ * requests over 300 seconds will be capped. These values could change.
+ */
+ public static final String EXTRA_DISCOVERABLE_DURATION =
+ "android.bluetooth.adapter.extra.DISCOVERABLE_DURATION";
+
+ /**
+ * Activity Action: Show a system activity that allows the user to turn on
+ * Bluetooth.
+ * <p>This system activity will return once Bluetooth has completed turning
+ * on, or the user has decided not to turn Bluetooth on.
+ * <p>Notification of the result of this activity is posted using the
+ * {@link android.app.Activity#onActivityResult} callback. The
+ * <code>resultCode</code>
+ * will be {@link android.app.Activity#RESULT_OK} if Bluetooth has been
+ * turned on or {@link android.app.Activity#RESULT_CANCELED} if the user
+ * has rejected the request or an error has occurred.
+ * <p>Applications can also listen for {@link #ACTION_STATE_CHANGED}
+ * for global notification whenever Bluetooth is turned on or off.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+ */
+ @SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
+ public static final String ACTION_REQUEST_ENABLE =
+ "android.bluetooth.adapter.action.REQUEST_ENABLE";
+
+ /**
+ * Broadcast Action: Indicates the Bluetooth scan mode of the local Adapter
+ * has changed.
+ * <p>Always contains the extra fields {@link #EXTRA_SCAN_MODE} and {@link
+ * #EXTRA_PREVIOUS_SCAN_MODE} containing the new and old scan modes
+ * respectively.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_SCAN_MODE_CHANGED =
+ "android.bluetooth.adapter.action.SCAN_MODE_CHANGED";
+
+ /**
+ * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED}
+ * intents to request the current scan mode. Possible values are:
+ * {@link #SCAN_MODE_NONE},
+ * {@link #SCAN_MODE_CONNECTABLE},
+ * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE},
+ */
+ public static final String EXTRA_SCAN_MODE = "android.bluetooth.adapter.extra.SCAN_MODE";
+ /**
+ * Used as an int extra field in {@link #ACTION_SCAN_MODE_CHANGED}
+ * intents to request the previous scan mode. Possible values are:
+ * {@link #SCAN_MODE_NONE},
+ * {@link #SCAN_MODE_CONNECTABLE},
+ * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE},
+ */
+ public static final String EXTRA_PREVIOUS_SCAN_MODE =
+ "android.bluetooth.adapter.extra.PREVIOUS_SCAN_MODE";
+
+ /**
+ * Indicates that both inquiry scan and page scan are disabled on the local
+ * Bluetooth adapter. Therefore this device is neither discoverable
+ * nor connectable from remote Bluetooth devices.
+ */
+ public static final int SCAN_MODE_NONE = 20;
+ /**
+ * Indicates that inquiry scan is disabled, but page scan is enabled on the
+ * local Bluetooth adapter. Therefore this device is not discoverable from
+ * remote Bluetooth devices, but is connectable from remote devices that
+ * have previously discovered this device.
+ */
+ public static final int SCAN_MODE_CONNECTABLE = 21;
+ /**
+ * Indicates that both inquiry scan and page scan are enabled on the local
+ * Bluetooth adapter. Therefore this device is both discoverable and
+ * connectable from remote Bluetooth devices.
+ */
+ public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 23;
+
+
+ /**
+ * Broadcast Action: The local Bluetooth adapter has started the remote
+ * device discovery process.
+ * <p>This usually involves an inquiry scan of about 12 seconds, followed
+ * by a page scan of each new device to retrieve its Bluetooth name.
+ * <p>Register for {@link BluetoothDevice#ACTION_FOUND} to be notified as
+ * remote Bluetooth devices are found.
+ * <p>Device discovery is a heavyweight procedure. New connections to
+ * remote Bluetooth devices should not be attempted while discovery is in
+ * progress, and existing connections will experience limited bandwidth
+ * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing
+ * discovery.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_DISCOVERY_STARTED =
+ "android.bluetooth.adapter.action.DISCOVERY_STARTED";
+ /**
+ * Broadcast Action: The local Bluetooth adapter has finished the device
+ * discovery process.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_DISCOVERY_FINISHED =
+ "android.bluetooth.adapter.action.DISCOVERY_FINISHED";
+
+ /**
+ * Broadcast Action: The local Bluetooth adapter has changed its friendly
+ * Bluetooth name.
+ * <p>This name is visible to remote Bluetooth devices.
+ * <p>Always contains the extra field {@link #EXTRA_LOCAL_NAME} containing
+ * the name.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_LOCAL_NAME_CHANGED =
+ "android.bluetooth.adapter.action.LOCAL_NAME_CHANGED";
+ /**
+ * Used as a String extra field in {@link #ACTION_LOCAL_NAME_CHANGED}
+ * intents to request the local Bluetooth name.
+ */
+ public static final String EXTRA_LOCAL_NAME = "android.bluetooth.adapter.extra.LOCAL_NAME";
+
+ /** @hide */
+ public static final String BLUETOOTH_SERVICE = "bluetooth";
+
+ private static final int ADDRESS_LENGTH = 17;
+
+ /**
+ * Lazyily initialized singleton. Guaranteed final after first object
+ * constructed.
+ */
+ private static BluetoothAdapter sAdapter;
+
+ private final IBluetooth mService;
+
+ /**
+ * Get a handle to the default local Bluetooth adapter.
+ * <p>Currently Android only supports one Bluetooth adapter, but the API
+ * could be extended to support more. This will always return the default
+ * adapter.
+ * @return the default local adapter, or null if Bluetooth is not supported
+ * on this hardware platform
+ */
+ public static synchronized BluetoothAdapter getDefaultAdapter() {
+ if (sAdapter == null) {
+ IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE);
+ if (b != null) {
+ IBluetooth service = IBluetooth.Stub.asInterface(b);
+ sAdapter = new BluetoothAdapter(service);
+ }
+ }
+ return sAdapter;
+ }
+
+ /**
+ * Use {@link #getDefaultAdapter} to get the BluetoothAdapter instance.
+ * @hide
+ */
+ public BluetoothAdapter(IBluetooth service) {
+ if (service == null) {
+ throw new IllegalArgumentException("service is null");
+ }
+ mService = service;
+ }
+
+ /**
+ * Get a {@link BluetoothDevice} object for the given Bluetooth hardware
+ * address.
+ * <p>Valid Bluetooth hardware addresses must be upper case, in a format
+ * such as "00:11:22:33:AA:BB". The helper {@link #checkBluetoothAddress} is
+ * available to validate a Bluetooth address.
+ * <p>A {@link BluetoothDevice} will always be returned for a valid
+ * hardware address, even if this adapter has never seen that device.
+ *
+ * @param address valid Bluetooth MAC address
+ * @throws IllegalArgumentException if address is invalid
+ */
+ public BluetoothDevice getRemoteDevice(String address) {
+ return new BluetoothDevice(address);
+ }
+
+ /**
+ * Return true if Bluetooth is currently enabled and ready for use.
+ * <p>Equivalent to:
+ * <code>getBluetoothState() == STATE_ON</code>
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+ *
+ * @return true if the local adapter is turned on
+ */
+ public boolean isEnabled() {
+ try {
+ return mService.isEnabled();
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return false;
+ }
+
+ /**
+ * Get the current state of the local Bluetooth adapter.
+ * <p>Possible return values are
+ * {@link #STATE_OFF},
+ * {@link #STATE_TURNING_ON},
+ * {@link #STATE_ON},
+ * {@link #STATE_TURNING_OFF}.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+ *
+ * @return current state of Bluetooth adapter
+ */
+ public int getState() {
+ try {
+ return mService.getBluetoothState();
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return STATE_OFF;
+ }
+
+ /**
+ * Turn on the local Bluetooth adapter.
+ * <p>This powers on the underlying Bluetooth hardware, and starts all
+ * Bluetooth system services.
+ * <p>This is an asynchronous call: it will return immediately, and
+ * clients should listen for {@link #ACTION_STATE_CHANGED}
+ * to be notified of subsequent adapter state changes. If this call returns
+ * true, then the adapter state will immediately transition from {@link
+ * #STATE_OFF} to {@link #STATE_TURNING_ON}, and some time
+ * later transition to either {@link #STATE_OFF} or {@link
+ * #STATE_ON}. If this call returns false then there was an
+ * immediate problem that will prevent the adapter from being turned on -
+ * such as Airplane mode, or the adapter is already turned on.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ *
+ * @return true to indicate adapter startup has begun, or false on
+ * immediate error
+ */
+ public boolean enable() {
+ try {
+ return mService.enable();
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return false;
+ }
+
+ /**
+ * Turn off the local Bluetooth adapter.
+ * <p>This gracefully shuts down all Bluetooth connections, stops Bluetooth
+ * system services, and powers down the underlying Bluetooth hardware.
+ * <p>This is an asynchronous call: it will return immediately, and
+ * clients should listen for {@link #ACTION_STATE_CHANGED}
+ * to be notified of subsequent adapter state changes. If this call returns
+ * true, then the adapter state will immediately transition from {@link
+ * #STATE_ON} to {@link #STATE_TURNING_OFF}, and some time
+ * later transition to either {@link #STATE_OFF} or {@link
+ * #STATE_ON}. If this call returns false then there was an
+ * immediate problem that will prevent the adapter from being turned off -
+ * such as the adapter already being turned off.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ *
+ * @return true to indicate adapter shutdown has begun, or false on
+ * immediate error
+ */
+ public boolean disable() {
+ try {
+ return mService.disable(true);
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return false;
+ }
+
+ /**
+ * Returns the hardware address of the local Bluetooth adapter.
+ * <p>For example, "00:11:22:AA:BB:CC".
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+ *
+ * @return Bluetooth hardware address as string
+ */
+ public String getAddress() {
+ try {
+ return mService.getAddress();
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return null;
+ }
+
+ /**
+ * Get the friendly Bluetooth name of the local Bluetooth adapter.
+ * <p>This name is visible to remote Bluetooth devices.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+ *
+ * @return the Bluetooth name, or null on error
+ */
+ public String getName() {
+ try {
+ return mService.getName();
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return null;
+ }
+
+ /**
+ * Set the friendly Bluetooth name of the local Bluetoth adapter.
+ * <p>This name is visible to remote Bluetooth devices.
+ * <p>Valid Bluetooth names are a maximum of 248 UTF-8 characters, however
+ * many remote devices can only display the first 40 characters, and some
+ * may be limited to just 20.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ *
+ * @param name a valid Bluetooth name
+ * @return true if the name was set, false otherwise
+ */
+ public boolean setName(String name) {
+ try {
+ return mService.setName(name);
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return false;
+ }
+
+ /**
+ * Get the current Bluetooth scan mode of the local Bluetooth adaper.
+ * <p>The Bluetooth scan mode determines if the local adapter is
+ * connectable and/or discoverable from remote Bluetooth devices.
+ * <p>Possible values are:
+ * {@link #SCAN_MODE_NONE},
+ * {@link #SCAN_MODE_CONNECTABLE},
+ * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+ *
+ * @return scan mode
+ */
+ public int getScanMode() {
+ try {
+ return mService.getScanMode();
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return SCAN_MODE_NONE;
+ }
+
+ /**
+ * Set the Bluetooth scan mode of the local Bluetooth adapter.
+ * <p>The Bluetooth scan mode determines if the local adapter is
+ * connectable and/or discoverable from remote Bluetooth devices.
+ * <p>For privacy reasons, discoverable mode is automatically turned off
+ * after <code>duration</code> seconds. For example, 120 seconds should be
+ * enough for a remote device to initiate and complete its discovery
+ * process.
+ * <p>Valid scan mode values are:
+ * {@link #SCAN_MODE_NONE},
+ * {@link #SCAN_MODE_CONNECTABLE},
+ * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}.
+ * <p>Requires {@link android.Manifest.permission#WRITE_SECURE_SETTINGS}
+ * <p>Applications cannot set the scan mode. They should use
+ * <code>startActivityForResult(
+ * BluetoothAdapter.ACTION_REQUEST_DISCOVERABLE})
+ * </code>instead.
+ *
+ * @param mode valid scan mode
+ * @param duration time in seconds to apply scan mode, only used for
+ * {@link #SCAN_MODE_CONNECTABLE_DISCOVERABLE}
+ * @return true if the scan mode was set, false otherwise
+ * @hide
+ */
+ public boolean setScanMode(int mode, int duration) {
+ try {
+ return mService.setScanMode(mode, duration);
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return false;
+ }
+
+ /** @hide */
+ public boolean setScanMode(int mode) {
+ return setScanMode(mode, 120);
+ }
+
+ /** @hide */
+ public int getDiscoverableTimeout() {
+ try {
+ return mService.getDiscoverableTimeout();
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return -1;
+ }
+
+ /** @hide */
+ public void setDiscoverableTimeout(int timeout) {
+ try {
+ mService.setDiscoverableTimeout(timeout);
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ }
+
+ /**
+ * Start the remote device discovery process.
+ * <p>The discovery process usually involves an inquiry scan of about 12
+ * seconds, followed by a page scan of each new device to retrieve its
+ * Bluetooth name.
+ * <p>This is an asynchronous call, it will return immediately. Register
+ * for {@link #ACTION_DISCOVERY_STARTED} and {@link
+ * #ACTION_DISCOVERY_FINISHED} intents to determine exactly when the
+ * discovery starts and completes. Register for {@link
+ * BluetoothDevice#ACTION_FOUND} to be notified as remote Bluetooth devices
+ * are found.
+ * <p>Device discovery is a heavyweight procedure. New connections to
+ * remote Bluetooth devices should not be attempted while discovery is in
+ * progress, and existing connections will experience limited bandwidth
+ * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing
+ * discovery.
+ * <p>Device discovery will only find remote devices that are currently
+ * <i>discoverable</i> (inquiry scan enabled). Many Bluetooth devices are
+ * not discoverable by default, and need to be entered into a special mode.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
+ *
+ * @return true on success, false on error
+ */
+ public boolean startDiscovery() {
+ try {
+ return mService.startDiscovery();
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return false;
+ }
+
+ /**
+ * Cancel the current device discovery process.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
+ *
+ * @return true on success, false on error
+ */
+ public boolean cancelDiscovery() {
+ try {
+ mService.cancelDiscovery();
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return false;
+ }
+
+ /**
+ * Return true if the local Bluetooth adapter is currently in the device
+ * discovery process.
+ * <p>Device discovery is a heavyweight procedure. New connections to
+ * remote Bluetooth devices should not be attempted while discovery is in
+ * progress, and existing connections will experience limited bandwidth
+ * and high latency. Use {@link #cancelDiscovery()} to cancel an ongoing
+ * discovery.
+ * <p>Applications can also register for {@link #ACTION_DISCOVERY_STARTED}
+ * or {@link #ACTION_DISCOVERY_FINISHED} to be notified when discovery
+ * starts or completes.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
+ *
+ * @return true if discovering
+ */
+ public boolean isDiscovering() {
+ try {
+ return mService.isDiscovering();
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return false;
+ }
+
+ /**
+ * Return the set of {@link BluetoothDevice} objects that are bonded
+ * (paired) to the local adapter.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
+ *
+ * @return unmodifiable set of {@link BluetoothDevice}, or null on error
+ */
+ public Set<BluetoothDevice> getBondedDevices() {
+ try {
+ return toDeviceSet(mService.listBonds());
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return null;
+ }
+
+ /**
+ * Picks RFCOMM channels until none are left.
+ * Avoids reserved channels.
+ */
+ private static class RfcommChannelPicker {
+ private static final int[] RESERVED_RFCOMM_CHANNELS = new int[] {
+ 10, // HFAG
+ 11, // HSAG
+ 12, // OPUSH
+ 19, // PBAP
+ };
+ private static LinkedList<Integer> sChannels; // master list of non-reserved channels
+ private static Random sRandom;
+
+ private final LinkedList<Integer> mChannels; // local list of channels left to try
+
+ private final UUID mUuid;
+
+ public RfcommChannelPicker(UUID uuid) {
+ synchronized (RfcommChannelPicker.class) {
+ if (sChannels == null) {
+ // lazy initialization of non-reserved rfcomm channels
+ sChannels = new LinkedList<Integer>();
+ for (int i = 1; i <= BluetoothSocket.MAX_RFCOMM_CHANNEL; i++) {
+ sChannels.addLast(new Integer(i));
+ }
+ for (int reserved : RESERVED_RFCOMM_CHANNELS) {
+ sChannels.remove(new Integer(reserved));
+ }
+ sRandom = new Random();
+ }
+ mChannels = (LinkedList<Integer>)sChannels.clone();
+ }
+ mUuid = uuid;
+ }
+ /* Returns next random channel, or -1 if we're out */
+ public int nextChannel() {
+ if (mChannels.size() == 0) {
+ return -1;
+ }
+ return mChannels.remove(sRandom.nextInt(mChannels.size()));
+ }
+ }
+
+ /**
+ * Create a listening, secure RFCOMM Bluetooth socket.
+ * <p>A remote device connecting to this socket will be authenticated and
+ * communication on this socket will be encrypted.
+ * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
+ * connections from a listening {@link BluetoothServerSocket}.
+ * <p>Valid RFCOMM channels are in range 1 to 30.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ * @param channel RFCOMM channel to listen on
+ * @return a listening RFCOMM BluetoothServerSocket
+ * @throws IOException on error, for example Bluetooth not available, or
+ * insufficient permissions, or channel in use.
+ * @hide
+ */
+ public BluetoothServerSocket listenUsingRfcommOn(int channel) throws IOException {
+ BluetoothServerSocket socket = new BluetoothServerSocket(
+ BluetoothSocket.TYPE_RFCOMM, true, true, channel);
+ int errno = socket.mSocket.bindListen();
+ if (errno != 0) {
+ try {
+ socket.close();
+ } catch (IOException e) {}
+ socket.mSocket.throwErrnoNative(errno);
+ }
+ return socket;
+ }
+
+ /**
+ * Create a listening, secure RFCOMM Bluetooth socket with Service Record.
+ * <p>A remote device connecting to this socket will be authenticated and
+ * communication on this socket will be encrypted.
+ * <p>Use {@link BluetoothServerSocket#accept} to retrieve incoming
+ * connections from a listening {@link BluetoothServerSocket}.
+ * <p>The system will assign an unused RFCOMM channel to listen on.
+ * <p>The system will also register a Service Discovery
+ * Protocol (SDP) record with the local SDP server containing the specified
+ * UUID, service name, and auto-assigned channel. Remote Bluetooth devices
+ * can use the same UUID to query our SDP server and discover which channel
+ * to connect to. This SDP record will be removed when this socket is
+ * closed, or if this application closes unexpectedly.
+ * <p>Use {@link BluetoothDevice#createRfcommSocketToServiceRecord} to
+ * connect to this socket from another device using the same {@link UUID}.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+ * @param name service name for SDP record
+ * @param uuid uuid for SDP record
+ * @return a listening RFCOMM BluetoothServerSocket
+ * @throws IOException on error, for example Bluetooth not available, or
+ * insufficient permissions, or channel in use.
+ */
+ public BluetoothServerSocket listenUsingRfcommWithServiceRecord(String name, UUID uuid)
+ throws IOException {
+ RfcommChannelPicker picker = new RfcommChannelPicker(uuid);
+
+ BluetoothServerSocket socket;
+ int channel;
+ int errno;
+ while (true) {
+ channel = picker.nextChannel();
+
+ if (channel == -1) {
+ throw new IOException("No available channels");
+ }
+
+ socket = new BluetoothServerSocket(
+ BluetoothSocket.TYPE_RFCOMM, true, true, channel);
+ errno = socket.mSocket.bindListen();
+ if (errno == 0) {
+ if (DBG) Log.d(TAG, "listening on RFCOMM channel " + channel);
+ break; // success
+ } else if (errno == BluetoothSocket.EADDRINUSE) {
+ if (DBG) Log.d(TAG, "RFCOMM channel " + channel + " in use");
+ try {
+ socket.close();
+ } catch (IOException e) {}
+ continue; // try another channel
+ } else {
+ try {
+ socket.close();
+ } catch (IOException e) {}
+ socket.mSocket.throwErrnoNative(errno); // Exception as a result of bindListen()
+ }
+ }
+
+ int handle = -1;
+ try {
+ handle = mService.addRfcommServiceRecord(name, new ParcelUuid(uuid), channel,
+ new Binder());
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ if (handle == -1) {
+ try {
+ socket.close();
+ } catch (IOException e) {}
+ throw new IOException("Not able to register SDP record for " + name);
+ }
+ socket.setCloseHandler(mHandler, handle);
+ return socket;
+ }
+
+ /**
+ * Construct an unencrypted, unauthenticated, RFCOMM server socket.
+ * Call #accept to retrieve connections to this socket.
+ * @return An RFCOMM BluetoothServerSocket
+ * @throws IOException On error, for example Bluetooth not available, or
+ * insufficient permissions.
+ * @hide
+ */
+ public BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException {
+ BluetoothServerSocket socket = new BluetoothServerSocket(
+ BluetoothSocket.TYPE_RFCOMM, false, false, port);
+ int errno = socket.mSocket.bindListen();
+ if (errno != 0) {
+ try {
+ socket.close();
+ } catch (IOException e) {}
+ socket.mSocket.throwErrnoNative(errno);
+ }
+ return socket;
+ }
+
+ /**
+ * Construct a SCO server socket.
+ * Call #accept to retrieve connections to this socket.
+ * @return A SCO BluetoothServerSocket
+ * @throws IOException On error, for example Bluetooth not available, or
+ * insufficient permissions.
+ * @hide
+ */
+ public static BluetoothServerSocket listenUsingScoOn() throws IOException {
+ BluetoothServerSocket socket = new BluetoothServerSocket(
+ BluetoothSocket.TYPE_SCO, false, false, -1);
+ int errno = socket.mSocket.bindListen();
+ if (errno != 0) {
+ try {
+ socket.close();
+ } catch (IOException e) {}
+ socket.mSocket.throwErrnoNative(errno);
+ }
+ return socket;
+ }
+
+ private Set<BluetoothDevice> toDeviceSet(String[] addresses) {
+ Set<BluetoothDevice> devices = new HashSet<BluetoothDevice>(addresses.length);
+ for (int i = 0; i < addresses.length; i++) {
+ devices.add(getRemoteDevice(addresses[i]));
+ }
+ return Collections.unmodifiableSet(devices);
+ }
+
+ private Handler mHandler = new Handler() {
+ public void handleMessage(Message msg) {
+ /* handle socket closing */
+ int handle = msg.what;
+ try {
+ if (DBG) Log.d(TAG, "Removing service record " + Integer.toHexString(handle));
+ mService.removeServiceRecord(handle);
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ }
+ };
+
+ /**
+ * Validate a Bluetooth address, such as "00:43:A8:23:10:F0"
+ * <p>Alphabetic characters must be uppercase to be valid.
+ *
+ * @param address Bluetooth address as string
+ * @return true if the address is valid, false otherwise
+ */
+ public static boolean checkBluetoothAddress(String address) {
+ if (address == null || address.length() != ADDRESS_LENGTH) {
+ return false;
+ }
+ for (int i = 0; i < ADDRESS_LENGTH; i++) {
+ char c = address.charAt(i);
+ switch (i % 3) {
+ case 0:
+ case 1:
+ if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) {
+ // hex character, OK
+ break;
+ }
+ return false;
+ case 2:
+ if (c == ':') {
+ break; // OK
+ }
+ return false;
+ }
+ }
+ return true;
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothAudioGateway.java b/core/java/android/bluetooth/BluetoothAudioGateway.java
index f3afd2a..abd7723 100644
--- a/core/java/android/bluetooth/BluetoothAudioGateway.java
+++ b/core/java/android/bluetooth/BluetoothAudioGateway.java
@@ -9,24 +9,22 @@ import android.util.Log;
/**
* Listen's for incoming RFCOMM connection for the headset / handsfree service.
*
- * This class is planned for deletion, in favor of a generic Rfcomm class.
+ * TODO: Use the new generic BluetoothSocket class instead of this legacy code
*
* @hide
*/
-public class BluetoothAudioGateway {
+public final class BluetoothAudioGateway {
private static final String TAG = "BT Audio Gateway";
private static final boolean DBG = false;
private int mNativeData;
static { classInitNative(); }
- private BluetoothDevice mBluetooth;
-
/* in */
private int mHandsfreeAgRfcommChannel = -1;
private int mHeadsetAgRfcommChannel = -1;
- /* out */
+ /* out - written by native code */
private String mConnectingHeadsetAddress;
private int mConnectingHeadsetRfcommChannel; /* -1 when not connected */
private int mConnectingHeadsetSocketFd;
@@ -35,17 +33,18 @@ public class BluetoothAudioGateway {
private int mConnectingHandsfreeSocketFd;
private int mTimeoutRemainingMs; /* in/out */
+ private final BluetoothAdapter mAdapter;
+
public static final int DEFAULT_HF_AG_CHANNEL = 10;
public static final int DEFAULT_HS_AG_CHANNEL = 11;
- public BluetoothAudioGateway(BluetoothDevice bluetooth) {
- this(bluetooth, DEFAULT_HF_AG_CHANNEL, DEFAULT_HS_AG_CHANNEL);
+ public BluetoothAudioGateway(BluetoothAdapter adapter) {
+ this(adapter, DEFAULT_HF_AG_CHANNEL, DEFAULT_HS_AG_CHANNEL);
}
- public BluetoothAudioGateway(BluetoothDevice bluetooth,
- int handsfreeAgRfcommChannel,
- int headsetAgRfcommChannel) {
- mBluetooth = bluetooth;
+ public BluetoothAudioGateway(BluetoothAdapter adapter, int handsfreeAgRfcommChannel,
+ int headsetAgRfcommChannel) {
+ mAdapter = adapter;
mHandsfreeAgRfcommChannel = handsfreeAgRfcommChannel;
mHeadsetAgRfcommChannel = headsetAgRfcommChannel;
initializeNativeDataNative();
@@ -58,18 +57,17 @@ public class BluetoothAudioGateway {
private Handler mCallback;
public class IncomingConnectionInfo {
- IncomingConnectionInfo(BluetoothDevice bluetooth, String address, int socketFd,
- int rfcommChan) {
- mBluetooth = bluetooth;
- mAddress = address;
+ public BluetoothAdapter mAdapter;
+ public BluetoothDevice mRemoteDevice;
+ public int mSocketFd;
+ public int mRfcommChan;
+ IncomingConnectionInfo(BluetoothAdapter adapter, BluetoothDevice remoteDevice,
+ int socketFd, int rfcommChan) {
+ mAdapter = adapter;
+ mRemoteDevice = remoteDevice;
mSocketFd = socketFd;
mRfcommChan = rfcommChan;
}
-
- public BluetoothDevice mBluetooth;
- public String mAddress;
- public int mSocketFd;
- public int mRfcommChan;
}
public static final int MSG_INCOMING_HEADSET_CONNECTION = 100;
@@ -111,12 +109,11 @@ public class BluetoothAudioGateway {
mConnectingHeadsetRfcommChannel);
Message msg = Message.obtain(mCallback);
msg.what = MSG_INCOMING_HEADSET_CONNECTION;
- msg.obj =
- new IncomingConnectionInfo(
- mBluetooth,
- mConnectingHeadsetAddress,
- mConnectingHeadsetSocketFd,
- mConnectingHeadsetRfcommChannel);
+ msg.obj = new IncomingConnectionInfo(
+ mAdapter,
+ mAdapter.getRemoteDevice(mConnectingHeadsetAddress),
+ mConnectingHeadsetSocketFd,
+ mConnectingHeadsetRfcommChannel);
msg.sendToTarget();
}
if (mConnectingHandsfreeRfcommChannel >= 0) {
@@ -126,12 +123,11 @@ public class BluetoothAudioGateway {
Message msg = Message.obtain();
msg.setTarget(mCallback);
msg.what = MSG_INCOMING_HANDSFREE_CONNECTION;
- msg.obj =
- new IncomingConnectionInfo(
- mBluetooth,
- mConnectingHandsfreeAddress,
- mConnectingHandsfreeSocketFd,
- mConnectingHandsfreeRfcommChannel);
+ msg.obj = new IncomingConnectionInfo(
+ mAdapter,
+ mAdapter.getRemoteDevice(mConnectingHandsfreeAddress),
+ mConnectingHandsfreeSocketFd,
+ mConnectingHandsfreeRfcommChannel);
msg.sendToTarget();
}
}
diff --git a/core/java/android/bluetooth/BluetoothClass.java b/core/java/android/bluetooth/BluetoothClass.java
index 88ce18b..bc06713 100644
--- a/core/java/android/bluetooth/BluetoothClass.java
+++ b/core/java/android/bluetooth/BluetoothClass.java
@@ -16,39 +16,98 @@
package android.bluetooth;
+import android.os.Parcel;
+import android.os.Parcelable;
+
/**
- * The Android Bluetooth API is not finalized, and *will* change. Use at your
- * own risk.
- *
- * Static helper methods and constants to decode the device class bit vector
- * returned by the Bluetooth API.
+ * Represents a Bluetooth class, which describes general characteristics
+ * and capabilities of a device. For example, a Bluetooth class will
+ * specify the general device type such as a phone, a computer, or
+ * headset, and whether it's capable of services such as audio or telephony.
*
- * The Android Bluetooth API returns a 32-bit integer to represent the class.
- * The format of these bits is defined at
- * http://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm
- * (login required). This class provides static helper methods and constants to
- * determine what Service Class(es) and Device Class are encoded in the 32-bit
- * class.
+ * <p>The Bluetooth class is useful as a hint to roughly describe a device (for example to
+ * show an icon in the UI), but does not reliably describe which Bluetooth
+ * profiles or services are actually supported by a device.
*
- * Devices typically have zero or more service classes, and exactly one device
- * class. The device class is encoded as a major and minor device class, the
- * minor being a subset of the major.
+ * <p>Every Bluetooth class is composed of zero or more service classes, and
+ * exactly one device class. The device class is further broken down into major
+ * and minor device class components.
*
- * Class is useful to describe a device (for example to show an icon),
- * but does not reliably describe what profiles a device supports. To determine
- * profile support you usually need to perform SDP queries.
+ * <p>{@link BluetoothClass} is useful as a hint to roughly describe a device
+ * (for example to show an icon in the UI), but does not reliably describe which
+ * Bluetooth profiles or services are actually supported by a device. Accurate
+ * service discovery is done through SDP requests, which are automatically
+ * performed when creating an RFCOMM socket with {@link
+ * BluetoothDevice#createRfcommSocketToServiceRecord(UUID)} and {@link
+ * BluetoothAdapter#listenUsingRfcommWithServiceRecord(String,UUID)}</p>
*
- * Each of these helper methods takes the 32-bit integer class as an argument.
+ * <p>Use {@link BluetoothDevice#getBluetoothClass} to retrieve the class for
+ * a remote device.
*
- * @hide
+ * <!--
+ * The Bluetooth class is a 32 bit field. The format of these bits is defined at
+ * http://www.bluetooth.org/Technical/AssignedNumbers/baseband.htm
+ * (login required). This class contains that 32 bit field, and provides
+ * constants and methods to determine which Service Class(es) and Device Class
+ * are encoded in that field.
+ * -->
*/
-public class BluetoothClass {
- /** Indicates the Bluetooth API could not retrieve the class */
+public final class BluetoothClass implements Parcelable {
+ /**
+ * Legacy error value. Applications should use null instead.
+ * @hide
+ */
public static final int ERROR = 0xFF000000;
- /** Every Bluetooth device has zero or more service classes */
- public static class Service {
- public static final int BITMASK = 0xFFE000;
+ private final int mClass;
+
+ /** @hide */
+ public BluetoothClass(int classInt) {
+ mClass = classInt;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof BluetoothClass) {
+ return mClass == ((BluetoothClass)o).mClass;
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ return mClass;
+ }
+
+ @Override
+ public String toString() {
+ return Integer.toHexString(mClass);
+ }
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Parcelable.Creator<BluetoothClass> CREATOR =
+ new Parcelable.Creator<BluetoothClass>() {
+ public BluetoothClass createFromParcel(Parcel in) {
+ return new BluetoothClass(in.readInt());
+ }
+ public BluetoothClass[] newArray(int size) {
+ return new BluetoothClass[size];
+ }
+ };
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeInt(mClass);
+ }
+
+ /**
+ * Defines all service class constants.
+ * <p>Each {@link BluetoothClass} encodes zero or more service classes.
+ */
+ public static final class Service {
+ private static final int BITMASK = 0xFFE000;
public static final int LIMITED_DISCOVERABILITY = 0x002000;
public static final int POSITIONING = 0x010000;
@@ -59,32 +118,41 @@ public class BluetoothClass {
public static final int AUDIO = 0x200000;
public static final int TELEPHONY = 0x400000;
public static final int INFORMATION = 0x800000;
+ }
- /** Returns true if the given class supports the given Service Class.
- * A bluetooth device can claim to support zero or more service classes.
- * @param btClass The bluetooth class.
- * @param serviceClass The service class constant to test for. For
- * example, Service.AUDIO. Must be one of the
- * Service.FOO constants.
- * @return True if the service class is supported.
- */
- public static boolean hasService(int btClass, int serviceClass) {
- if (btClass == ERROR) {
- return false;
- }
- return ((btClass & Service.BITMASK & serviceClass) != 0);
- }
+ /**
+ * Return true if the specified service class is supported by this
+ * {@link BluetoothClass}.
+ * <p>Valid service classes are the public constants in
+ * {@link BluetoothClass.Service}. For example, {@link
+ * BluetoothClass.Service#AUDIO}.
+ *
+ * @param service valid service class
+ * @return true if the service class is supported
+ */
+ public boolean hasService(int service) {
+ return ((mClass & Service.BITMASK & service) != 0);
}
- /** Every Bluetooth device has exactly one device class, comprimised of
- * major and minor components. We have not included the minor classes for
- * major classes: NETWORKING, PERIPHERAL and IMAGING yet because they work
- * a little differently. */
+ /**
+ * Defines all device class constants.
+ * <p>Each {@link BluetoothClass} encodes exactly one device class, with
+ * major and minor components.
+ * <p>The constants in {@link
+ * BluetoothClass.Device} represent a combination of major and minor
+ * device components (the complete device class). The constants in {@link
+ * BluetoothClass.Device.Major} represent only major device classes.
+ * <p>See {@link BluetoothClass.Service} for service class constants.
+ */
public static class Device {
- public static final int BITMASK = 0x1FFC;
+ private static final int BITMASK = 0x1FFC;
+ /**
+ * Defines all major device class constants.
+ * <p>See {@link BluetoothClass.Device} for minor classes.
+ */
public static class Major {
- public static final int BITMASK = 0x1F00;
+ private static final int BITMASK = 0x1F00;
public static final int MISC = 0x0000;
public static final int COMPUTER = 0x0100;
@@ -97,18 +165,6 @@ public class BluetoothClass {
public static final int TOY = 0x0800;
public static final int HEALTH = 0x0900;
public static final int UNCATEGORIZED = 0x1F00;
-
- /** Returns the Major Device Class component of a bluetooth class.
- * Values returned from this function can be compared with the constants
- * Device.Major.FOO. A bluetooth device can only be associated
- * with one major class.
- */
- public static int getDeviceMajor(int btClass) {
- if (btClass == ERROR) {
- return ERROR;
- }
- return (btClass & Device.Major.BITMASK);
- }
}
// Devices in the COMPUTER major class
@@ -174,18 +230,106 @@ public class BluetoothClass {
public static final int HEALTH_PULSE_OXIMETER = 0x0914;
public static final int HEALTH_PULSE_RATE = 0x0918;
public static final int HEALTH_DATA_DISPLAY = 0x091C;
+ }
- /** Returns the Device Class component of a bluetooth class. This includes
- * both the major and minor device components. Values returned from this
- * function can be compared with the constants Device.FOO. A bluetooth
- * device can only be associated with one device class.
- */
- public static int getDevice(int btClass) {
- if (btClass == ERROR) {
- return ERROR;
+ /**
+ * Return the major device class component of this {@link BluetoothClass}.
+ * <p>Values returned from this function can be compared with the
+ * public constants in {@link BluetoothClass.Device.Major} to determine
+ * which major class is encoded in this Bluetooth class.
+ *
+ * @return major device class component
+ */
+ public int getMajorDeviceClass() {
+ return (mClass & Device.Major.BITMASK);
+ }
+
+ /**
+ * Return the (major and minor) device class component of this
+ * {@link BluetoothClass}.
+ * <p>Values returned from this function can be compared with the
+ * public constants in {@link BluetoothClass.Device} to determine which
+ * device class is encoded in this Bluetooth class.
+ *
+ * @return device class component
+ */
+ public int getDeviceClass() {
+ return (mClass & Device.BITMASK);
+ }
+
+ /** @hide */
+ public static final int PROFILE_HEADSET = 0;
+ /** @hide */
+ public static final int PROFILE_A2DP = 1;
+ /** @hide */
+ public static final int PROFILE_OPP = 2;
+
+ /**
+ * Check class bits for possible bluetooth profile support.
+ * This is a simple heuristic that tries to guess if a device with the
+ * given class bits might support specified profile. It is not accurate for all
+ * devices. It tries to err on the side of false positives.
+ * @param profile The profile to be checked
+ * @return True if this device might support specified profile.
+ * @hide
+ */
+ public boolean doesClassMatch(int profile) {
+ if (profile == PROFILE_A2DP) {
+ if (hasService(Service.RENDER)) {
+ return true;
+ }
+ // By the A2DP spec, sinks must indicate the RENDER service.
+ // However we found some that do not (Chordette). So lets also
+ // match on some other class bits.
+ switch (getDeviceClass()) {
+ case Device.AUDIO_VIDEO_HIFI_AUDIO:
+ case Device.AUDIO_VIDEO_HEADPHONES:
+ case Device.AUDIO_VIDEO_LOUDSPEAKER:
+ case Device.AUDIO_VIDEO_CAR_AUDIO:
+ return true;
+ default:
+ return false;
+ }
+ } else if (profile == PROFILE_HEADSET) {
+ // The render service class is required by the spec for HFP, so is a
+ // pretty good signal
+ if (hasService(Service.RENDER)) {
+ return true;
}
- return (btClass & Device.BITMASK);
+ // Just in case they forgot the render service class
+ switch (getDeviceClass()) {
+ case Device.AUDIO_VIDEO_HANDSFREE:
+ case Device.AUDIO_VIDEO_WEARABLE_HEADSET:
+ case Device.AUDIO_VIDEO_CAR_AUDIO:
+ return true;
+ default:
+ return false;
+ }
+ } else if (profile == PROFILE_OPP) {
+ if (hasService(Service.OBJECT_TRANSFER)) {
+ return true;
+ }
+
+ switch (getDeviceClass()) {
+ case Device.COMPUTER_UNCATEGORIZED:
+ case Device.COMPUTER_DESKTOP:
+ case Device.COMPUTER_SERVER:
+ case Device.COMPUTER_LAPTOP:
+ case Device.COMPUTER_HANDHELD_PC_PDA:
+ case Device.COMPUTER_PALM_SIZE_PC_PDA:
+ case Device.COMPUTER_WEARABLE:
+ case Device.PHONE_UNCATEGORIZED:
+ case Device.PHONE_CELLULAR:
+ case Device.PHONE_CORDLESS:
+ case Device.PHONE_SMART:
+ case Device.PHONE_MODEM_OR_GATEWAY:
+ case Device.PHONE_ISDN:
+ return true;
+ default:
+ return false;
+ }
+ } else {
+ return false;
}
}
}
-
diff --git a/core/java/android/bluetooth/BluetoothDevice.aidl b/core/java/android/bluetooth/BluetoothDevice.aidl
new file mode 100644
index 0000000..daae74d
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothDevice.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+parcelable BluetoothDevice;
diff --git a/core/java/android/bluetooth/BluetoothDevice.java b/core/java/android/bluetooth/BluetoothDevice.java
index 951b4b0..6cb9770 100644
--- a/core/java/android/bluetooth/BluetoothDevice.java
+++ b/core/java/android/bluetooth/BluetoothDevice.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008 The Android Open Source Project
+ * Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -16,515 +16,689 @@
package android.bluetooth;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.ParcelUuid;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.util.Log;
+import java.io.IOException;
import java.io.UnsupportedEncodingException;
+import java.util.UUID;
/**
- * The Android Bluetooth API is not finalized, and *will* change. Use at your
- * own risk.
+ * Represents a remote Bluetooth device. A {@link BluetoothDevice} lets you
+ * create a connection with the repective device or query information about
+ * it, such as the name, address, class, and bonding state.
*
- * Manages the local Bluetooth device. Scan for devices, create bondings,
- * power up and down the adapter.
+ * <p>This class is really just a thin wrapper for a Bluetooth hardware
+ * address. Objects of this class are immutable. Operations on this class
+ * are performed on the remote Bluetooth hardware address, using the
+ * {@link BluetoothAdapter} that was used to create this {@link
+ * BluetoothDevice}.
*
- * @hide
+ * <p>To get a {@link BluetoothDevice}, use
+ * {@link BluetoothAdapter#getRemoteDevice(String)
+ * BluetoothAdapter.getRemoteDevice(String)} to create one representing a device
+ * of a known MAC address (which you can get through device discovery with
+ * {@link BluetoothAdapter}) or get one from the set of bonded devices
+ * returned by {@link BluetoothAdapter#getBondedDevices()
+ * BluetoothAdapter.getBondedDevices()}. You can then open a
+ * {@link BluetoothSocket} for communciation with the remote device, using
+ * {@link #createRfcommSocketToServiceRecord(UUID)}.
+ *
+ * <p class="note"><strong>Note:</strong>
+ * Requires the {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * {@see BluetoothAdapter}
+ * {@see BluetoothSocket}
*/
-public class BluetoothDevice {
-
- public static final int BLUETOOTH_STATE_OFF = 0;
- public static final int BLUETOOTH_STATE_TURNING_ON = 1;
- public static final int BLUETOOTH_STATE_ON = 2;
- public static final int BLUETOOTH_STATE_TURNING_OFF = 3;
-
- /** Inquiry scan and page scan are both off.
- * Device is neither discoverable nor connectable */
- public static final int SCAN_MODE_NONE = 0;
- /** Page scan is on, inquiry scan is off.
- * Device is connectable, but not discoverable */
- public static final int SCAN_MODE_CONNECTABLE = 1;
- /** Page scan and inquiry scan are on.
- * Device is connectable and discoverable */
- public static final int SCAN_MODE_CONNECTABLE_DISCOVERABLE = 3;
-
- public static final int RESULT_FAILURE = -1;
- public static final int RESULT_SUCCESS = 0;
-
- /** We do not have a link key for the remote device, and are therefore not
- * bonded */
- public static final int BOND_NOT_BONDED = 0;
- /** We have a link key for the remote device, and are probably bonded. */
- public static final int BOND_BONDED = 1;
- /** We are currently attempting bonding */
- public static final int BOND_BONDING = 2;
-
- //TODO: Unify these result codes in BluetoothResult or BluetoothError
- /** A bond attempt failed because pins did not match, or remote device did
- * not respond to pin request in time */
- public static final int UNBOND_REASON_AUTH_FAILED = 1;
- /** A bond attempt failed because the other side explicilty rejected
- * bonding */
- public static final int UNBOND_REASON_AUTH_REJECTED = 2;
- /** A bond attempt failed because we canceled the bonding process */
- public static final int UNBOND_REASON_AUTH_CANCELED = 3;
- /** A bond attempt failed because we could not contact the remote device */
- public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4;
- /** A bond attempt failed because a discovery is in progress */
- public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5;
- /** An existing bond was explicitly revoked */
- public static final int UNBOND_REASON_REMOVED = 6;
-
+public final class BluetoothDevice implements Parcelable {
private static final String TAG = "BluetoothDevice";
-
- private final IBluetoothDevice mService;
+
/**
- * @hide - hide this because it takes a parameter of type
- * IBluetoothDevice, which is a System private class.
- * Also note that Context.getSystemService is a factory that
- * returns a BlueToothDevice. That is the right way to get
- * a BluetoothDevice.
+ * Sentinel error value for this class. Guaranteed to not equal any other
+ * integer constant in this class. Provided as a convenience for functions
+ * that require a sentinel error value, for example:
+ * <p><code>Intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE,
+ * BluetoothDevice.ERROR)</code>
*/
- public BluetoothDevice(IBluetoothDevice service) {
- mService = service;
- }
+ public static final int ERROR = Integer.MIN_VALUE;
/**
- * Is Bluetooth currently turned on.
- *
- * @return true if Bluetooth enabled, false otherwise.
+ * Broadcast Action: Remote device discovered.
+ * <p>Sent when a remote device is found during discovery.
+ * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link
+ * #EXTRA_CLASS}. Can contain the extra fields {@link #EXTRA_NAME} and/or
+ * {@link #EXTRA_RSSI} if they are available.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
- public boolean isEnabled() {
- try {
- return mService.isEnabled();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return false;
- }
+ // TODO: Change API to not broadcast RSSI if not available (incoming connection)
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_FOUND =
+ "android.bluetooth.device.action.FOUND";
/**
- * Get the current state of Bluetooth.
- *
- * @return One of BLUETOOTH_STATE_ or BluetoothError.ERROR.
+ * Broadcast Action: Remote device disappeared.
+ * <p>Sent when a remote device that was found in the last discovery is not
+ * found in the current discovery.
+ * <p>Always contains the extra field {@link #EXTRA_DEVICE}.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+ * @hide
*/
- public int getBluetoothState() {
- try {
- return mService.getBluetoothState();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return BluetoothError.ERROR;
- }
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_DISAPPEARED =
+ "android.bluetooth.device.action.DISAPPEARED";
/**
- * Enable the Bluetooth device.
- * Turn on the underlying hardware.
- * This is an asynchronous call,
- * BluetoothIntent.BLUETOOTH_STATE_CHANGED_ACTION can be used to check if
- * and when the device is sucessfully enabled.
- * @return false if we cannot enable the Bluetooth device. True does not
- * imply the device was enabled, it only implies that so far there were no
- * problems.
+ * Broadcast Action: Bluetooth class of a remote device has changed.
+ * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link
+ * #EXTRA_CLASS}.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+ * @see {@link BluetoothClass}
*/
- public boolean enable() {
- try {
- return mService.enable();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return false;
- }
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_CLASS_CHANGED =
+ "android.bluetooth.device.action.CLASS_CHANGED";
/**
- * Disable the Bluetooth device.
- * This turns off the underlying hardware.
- *
- * @return true if successful, false otherwise.
+ * Broadcast Action: Indicates a low level (ACL) connection has been
+ * established with a remote device.
+ * <p>Always contains the extra field {@link #EXTRA_DEVICE}.
+ * <p>ACL connections are managed automatically by the Android Bluetooth
+ * stack.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
- public boolean disable() {
- try {
- return mService.disable(true);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return false;
- }
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_ACL_CONNECTED =
+ "android.bluetooth.device.action.ACL_CONNECTED";
- public String getAddress() {
- try {
- return mService.getAddress();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
- }
+ /**
+ * Broadcast Action: Indicates that a low level (ACL) disconnection has
+ * been requested for a remote device, and it will soon be disconnected.
+ * <p>This is useful for graceful disconnection. Applications should use
+ * this intent as a hint to immediately terminate higher level connections
+ * (RFCOMM, L2CAP, or profile connections) to the remote device.
+ * <p>Always contains the extra field {@link #EXTRA_DEVICE}.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_ACL_DISCONNECT_REQUESTED =
+ "android.bluetooth.device.action.ACL_DISCONNECT_REQUESTED";
/**
- * Get the friendly Bluetooth name of this device.
- *
- * This name is visible to remote Bluetooth devices. Currently it is only
- * possible to retrieve the Bluetooth name when Bluetooth is enabled.
- *
- * @return the Bluetooth name, or null if there was a problem.
+ * Broadcast Action: Indicates a low level (ACL) disconnection from a
+ * remote device.
+ * <p>Always contains the extra field {@link #EXTRA_DEVICE}.
+ * <p>ACL connections are managed automatically by the Android Bluetooth
+ * stack.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
- public String getName() {
- try {
- return mService.getName();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
- }
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_ACL_DISCONNECTED =
+ "android.bluetooth.device.action.ACL_DISCONNECTED";
/**
- * Set the friendly Bluetooth name of this device.
- *
- * This name is visible to remote Bluetooth devices. The Bluetooth Service
- * is responsible for persisting this name.
- *
- * @param name the name to set
- * @return true, if the name was successfully set. False otherwise.
+ * Broadcast Action: Indicates the friendly name of a remote device has
+ * been retrieved for the first time, or changed since the last retrieval.
+ * <p>Always contains the extra fields {@link #EXTRA_DEVICE} and {@link
+ * #EXTRA_NAME}.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
*/
- public boolean setName(String name) {
- try {
- return mService.setName(name);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return false;
- }
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_NAME_CHANGED =
+ "android.bluetooth.device.action.NAME_CHANGED";
- public String getVersion() {
- try {
- return mService.getVersion();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
- }
- public String getRevision() {
- try {
- return mService.getRevision();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
- }
- public String getManufacturer() {
- try {
- return mService.getManufacturer();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
- }
- public String getCompany() {
- try {
- return mService.getCompany();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
- }
+ /**
+ * Broadcast Action: Indicates a change in the bond state of a remote
+ * device. For example, if a device is bonded (paired).
+ * <p>Always contains the extra fields {@link #EXTRA_DEVICE}, {@link
+ * #EXTRA_BOND_STATE} and {@link #EXTRA_PREVIOUS_BOND_STATE}.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+ */
+ // Note: When EXTRA_BOND_STATE is BOND_NONE then this will also
+ // contain a hidden extra field EXTRA_REASON with the result code.
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_BOND_STATE_CHANGED =
+ "android.bluetooth.device.action.BOND_STATE_CHANGED";
/**
- * Get the current scan mode.
- * Used to determine if the local device is connectable and/or discoverable
- * @return Scan mode, one of SCAN_MODE_* or an error code
+ * Used as a Parcelable {@link BluetoothDevice} extra field in every intent
+ * broadcast by this class. It contains the {@link BluetoothDevice} that
+ * the intent applies to.
*/
- public int getScanMode() {
- try {
- return mService.getScanMode();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return BluetoothError.ERROR_IPC;
- }
+ public static final String EXTRA_DEVICE = "android.bluetooth.device.extra.DEVICE";
/**
- * Set the current scan mode.
- * Used to make the local device connectable and/or discoverable
- * @param scanMode One of SCAN_MODE_*
+ * Used as a String extra field in {@link #ACTION_NAME_CHANGED} and {@link
+ * #ACTION_FOUND} intents. It contains the friendly Bluetooth name.
*/
- public void setScanMode(int scanMode) {
- try {
- mService.setScanMode(scanMode);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- }
+ public static final String EXTRA_NAME = "android.bluetooth.device.extra.NAME";
- public int getDiscoverableTimeout() {
- try {
- return mService.getDiscoverableTimeout();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return -1;
- }
- public void setDiscoverableTimeout(int timeout) {
- try {
- mService.setDiscoverableTimeout(timeout);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
+ /**
+ * Used as an optional short extra field in {@link #ACTION_FOUND} intents.
+ * Contains the RSSI value of the remote device as reported by the
+ * Bluetooth hardware.
+ */
+ public static final String EXTRA_RSSI = "android.bluetooth.device.extra.RSSI";
+
+ /**
+ * Used as an Parcelable {@link BluetoothClass} extra field in {@link
+ * #ACTION_FOUND} and {@link #ACTION_CLASS_CHANGED} intents.
+ */
+ public static final String EXTRA_CLASS = "android.bluetooth.device.extra.CLASS";
+
+ /**
+ * Used as an int extra field in {@link #ACTION_BOND_STATE_CHANGED} intents.
+ * Contains the bond state of the remote device.
+ * <p>Possible values are:
+ * {@link #BOND_NONE},
+ * {@link #BOND_BONDING},
+ * {@link #BOND_BONDED}.
+ */
+ public static final String EXTRA_BOND_STATE = "android.bluetooth.device.extra.BOND_STATE";
+ /**
+ * Used as an int extra field in {@link #ACTION_BOND_STATE_CHANGED} intents.
+ * Contains the previous bond state of the remote device.
+ * <p>Possible values are:
+ * {@link #BOND_NONE},
+ * {@link #BOND_BONDING},
+ * {@link #BOND_BONDED}.
+ */
+ public static final String EXTRA_PREVIOUS_BOND_STATE =
+ "android.bluetooth.device.extra.PREVIOUS_BOND_STATE";
+ /**
+ * Indicates the remote device is not bonded (paired).
+ * <p>There is no shared link key with the remote device, so communication
+ * (if it is allowed at all) will be unauthenticated and unencrypted.
+ */
+ public static final int BOND_NONE = 10;
+ /**
+ * Indicates bonding (pairing) is in progress with the remote device.
+ */
+ public static final int BOND_BONDING = 11;
+ /**
+ * Indicates the remote device is bonded (paired).
+ * <p>A shared link keys exists locally for the remote device, so
+ * communication can be authenticated and encrypted.
+ * <p><i>Being bonded (paired) with a remote device does not necessarily
+ * mean the device is currently connected. It just means that the ponding
+ * procedure was compeleted at some earlier time, and the link key is still
+ * stored locally, ready to use on the next connection.
+ * </i>
+ */
+ public static final int BOND_BONDED = 12;
+
+ /** @hide */
+ public static final String EXTRA_REASON = "android.bluetooth.device.extra.REASON";
+ /** @hide */
+ public static final String EXTRA_PAIRING_VARIANT =
+ "android.bluetooth.device.extra.PAIRING_VARIANT";
+ /** @hide */
+ public static final String EXTRA_PASSKEY = "android.bluetooth.device.extra.PASSKEY";
+
+ /**
+ * Broadcast Action: This intent is used to broadcast the {@link UUID}
+ * wrapped as a {@link android.os.ParcelUuid} of the remote device after it
+ * has been fetched. This intent is sent only when the UUIDs of the remote
+ * device are requested to be fetched using Service Discovery Protocol
+ * <p> Always contains the extra field {@link #EXTRA_DEVICE}
+ * <p> Always contains the extra filed {@link #EXTRA_UUID}
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_UUID =
+ "android.bleutooth.device.action.UUID";
+
+ /**
+ * Broadcast Action: Indicates a failure to retrieve the name of a remote
+ * device.
+ * <p>Always contains the extra field {@link #EXTRA_DEVICE}.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH} to receive.
+ * @hide
+ */
+ //TODO: is this actually useful?
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_NAME_FAILED =
+ "android.bluetooth.device.action.NAME_FAILED";
+
+ /** @hide */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_PAIRING_REQUEST =
+ "android.bluetooth.device.action.PAIRING_REQUEST";
+ /** @hide */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_PAIRING_CANCEL =
+ "android.bluetooth.device.action.PAIRING_CANCEL";
+
+ /** A bond attempt succeeded
+ * @hide */
+ public static final int BOND_SUCCESS = 0;
+ /** A bond attempt failed because pins did not match, or remote device did
+ * not respond to pin request in time
+ * @hide */
+ public static final int UNBOND_REASON_AUTH_FAILED = 1;
+ /** A bond attempt failed because the other side explicilty rejected
+ * bonding
+ * @hide */
+ public static final int UNBOND_REASON_AUTH_REJECTED = 2;
+ /** A bond attempt failed because we canceled the bonding process
+ * @hide */
+ public static final int UNBOND_REASON_AUTH_CANCELED = 3;
+ /** A bond attempt failed because we could not contact the remote device
+ * @hide */
+ public static final int UNBOND_REASON_REMOTE_DEVICE_DOWN = 4;
+ /** A bond attempt failed because a discovery is in progress
+ * @hide */
+ public static final int UNBOND_REASON_DISCOVERY_IN_PROGRESS = 5;
+ /** A bond attempt failed because of authentication timeout
+ * @hide */
+ public static final int UNBOND_REASON_AUTH_TIMEOUT = 6;
+ /** A bond attempt failed because of repeated attempts
+ * @hide */
+ public static final int UNBOND_REASON_REPEATED_ATTEMPTS = 7;
+ /** A bond attempt failed because we received an Authentication Cancel
+ * by remote end
+ * @hide */
+ public static final int UNBOND_REASON_REMOTE_AUTH_CANCELED = 8;
+ /** An existing bond was explicitly revoked
+ * @hide */
+ public static final int UNBOND_REASON_REMOVED = 9;
+
+ /** The user will be prompted to enter a pin
+ * @hide */
+ public static final int PAIRING_VARIANT_PIN = 0;
+ /** The user will be prompted to enter a passkey
+ * @hide */
+ public static final int PAIRING_VARIANT_PASSKEY = 1;
+ /** The user will be prompted to confirm the passkey displayed on the screen
+ * @hide */
+ public static final int PAIRING_VARIANT_PASSKEY_CONFIRMATION = 2;
+ /** The user will be prompted to accept or deny the incoming pairing request
+ * @hide */
+ public static final int PAIRING_VARIANT_CONSENT = 3;
+ /** The user will be prompted to enter the passkey displayed on remote device
+ * @hide */
+ public static final int PAIRING_VARIANT_DISPLAY_PASSKEY = 4;
+
+ /**
+ * Used as an extra field in {@link #ACTION_UUID} intents,
+ * Contains the {@link android.os.ParcelUuid}s of the remote device which
+ * is a parcelable version of {@link UUID}.
+ * @hide
+ */
+ public static final String EXTRA_UUID = "android.bluetooth.device.extra.UUID";
+
+ /**
+ * Lazy initialization. Guaranteed final after first object constructed, or
+ * getService() called.
+ * TODO: Unify implementation of sService amongst BluetoothFoo API's
+ */
+ private static IBluetooth sService;
+
+ private final String mAddress;
+
+ /*package*/ static IBluetooth getService() {
+ synchronized (BluetoothDevice.class) {
+ if (sService == null) {
+ IBinder b = ServiceManager.getService(BluetoothAdapter.BLUETOOTH_SERVICE);
+ if (b == null) {
+ throw new RuntimeException("Bluetooth service not available");
+ }
+ sService = IBluetooth.Stub.asInterface(b);
+ }
+ }
+ return sService;
}
- public boolean startDiscovery() {
- return startDiscovery(true);
+ /**
+ * Create a new BluetoothDevice
+ * Bluetooth MAC address must be upper case, such as "00:11:22:33:AA:BB",
+ * and is validated in this constructor.
+ * @param address valid Bluetooth MAC address
+ * @throws RuntimeException Bluetooth is not available on this platform
+ * @throws IllegalArgumentException address is invalid
+ * @hide
+ */
+ /*package*/ BluetoothDevice(String address) {
+ getService(); // ensures sService is initialized
+ if (!BluetoothAdapter.checkBluetoothAddress(address)) {
+ throw new IllegalArgumentException(address + " is not a valid Bluetooth address");
+ }
+
+ mAddress = address;
}
- public boolean startDiscovery(boolean resolveNames) {
- try {
- return mService.startDiscovery(resolveNames);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
+
+ @Override
+ public boolean equals(Object o) {
+ if (o instanceof BluetoothDevice) {
+ return mAddress.equals(((BluetoothDevice)o).getAddress());
+ }
return false;
}
- public void cancelDiscovery() {
- try {
- mService.cancelDiscovery();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
+ @Override
+ public int hashCode() {
+ return mAddress.hashCode();
}
- public boolean isDiscovering() {
- try {
- return mService.isDiscovering();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return false;
+ /**
+ * Returns a string representation of this BluetoothDevice.
+ * <p>Currently this is the Bluetooth hardware address, for example
+ * "00:11:22:AA:BB:CC". However, you should always use {@link #getAddress}
+ * if you explicitly require the Bluetooth hardware address in case the
+ * {@link #toString} representation changes in the future.
+ * @return string representation of this BluetoothDevice
+ */
+ @Override
+ public String toString() {
+ return mAddress;
}
- public boolean startPeriodicDiscovery() {
- try {
- return mService.startPeriodicDiscovery();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return false;
+ public int describeContents() {
+ return 0;
}
- public boolean stopPeriodicDiscovery() {
- try {
- return mService.stopPeriodicDiscovery();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return false;
- }
- public boolean isPeriodicDiscovery() {
- try {
- return mService.isPeriodicDiscovery();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return false;
+
+ public static final Parcelable.Creator<BluetoothDevice> CREATOR =
+ new Parcelable.Creator<BluetoothDevice>() {
+ public BluetoothDevice createFromParcel(Parcel in) {
+ return new BluetoothDevice(in.readString());
+ }
+ public BluetoothDevice[] newArray(int size) {
+ return new BluetoothDevice[size];
+ }
+ };
+
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeString(mAddress);
}
- public String[] listRemoteDevices() {
- try {
- return mService.listRemoteDevices();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
+ /**
+ * Returns the hardware address of this BluetoothDevice.
+ * <p> For example, "00:11:22:AA:BB:CC".
+ * @return Bluetooth hardware address as string
+ */
+ public String getAddress() {
+ return mAddress;
}
/**
- * List remote devices that have a low level (ACL) connection.
- *
- * RFCOMM, SDP and L2CAP are all built on ACL connections. Devices can have
- * an ACL connection even when not paired - this is common for SDP queries
- * or for in-progress pairing requests.
+ * Get the friendly Bluetooth name of the remote device.
*
- * In most cases you probably want to test if a higher level protocol is
- * connected, rather than testing ACL connections.
+ * <p>The local adapter will automatically retrieve remote names when
+ * performing a device scan, and will cache them. This method just returns
+ * the name for this device from the cache.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
*
- * @return bluetooth hardware addresses of remote devices with a current
- * ACL connection. Array size is 0 if no devices have a
- * connection. Null on error.
+ * @return the Bluetooth name, or null if there was a problem.
*/
- public String[] listAclConnections() {
+ public String getName() {
try {
- return mService.listAclConnections();
+ return sService.getRemoteName(mAddress);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
/**
- * Check if a specified remote device has a low level (ACL) connection.
- *
- * RFCOMM, SDP and L2CAP are all built on ACL connections. Devices can have
- * an ACL connection even when not paired - this is common for SDP queries
- * or for in-progress pairing requests.
- *
- * In most cases you probably want to test if a higher level protocol is
- * connected, rather than testing ACL connections.
+ * Start the bonding (pairing) process with the remote device.
+ * <p>This is an asynchronous call, it will return immediately. Register
+ * for {@link #ACTION_BOND_STATE_CHANGED} intents to be notified when
+ * the bonding process completes, and its result.
+ * <p>Android system services will handle the necessary user interactions
+ * to confirm and complete the bonding process.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
*
- * @param address the Bluetooth hardware address you want to check.
- * @return true if there is an ACL connection, false otherwise and on
- * error.
+ * @return false on immediate error, true if bonding will begin
+ * @hide
*/
- public boolean isAclConnected(String address) {
+ public boolean createBond() {
try {
- return mService.isAclConnected(address);
+ return sService.createBond(mAddress);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
/**
- * Perform a low level (ACL) disconnection of a remote device.
+ * Cancel an in-progress bonding request started with {@link #createBond}.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
*
- * This forcably disconnects the ACL layer connection to a remote device,
- * which will cause all RFCOMM, SDP and L2CAP connections to this remote
- * device to close.
- *
- * @param address the Bluetooth hardware address you want to disconnect.
- * @return true if the device was disconnected, false otherwise and on
- * error.
+ * @return true on sucess, false on error
+ * @hide
*/
- public boolean disconnectRemoteDeviceAcl(String address) {
+ public boolean cancelBondProcess() {
try {
- return mService.disconnectRemoteDeviceAcl(address);
+ return sService.cancelBondProcess(mAddress);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
/**
- * Create a bonding with a remote bluetooth device.
- *
- * This is an asynchronous call. The result of this bonding attempt can be
- * observed through BluetoothIntent.BOND_STATE_CHANGED_ACTION intents.
+ * Remove bond (pairing) with the remote device.
+ * <p>Delete the link key associated with the remote device, and
+ * immediately terminate connections to that device that require
+ * authentication and encryption.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
*
- * @param address the remote device Bluetooth address.
- * @return false If there was an immediate problem creating the bonding,
- * true otherwise.
+ * @return true on sucess, false on error
+ * @hide
*/
- public boolean createBond(String address) {
+ public boolean removeBond() {
try {
- return mService.createBond(address);
+ return sService.removeBond(mAddress);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
/**
- * Cancel an in-progress bonding request started with createBond.
+ * Get the bond state of the remote device.
+ * <p>Possible values for the bond state are:
+ * {@link #BOND_NONE},
+ * {@link #BOND_BONDING},
+ * {@link #BOND_BONDED}.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
+ *
+ * @return the bond state
*/
- public boolean cancelBondProcess(String address) {
+ public int getBondState() {
try {
- return mService.cancelBondProcess(address);
+ return sService.getBondState(mAddress);
} catch (RemoteException e) {Log.e(TAG, "", e);}
- return false;
+ return BOND_NONE;
}
/**
- * Remove an already exisiting bonding (delete the link key).
+ * Get the Bluetooth class of the remote device.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
+ *
+ * @return Bluetooth class object, or null on error
*/
- public boolean removeBond(String address) {
+ public BluetoothClass getBluetoothClass() {
try {
- return mService.removeBond(address);
+ int classInt = sService.getRemoteClass(mAddress);
+ if (classInt == BluetoothClass.ERROR) return null;
+ return new BluetoothClass(classInt);
} catch (RemoteException e) {Log.e(TAG, "", e);}
- return false;
+ return null;
}
/**
- * List remote devices that are bonded (paired) to the local device.
- *
- * Bonding (pairing) is the process by which the user enters a pin code for
- * the device, which generates a shared link key, allowing for
- * authentication and encryption of future connections. In Android we
- * require bonding before RFCOMM or SCO connections can be made to a remote
- * device.
- *
- * This function lists which remote devices we have a link key for. It does
- * not cause any RF transmission, and does not check if the remote device
- * still has it's link key with us. If the other side no longer has its
- * link key then the RFCOMM or SCO connection attempt will result in an
- * error.
- *
- * This function does not check if the remote device is in range.
- *
- * Remote devices that have an in-progress bonding attempt are not
- * returned.
- *
- * @return bluetooth hardware addresses of remote devices that are
- * bonded. Array size is 0 if no devices are bonded. Null on error.
+ * Get trust state of a remote device.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}.
+ * @hide
*/
- public String[] listBonds() {
+ public boolean getTrustState() {
try {
- return mService.listBonds();
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
+ return sService.getTrustState(mAddress);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ return false;
}
/**
- * Get the bonding state of a remote device.
- *
- * Result is one of:
- * BluetoothError.*
- * BOND_*
- *
- * @param address Bluetooth hardware address of the remote device to check.
- * @return Result code
+ * Set trust state for a remote device.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
+ * @param value the trust state value (true or false)
+ * @hide
*/
- public int getBondState(String address) {
+ public boolean setTrust(boolean value) {
try {
- return mService.getBondState(address);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return BluetoothError.ERROR_IPC;
+ return sService.setTrust(mAddress, value);
+ } catch (RemoteException e) {
+ Log.e(TAG, "", e);
+ }
+ return false;
}
- public String getRemoteName(String address) {
+ /** @hide */
+ public ParcelUuid[] getUuids() {
try {
- return mService.getRemoteName(address);
+ return sService.getRemoteUuids(mAddress);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return null;
}
- public String getRemoteVersion(String address) {
- try {
- return mService.getRemoteVersion(address);
+ /**
+ * Perform a SDP query on the remote device to get the UUIDs
+ * supported. This API is asynchronous and an Intent is sent,
+ * with the UUIDs supported by the remote end. If there is an error
+ * in getting the SDP records or if the process takes a long time,
+ * an Intent is sent with the UUIDs that is currently present in the
+ * cache. Clients should use the {@link getUuids} to get UUIDs
+ * is SDP is not to be performed.
+ *
+ * @return False if the sanity check fails, True if the process
+ * of initiating an ACL connection to the remote device
+ * was started.
+ * @hide
+ */
+ public boolean fetchUuidsWithSdp() {
+ try {
+ return sService.fetchRemoteUuids(mAddress, null, null);
} catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
+ return false;
}
- public String getRemoteRevision(String address) {
+
+ /** @hide */
+ public int getServiceChannel(ParcelUuid uuid) {
+ try {
+ return sService.getRemoteServiceChannel(mAddress, uuid);
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+ return BluetoothDevice.ERROR;
+ }
+
+ /** @hide */
+ public boolean setPin(byte[] pin) {
try {
- return mService.getRemoteRevision(address);
+ return sService.setPin(mAddress, pin);
} catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
+ return false;
}
- public String getRemoteManufacturer(String address) {
+
+ /** @hide */
+ public boolean setPasskey(int passkey) {
try {
- return mService.getRemoteManufacturer(address);
+ return sService.setPasskey(mAddress, passkey);
} catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
+ return false;
}
- public String getRemoteCompany(String address) {
+
+ /** @hide */
+ public boolean setPairingConfirmation(boolean confirm) {
try {
- return mService.getRemoteCompany(address);
+ return sService.setPairingConfirmation(mAddress, confirm);
} catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
+ return false;
}
- /**
- * Returns the RFCOMM channel associated with the 16-byte UUID on
- * the remote Bluetooth address.
- *
- * Performs a SDP ServiceSearchAttributeRequest transaction. The provided
- * uuid is verified in the returned record. If there was a problem, or the
- * specified uuid does not exist, -1 is returned.
- */
- public boolean getRemoteServiceChannel(String address, short uuid16,
- IBluetoothDeviceCallback callback) {
+ /** @hide */
+ public boolean cancelPairingUserInput() {
try {
- return mService.getRemoteServiceChannel(address, uuid16, callback);
+ return sService.cancelPairingUserInput(mAddress);
} catch (RemoteException e) {Log.e(TAG, "", e);}
return false;
}
/**
- * Get the major, minor and servics classes of a remote device.
- * These classes are encoded as a 32-bit integer. See BluetoothClass.
- * @param address remote device
- * @return 32-bit class suitable for use with BluetoothClass, or
- * BluetoothClass.ERROR on error
+ * Create an RFCOMM {@link BluetoothSocket} ready to start a secure
+ * outgoing connection to this remote device on given channel.
+ * <p>The remote device will be authenticated and communication on this
+ * socket will be encrypted.
+ * <p>Use {@link BluetoothSocket#connect} to intiate the outgoing
+ * connection.
+ * <p>Valid RFCOMM channels are in range 1 to 30.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+ *
+ * @param channel RFCOMM channel to connect to
+ * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
+ * @throws IOException on error, for example Bluetooth not available, or
+ * insufficient permissions
+ * @hide
*/
- public int getRemoteClass(String address) {
- try {
- return mService.getRemoteClass(address);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return BluetoothClass.ERROR;
+ public BluetoothSocket createRfcommSocket(int channel) throws IOException {
+ return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, channel,
+ null);
}
- public byte[] getRemoteFeatures(String address) {
- try {
- return mService.getRemoteFeatures(address);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
- }
- public String lastSeen(String address) {
- try {
- return mService.lastSeen(address);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
- }
- public String lastUsed(String address) {
- try {
- return mService.lastUsed(address);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return null;
+ /**
+ * Create an RFCOMM {@link BluetoothSocket} ready to start a secure
+ * outgoing connection to this remote device using SDP lookup of uuid.
+ * <p>This is designed to be used with {@link
+ * BluetoothAdapter#listenUsingRfcommWithServiceRecord} for peer-peer
+ * Bluetooth applications.
+ * <p>Use {@link BluetoothSocket#connect} to intiate the outgoing
+ * connection. This will also perform an SDP lookup of the given uuid to
+ * determine which channel to connect to.
+ * <p>The remote device will be authenticated and communication on this
+ * socket will be encrypted.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH}
+ *
+ * @param uuid service record uuid to lookup RFCOMM channel
+ * @return a RFCOMM BluetoothServerSocket ready for an outgoing connection
+ * @throws IOException on error, for example Bluetooth not available, or
+ * insufficient permissions
+ */
+ public BluetoothSocket createRfcommSocketToServiceRecord(UUID uuid) throws IOException {
+ return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, true, true, this, -1,
+ new ParcelUuid(uuid));
}
- public boolean setPin(String address, byte[] pin) {
- try {
- return mService.setPin(address, pin);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return false;
+ /**
+ * Construct an insecure RFCOMM socket ready to start an outgoing
+ * connection.
+ * Call #connect on the returned #BluetoothSocket to begin the connection.
+ * The remote device will not be authenticated and communication on this
+ * socket will not be encrypted.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ *
+ * @param port remote port
+ * @return An RFCOMM BluetoothSocket
+ * @throws IOException On error, for example Bluetooth not available, or
+ * insufficient permissions.
+ * @hide
+ */
+ public BluetoothSocket createInsecureRfcommSocket(int port) throws IOException {
+ return new BluetoothSocket(BluetoothSocket.TYPE_RFCOMM, -1, false, false, this, port,
+ null);
}
- public boolean cancelPin(String address) {
- try {
- return mService.cancelPin(address);
- } catch (RemoteException e) {Log.e(TAG, "", e);}
- return false;
+
+ /**
+ * Construct a SCO socket ready to start an outgoing connection.
+ * Call #connect on the returned #BluetoothSocket to begin the connection.
+ * <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+ *
+ * @return a SCO BluetoothSocket
+ * @throws IOException on error, for example Bluetooth not available, or
+ * insufficient permissions.
+ * @hide
+ */
+ public BluetoothSocket createScoSocket() throws IOException {
+ return new BluetoothSocket(BluetoothSocket.TYPE_SCO, -1, true, true, this, -1, null);
}
/**
@@ -534,6 +708,7 @@ public class BluetoothDevice {
* @param pin pin as java String
* @return the pin code as a UTF8 byte array, or null if it is an invalid
* Bluetooth pin.
+ * @hide
*/
public static byte[] convertPinToBytes(String pin) {
if (pin == null) {
@@ -552,28 +727,4 @@ public class BluetoothDevice {
return pinBytes;
}
- private static final int ADDRESS_LENGTH = 17;
- /** Sanity check a bluetooth address, such as "00:43:A8:23:10:F0" */
- public static boolean checkBluetoothAddress(String address) {
- if (address == null || address.length() != ADDRESS_LENGTH) {
- return false;
- }
- for (int i = 0; i < ADDRESS_LENGTH; i++) {
- char c = address.charAt(i);
- switch (i % 3) {
- case 0:
- case 1:
- if (Character.digit(c, 16) != -1) {
- break; // hex character, OK
- }
- return false;
- case 2:
- if (c == ':') {
- break; // OK
- }
- return false;
- }
- }
- return true;
- }
}
diff --git a/core/java/android/bluetooth/BluetoothDevicePicker.java b/core/java/android/bluetooth/BluetoothDevicePicker.java
new file mode 100644
index 0000000..05eed0e
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothDevicePicker.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
+
+/**
+ * A helper to show a system "Device Picker" activity to the user.
+ *
+ * @hide
+ */
+public interface BluetoothDevicePicker {
+ public static final String EXTRA_NEED_AUTH =
+ "android.bluetooth.devicepicker.extra.NEED_AUTH";
+ public static final String EXTRA_FILTER_TYPE =
+ "android.bluetooth.devicepicker.extra.FILTER_TYPE";
+ public static final String EXTRA_LAUNCH_PACKAGE =
+ "android.bluetooth.devicepicker.extra.LAUNCH_PACKAGE";
+ public static final String EXTRA_LAUNCH_CLASS =
+ "android.bluetooth.devicepicker.extra.DEVICE_PICKER_LAUNCH_CLASS";
+
+ /**
+ * Broadcast when one BT device is selected from BT device picker screen.
+ * Selected BT device address is contained in extra string {@link BluetoothIntent}
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_DEVICE_SELECTED =
+ "android.bluetooth.devicepicker.action.DEVICE_SELECTED";
+
+ /**
+ * Broadcast when someone want to select one BT device from devices list.
+ * This intent contains below extra data:
+ * - {@link #EXTRA_NEED_AUTH} (boolean): if need authentication
+ * - {@link #EXTRA_FILTER_TYPE} (int): what kinds of device should be
+ * listed
+ * - {@link #EXTRA_LAUNCH_PACKAGE} (string): where(which package) this
+ * intent come from
+ * - {@link #EXTRA_LAUNCH_CLASS} (string): where(which class) this intent
+ * come from
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_LAUNCH =
+ "android.bluetooth.devicepicker.action.LAUNCH";
+
+ /** Ask device picker to show all kinds of BT devices */
+ public static final int FILTER_TYPE_ALL = 0;
+ /** Ask device picker to show BT devices that support AUDIO profiles */
+ public static final int FILTER_TYPE_AUDIO = 1;
+ /** Ask device picker to show BT devices that support Object Transfer */
+ public static final int FILTER_TYPE_TRANSFER = 2;
+}
diff --git a/core/java/android/bluetooth/BluetoothHeadset.java b/core/java/android/bluetooth/BluetoothHeadset.java
index fe1e09a..90cff6b 100644
--- a/core/java/android/bluetooth/BluetoothHeadset.java
+++ b/core/java/android/bluetooth/BluetoothHeadset.java
@@ -16,6 +16,8 @@
package android.bluetooth;
+import android.annotation.SdkConstant;
+import android.annotation.SdkConstant.SdkConstantType;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
@@ -49,11 +51,32 @@ import android.util.Log;
*
* @hide
*/
-public class BluetoothHeadset {
+public final class BluetoothHeadset {
private static final String TAG = "BluetoothHeadset";
private static final boolean DBG = false;
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_STATE_CHANGED =
+ "android.bluetooth.headset.action.STATE_CHANGED";
+ /**
+ * TODO(API release): Consider incorporating as new state in
+ * HEADSET_STATE_CHANGED
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ public static final String ACTION_AUDIO_STATE_CHANGED =
+ "android.bluetooth.headset.action.AUDIO_STATE_CHANGED";
+ public static final String EXTRA_STATE =
+ "android.bluetooth.headset.extra.STATE";
+ public static final String EXTRA_PREVIOUS_STATE =
+ "android.bluetooth.headset.extra.PREVIOUS_STATE";
+ public static final String EXTRA_AUDIO_STATE =
+ "android.bluetooth.headset.extra.AUDIO_STATE";
+
+ /**
+ * TODO(API release): Consider incorporating as new state in
+ * HEADSET_STATE_CHANGED
+ */
private IBluetoothHeadset mService;
private final Context mContext;
private final ServiceListener mServiceListener;
@@ -163,16 +186,16 @@ public class BluetoothHeadset {
}
/**
- * Get the Bluetooth address of the current headset.
- * @return The Bluetooth address, or null if not in connected or connecting
+ * Get the BluetoothDevice for the current headset.
+ * @return current headset, or null if not in connected or connecting
* state, or if this proxy object is not connected to the Headset
* service.
*/
- public String getHeadsetAddress() {
- if (DBG) log("getHeadsetAddress()");
+ public BluetoothDevice getCurrentHeadset() {
+ if (DBG) log("getCurrentHeadset()");
if (mService != null) {
try {
- return mService.getHeadsetAddress();
+ return mService.getCurrentHeadset();
} catch (RemoteException e) {Log.e(TAG, e.toString());}
} else {
Log.w(TAG, "Proxy not attached to service");
@@ -185,19 +208,19 @@ public class BluetoothHeadset {
* Request to initiate a connection to a headset.
* This call does not block. Fails if a headset is already connecting
* or connected.
- * Initiates auto-connection if address is null. Tries to connect to all
+ * Initiates auto-connection if device is null. Tries to connect to all
* devices with priority greater than PRIORITY_AUTO in descending order.
- * @param address The Bluetooth Address to connect to, or null to
- * auto-connect to the last connected headset.
- * @return False if there was a problem initiating the connection
- * procedure, and no further HEADSET_STATE_CHANGED intents
- * will be expected.
+ * @param device device to connect to, or null to auto-connect last connected
+ * headset
+ * @return false if there was a problem initiating the connection
+ * procedure, and no further HEADSET_STATE_CHANGED intents
+ * will be expected.
*/
- public boolean connectHeadset(String address) {
- if (DBG) log("connectHeadset(" + address + ")");
+ public boolean connectHeadset(BluetoothDevice device) {
+ if (DBG) log("connectHeadset(" + device + ")");
if (mService != null) {
try {
- if (mService.connectHeadset(address)) {
+ if (mService.connectHeadset(device)) {
return true;
}
} catch (RemoteException e) {Log.e(TAG, e.toString());}
@@ -213,11 +236,11 @@ public class BluetoothHeadset {
* connecting). Returns false if not connected, or if this proxy object
* if not currently connected to the headset service.
*/
- public boolean isConnected(String address) {
- if (DBG) log("isConnected(" + address + ")");
+ public boolean isConnected(BluetoothDevice device) {
+ if (DBG) log("isConnected(" + device + ")");
if (mService != null) {
try {
- return mService.isConnected(address);
+ return mService.isConnected(device);
} catch (RemoteException e) {Log.e(TAG, e.toString());}
} else {
Log.w(TAG, "Proxy not attached to service");
@@ -295,16 +318,16 @@ public class BluetoothHeadset {
* auto-connected.
* Incoming connections are ignored regardless of priority if there is
* already a headset connected.
- * @param address Paired headset
+ * @param device paired headset
* @param priority Integer priority, for example PRIORITY_AUTO or
* PRIORITY_NONE
- * @return True if successful, false if there was some error.
+ * @return true if successful, false if there was some error
*/
- public boolean setPriority(String address, int priority) {
- if (DBG) log("setPriority(" + address + ", " + priority + ")");
+ public boolean setPriority(BluetoothDevice device, int priority) {
+ if (DBG) log("setPriority(" + device + ", " + priority + ")");
if (mService != null) {
try {
- return mService.setPriority(address, priority);
+ return mService.setPriority(device, priority);
} catch (RemoteException e) {Log.e(TAG, e.toString());}
} else {
Log.w(TAG, "Proxy not attached to service");
@@ -315,14 +338,14 @@ public class BluetoothHeadset {
/**
* Get priority of headset.
- * @param address Headset
- * @return non-negative priority, or negative error code on error.
+ * @param device headset
+ * @return non-negative priority, or negative error code on error
*/
- public int getPriority(String address) {
- if (DBG) log("getPriority(" + address + ")");
+ public int getPriority(BluetoothDevice device) {
+ if (DBG) log("getPriority(" + device + ")");
if (mService != null) {
try {
- return mService.getPriority(address);
+ return mService.getPriority(device);
} catch (RemoteException e) {Log.e(TAG, e.toString());}
} else {
Log.w(TAG, "Proxy not attached to service");
@@ -356,30 +379,6 @@ public class BluetoothHeadset {
return -1;
}
- /**
- * Check class bits for possible HSP or HFP support.
- * This is a simple heuristic that tries to guess if a device with the
- * given class bits might support HSP or HFP. It is not accurate for all
- * devices. It tries to err on the side of false positives.
- * @return True if this device might support HSP or HFP.
- */
- public static boolean doesClassMatch(int btClass) {
- // The render service class is required by the spec for HFP, so is a
- // pretty good signal
- if (BluetoothClass.Service.hasService(btClass, BluetoothClass.Service.RENDER)) {
- return true;
- }
- // Just in case they forgot the render service class
- switch (BluetoothClass.Device.getDevice(btClass)) {
- case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
- case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
- case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
- return true;
- default:
- return false;
- }
- }
-
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
if (DBG) Log.d(TAG, "Proxy object connected");
diff --git a/core/java/android/bluetooth/BluetoothInputStream.java b/core/java/android/bluetooth/BluetoothInputStream.java
index ceae70c..03af953 100644
--- a/core/java/android/bluetooth/BluetoothInputStream.java
+++ b/core/java/android/bluetooth/BluetoothInputStream.java
@@ -24,7 +24,6 @@ import java.io.InputStream;
*
* Used to write to a Bluetooth socket.
*
- * TODO: Implement bulk writes (instead of one byte at a time).
* @hide
*/
/*package*/ final class BluetoothInputStream extends InputStream {
@@ -38,7 +37,7 @@ import java.io.InputStream;
* Return number of bytes available before this stream will block.
*/
public int available() throws IOException {
- return mSocket.availableNative();
+ return mSocket.available();
}
public void close() throws IOException {
@@ -54,9 +53,46 @@ import java.io.InputStream;
* @return the byte read or -1 if the end of stream has been reached.
* @throws IOException
* if the stream is closed or another IOException occurs.
- * @since Android 1.0
+ * @since Android 1.5
*/
public int read() throws IOException {
- return mSocket.readNative();
+ byte b[] = new byte[1];
+ int ret = mSocket.read(b, 0, 1);
+ if (ret == 1) {
+ return (int)b[0] & 0xff;
+ } else {
+ return -1;
+ }
+ }
+
+ /**
+ * Reads at most {@code length} bytes from this stream and stores them in
+ * the byte array {@code b} starting at {@code offset}.
+ *
+ * @param b
+ * the byte array in which to store the bytes read.
+ * @param offset
+ * the initial position in {@code buffer} to store the bytes
+ * read from this stream.
+ * @param length
+ * the maximum number of bytes to store in {@code b}.
+ * @return the number of bytes actually read or -1 if the end of the stream
+ * has been reached.
+ * @throws IndexOutOfBoundsException
+ * if {@code offset < 0} or {@code length < 0}, or if
+ * {@code offset + length} is greater than the length of
+ * {@code b}.
+ * @throws IOException
+ * if the stream is closed or another IOException occurs.
+ * @since Android 1.5
+ */
+ public int read(byte[] b, int offset, int length) throws IOException {
+ if (b == null) {
+ throw new NullPointerException("byte array is null");
+ }
+ if ((offset | length) < 0 || length > b.length - offset) {
+ throw new ArrayIndexOutOfBoundsException("invalid offset or length");
+ }
+ return mSocket.read(b, offset, length);
}
}
diff --git a/core/java/android/bluetooth/BluetoothIntent.java b/core/java/android/bluetooth/BluetoothIntent.java
deleted file mode 100644
index 344601b..0000000
--- a/core/java/android/bluetooth/BluetoothIntent.java
+++ /dev/null
@@ -1,145 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.bluetooth;
-
-import android.annotation.SdkConstant;
-import android.annotation.SdkConstant.SdkConstantType;
-
-/**
- * The Android Bluetooth API is not finalized, and *will* change. Use at your
- * own risk.
- *
- * Manages the local Bluetooth device. Scan for devices, create bondings,
- * power up and down the adapter.
- *
- * @hide
- */
-public interface BluetoothIntent {
- public static final String SCAN_MODE =
- "android.bluetooth.intent.SCAN_MODE";
- public static final String ADDRESS =
- "android.bluetooth.intent.ADDRESS";
- public static final String NAME =
- "android.bluetooth.intent.NAME";
- public static final String ALIAS =
- "android.bluetooth.intent.ALIAS";
- public static final String RSSI =
- "android.bluetooth.intent.RSSI";
- public static final String CLASS =
- "android.bluetooth.intent.CLASS";
- public static final String BLUETOOTH_STATE =
- "android.bluetooth.intent.BLUETOOTH_STATE";
- public static final String BLUETOOTH_PREVIOUS_STATE =
- "android.bluetooth.intent.BLUETOOTH_PREVIOUS_STATE";
- public static final String HEADSET_STATE =
- "android.bluetooth.intent.HEADSET_STATE";
- public static final String HEADSET_PREVIOUS_STATE =
- "android.bluetooth.intent.HEADSET_PREVIOUS_STATE";
- public static final String HEADSET_AUDIO_STATE =
- "android.bluetooth.intent.HEADSET_AUDIO_STATE";
- public static final String BOND_STATE =
- "android.bluetooth.intent.BOND_STATE";
- public static final String BOND_PREVIOUS_STATE =
- "android.bluetooth.intent.BOND_PREVIOUS_STATE";
- public static final String REASON =
- "android.bluetooth.intent.REASON";
-
- /** Broadcast when the local Bluetooth device state changes, for example
- * when Bluetooth is enabled. Will contain int extra's BLUETOOTH_STATE and
- * BLUETOOTH_PREVIOUS_STATE. */
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String BLUETOOTH_STATE_CHANGED_ACTION =
- "android.bluetooth.intent.action.BLUETOOTH_STATE_CHANGED";
-
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String NAME_CHANGED_ACTION =
- "android.bluetooth.intent.action.NAME_CHANGED";
-
- /**
- * Broadcast when the scan mode changes. Always contains an int extra
- * named SCAN_MODE that contains the new scan mode.
- */
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String SCAN_MODE_CHANGED_ACTION =
- "android.bluetooth.intent.action.SCAN_MODE_CHANGED";
-
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String DISCOVERY_STARTED_ACTION =
- "android.bluetooth.intent.action.DISCOVERY_STARTED";
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String DISCOVERY_COMPLETED_ACTION =
- "android.bluetooth.intent.action.DISCOVERY_COMPLETED";
-
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String PAIRING_REQUEST_ACTION =
- "android.bluetooth.intent.action.PAIRING_REQUEST";
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String PAIRING_CANCEL_ACTION =
- "android.bluetooth.intent.action.PAIRING_CANCEL";
-
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String REMOTE_DEVICE_FOUND_ACTION =
- "android.bluetooth.intent.action.REMOTE_DEVICE_FOUND";
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String REMOTE_DEVICE_DISAPPEARED_ACTION =
- "android.bluetooth.intent.action.REMOTE_DEVICE_DISAPPEARED";
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String REMOTE_DEVICE_CLASS_UPDATED_ACTION =
- "android.bluetooth.intent.action.REMOTE_DEVICE_DISAPPEARED";
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String REMOTE_DEVICE_CONNECTED_ACTION =
- "android.bluetooth.intent.action.REMOTE_DEVICE_CONNECTED";
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String REMOTE_DEVICE_DISCONNECT_REQUESTED_ACTION =
- "android.bluetooth.intent.action.REMOTE_DEVICE_DISCONNECT_REQUESTED";
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String REMOTE_DEVICE_DISCONNECTED_ACTION =
- "android.bluetooth.intent.action.REMOTE_DEVICE_DISCONNECTED";
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String REMOTE_NAME_UPDATED_ACTION =
- "android.bluetooth.intent.action.REMOTE_NAME_UPDATED";
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String REMOTE_NAME_FAILED_ACTION =
- "android.bluetooth.intent.action.REMOTE_NAME_FAILED";
-
- /**
- * Broadcast when the bond state of a remote device changes.
- * Has string extra ADDRESS and int extras BOND_STATE and
- * BOND_PREVIOUS_STATE.
- * If BOND_STATE is BluetoothDevice.BOND_NOT_BONDED then will
- * also have an int extra REASON with a value of:
- * BluetoothDevice.BOND_RESULT_*
- * */
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String BOND_STATE_CHANGED_ACTION =
- "android.bluetooth.intent.action.BOND_STATE_CHANGED_ACTION";
-
- /**
- * TODO(API release): Move into BluetoothHeadset
- */
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String HEADSET_STATE_CHANGED_ACTION =
- "android.bluetooth.intent.action.HEADSET_STATE_CHANGED";
-
- /**
- * TODO(API release): Consider incorporating as new state in
- * HEADSET_STATE_CHANGED
- */
- @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
- public static final String HEADSET_AUDIO_STATE_CHANGED_ACTION =
- "android.bluetooth.intent.action.HEADSET_ADUIO_STATE_CHANGED";
-}
diff --git a/core/java/android/bluetooth/BluetoothOutputStream.java b/core/java/android/bluetooth/BluetoothOutputStream.java
index 32e6d17..62242a2 100644
--- a/core/java/android/bluetooth/BluetoothOutputStream.java
+++ b/core/java/android/bluetooth/BluetoothOutputStream.java
@@ -24,7 +24,6 @@ import java.io.OutputStream;
*
* Used to read from a Bluetooth socket.
*
- * TODO: Implement bulk reads (instead of one byte at a time).
* @hide
*/
/*package*/ final class BluetoothOutputStream extends OutputStream {
@@ -52,6 +51,37 @@ import java.io.OutputStream;
* @since Android 1.0
*/
public void write(int oneByte) throws IOException {
- mSocket.writeNative(oneByte);
+ byte b[] = new byte[1];
+ b[0] = (byte)oneByte;
+ mSocket.write(b, 0, 1);
+ }
+
+ /**
+ * Writes {@code count} bytes from the byte array {@code buffer} starting
+ * at position {@code offset} to this stream.
+ *
+ * @param b
+ * the buffer to be written.
+ * @param offset
+ * the start position in {@code buffer} from where to get bytes.
+ * @param count
+ * the number of bytes from {@code buffer} to write to this
+ * stream.
+ * @throws IOException
+ * if an error occurs while writing to this stream.
+ * @throws IndexOutOfBoundsException
+ * if {@code offset < 0} or {@code count < 0}, or if
+ * {@code offset + count} is bigger than the length of
+ * {@code buffer}.
+ * @since Android 1.0
+ */
+ public void write(byte[] b, int offset, int count) throws IOException {
+ if (b == null) {
+ throw new NullPointerException("buffer is null");
+ }
+ if ((offset | count) < 0 || count > b.length - offset) {
+ throw new IndexOutOfBoundsException("invalid offset or length");
+ }
+ mSocket.write(b, offset, count);
}
}
diff --git a/core/java/android/bluetooth/BluetoothPbap.java b/core/java/android/bluetooth/BluetoothPbap.java
new file mode 100644
index 0000000..b48f48e
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothPbap.java
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.ServiceManager;
+import android.util.Log;
+
+/**
+ * The Android Bluetooth API is not finalized, and *will* change. Use at your
+ * own risk.
+ *
+ * Public API for controlling the Bluetooth Pbap Service. This includes
+ * Bluetooth Phone book Access profile.
+ * BluetoothPbap is a proxy object for controlling the Bluetooth Pbap
+ * Service via IPC.
+ *
+ * Creating a BluetoothPbap object will create a binding with the
+ * BluetoothPbap service. Users of this object should call close() when they
+ * are finished with the BluetoothPbap, so that this proxy object can unbind
+ * from the service.
+ *
+ * This BluetoothPbap object is not immediately bound to the
+ * BluetoothPbap service. Use the ServiceListener interface to obtain a
+ * notification when it is bound, this is especially important if you wish to
+ * immediately call methods on BluetoothPbap after construction.
+ *
+ * Android only supports one connected Bluetooth Pce at a time.
+ *
+ * @hide
+ */
+public class BluetoothPbap {
+
+ private static final String TAG = "BluetoothPbap";
+ private static final boolean DBG = false;
+
+ /** int extra for PBAP_STATE_CHANGED_ACTION */
+ public static final String PBAP_STATE =
+ "android.bluetooth.pbap.intent.PBAP_STATE";
+ /** int extra for PBAP_STATE_CHANGED_ACTION */
+ public static final String PBAP_PREVIOUS_STATE =
+ "android.bluetooth.pbap.intent.PBAP_PREVIOUS_STATE";
+
+ /** Indicates the state of an pbap connection state has changed.
+ * This intent will always contain PBAP_STATE, PBAP_PREVIOUS_STATE and
+ * BluetoothIntent.ADDRESS extras.
+ */
+ public static final String PBAP_STATE_CHANGED_ACTION =
+ "android.bluetooth.pbap.intent.action.PBAP_STATE_CHANGED";
+
+ private IBluetoothPbap mService;
+ private final Context mContext;
+ private final ServiceListener mServiceListener;
+
+ /** There was an error trying to obtain the state */
+ public static final int STATE_ERROR = -1;
+ /** No client currently connected */
+ public static final int STATE_DISCONNECTED = 0;
+ /** Connection attempt in progress */
+ public static final int STATE_CONNECTING = 1;
+ /** Client is currently connected */
+ public static final int STATE_CONNECTED = 2;
+
+ public static final int RESULT_FAILURE = 0;
+ public static final int RESULT_SUCCESS = 1;
+ /** Connection canceled before completion. */
+ public static final int RESULT_CANCELED = 2;
+
+ /**
+ * An interface for notifying Bluetooth PCE IPC clients when they have
+ * been connected to the BluetoothPbap service.
+ */
+ public interface ServiceListener {
+ /**
+ * Called to notify the client when this proxy object has been
+ * connected to the BluetoothPbap service. Clients must wait for
+ * this callback before making IPC calls on the BluetoothPbap
+ * service.
+ */
+ public void onServiceConnected();
+
+ /**
+ * Called to notify the client that this proxy object has been
+ * disconnected from the BluetoothPbap service. Clients must not
+ * make IPC calls on the BluetoothPbap service after this callback.
+ * This callback will currently only occur if the application hosting
+ * the BluetoothPbap service, but may be called more often in future.
+ */
+ public void onServiceDisconnected();
+ }
+
+ /**
+ * Create a BluetoothPbap proxy object.
+ */
+ public BluetoothPbap(Context context, ServiceListener l) {
+ mContext = context;
+ mServiceListener = l;
+ if (!context.bindService(new Intent(IBluetoothPbap.class.getName()), mConnection, 0)) {
+ Log.e(TAG, "Could not bind to Bluetooth Pbap Service");
+ }
+ }
+
+ protected void finalize() throws Throwable {
+ try {
+ close();
+ } finally {
+ super.finalize();
+ }
+ }
+
+ /**
+ * Close the connection to the backing service.
+ * Other public functions of BluetoothPbap will return default error
+ * results once close() has been called. Multiple invocations of close()
+ * are ok.
+ */
+ public synchronized void close() {
+ if (mConnection != null) {
+ mContext.unbindService(mConnection);
+ mConnection = null;
+ }
+ }
+
+ /**
+ * Get the current state of the BluetoothPbap service.
+ * @return One of the STATE_ return codes, or STATE_ERROR if this proxy
+ * object is currently not connected to the Pbap service.
+ */
+ public int getState() {
+ if (DBG) log("getState()");
+ if (mService != null) {
+ try {
+ return mService.getState();
+ } catch (RemoteException e) {Log.e(TAG, e.toString());}
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ }
+ return BluetoothPbap.STATE_ERROR;
+ }
+
+ /**
+ * Get the currently connected remote Bluetooth device (PCE).
+ * @return The remote Bluetooth device, or null if not in connected or
+ * connecting state, or if this proxy object is not connected to
+ * the Pbap service.
+ */
+ public BluetoothDevice getClient() {
+ if (DBG) log("getClient()");
+ if (mService != null) {
+ try {
+ return mService.getClient();
+ } catch (RemoteException e) {Log.e(TAG, e.toString());}
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ }
+ return null;
+ }
+
+ /**
+ * Returns true if the specified Bluetooth device is connected (does not
+ * include connecting). Returns false if not connected, or if this proxy
+ * object is not currently connected to the Pbap service.
+ */
+ public boolean isConnected(BluetoothDevice device) {
+ if (DBG) log("isConnected(" + device + ")");
+ if (mService != null) {
+ try {
+ return mService.isConnected(device);
+ } catch (RemoteException e) {Log.e(TAG, e.toString());}
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ }
+ return false;
+ }
+
+ /**
+ * Disconnects the current Pbap client (PCE). Currently this call blocks,
+ * it may soon be made asynchornous. Returns false if this proxy object is
+ * not currently connected to the Pbap service.
+ */
+ public boolean disconnect() {
+ if (DBG) log("disconnect()");
+ if (mService != null) {
+ try {
+ mService.disconnect();
+ return true;
+ } catch (RemoteException e) {Log.e(TAG, e.toString());}
+ } else {
+ Log.w(TAG, "Proxy not attached to service");
+ if (DBG) log(Log.getStackTraceString(new Throwable()));
+ }
+ return false;
+ }
+
+ /**
+ * Check class bits for possible PBAP support.
+ * This is a simple heuristic that tries to guess if a device with the
+ * given class bits might support PBAP. It is not accurate for all
+ * devices. It tries to err on the side of false positives.
+ * @return True if this device might support PBAP.
+ */
+ public static boolean doesClassMatchSink(BluetoothClass btClass) {
+ // TODO optimize the rule
+ switch (btClass.getDeviceClass()) {
+ case BluetoothClass.Device.COMPUTER_DESKTOP:
+ case BluetoothClass.Device.COMPUTER_LAPTOP:
+ case BluetoothClass.Device.COMPUTER_SERVER:
+ case BluetoothClass.Device.COMPUTER_UNCATEGORIZED:
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private ServiceConnection mConnection = new ServiceConnection() {
+ public void onServiceConnected(ComponentName className, IBinder service) {
+ if (DBG) log("Proxy object connected");
+ mService = IBluetoothPbap.Stub.asInterface(service);
+ if (mServiceListener != null) {
+ mServiceListener.onServiceConnected();
+ }
+ }
+ public void onServiceDisconnected(ComponentName className) {
+ if (DBG) log("Proxy object disconnected");
+ mService = null;
+ if (mServiceListener != null) {
+ mServiceListener.onServiceDisconnected();
+ }
+ }
+ };
+
+ private static void log(String msg) {
+ Log.d(TAG, msg);
+ }
+}
diff --git a/core/java/android/bluetooth/BluetoothServerSocket.java b/core/java/android/bluetooth/BluetoothServerSocket.java
index ca46701..1b23f6c 100644
--- a/core/java/android/bluetooth/BluetoothServerSocket.java
+++ b/core/java/android/bluetooth/BluetoothServerSocket.java
@@ -16,85 +16,72 @@
package android.bluetooth;
+import android.os.Handler;
+
import java.io.Closeable;
import java.io.IOException;
/**
- * Server (listening) Bluetooth Socket.
+ * A listening Bluetooth socket.
+ *
+ * <p>The interface for Bluetooth Sockets is similar to that of TCP sockets:
+ * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server
+ * side, use a {@link BluetoothServerSocket} to create a listening server
+ * socket. When a connection is accepted by the {@link BluetoothServerSocket},
+ * it will return a new {@link BluetoothSocket} to manage the connection.
+ * On the client side, use a single {@link BluetoothSocket} to both intiate
+ * an outgoing connection and to manage the connection.
+ *
+ * <p>The most common type of Bluetooth socket is RFCOMM, which is the type
+ * supported by the Android APIs. RFCOMM is a connection-oriented, streaming
+ * transport over Bluetooth. It is also known as the Serial Port Profile (SPP).
*
- * Currently only supports RFCOMM sockets.
+ * <p>To create a listenting {@link BluetoothServerSocket} that's ready for
+ * incoming connections, use
+ * {@link BluetoothAdapter#listenUsingRfcommWithServiceRecord
+ * BluetoothAdapter.listenUsingRfcommWithServiceRecord()}. Then call
+ * {@link #accept()} to listen for incoming connection requests. This call
+ * will block until a connection is established, at which point, it will return
+ * a {@link BluetoothSocket} to manage the connection.
*
- * RFCOMM is a connection orientated, streaming transport over Bluetooth. It is
- * also known as the Serial Port Profile (SPP).
+ * <p>{@link BluetoothServerSocket} is thread
+ * safe. In particular, {@link #close} will always immediately abort ongoing
+ * operations and close the server socket.
*
- * TODO: Consider implementing SCO and L2CAP sockets.
- * TODO: Clean up javadoc grammer and formatting.
- * TODO: Remove @hide
- * @hide
+ * <p class="note"><strong>Note:</strong>
+ * Requires the {@link android.Manifest.permission#BLUETOOTH} permission.
+ *
+ * {@see BluetoothSocket}
*/
public final class BluetoothServerSocket implements Closeable {
- private final BluetoothSocket mSocket;
- /**
- * Construct a listening, secure RFCOMM server socket.
- * The remote device connecting to this socket will be authenticated and
- * communication on this socket will be encrypted.
- * Call #accept to retrieve connections to this socket.
- * @return An RFCOMM BluetoothServerSocket
- * @throws IOException On error, for example Bluetooth not available, or
- * insufficient permissions.
- */
- public static BluetoothServerSocket listenUsingRfcommOn(int port) throws IOException {
- BluetoothServerSocket socket = new BluetoothServerSocket(true, true);
- try {
- socket.mSocket.bindListenNative(port);
- } catch (IOException e) {
- try {
- socket.close();
- } catch (IOException e2) { }
- throw e;
- }
- return socket;
- }
-
- /**
- * Construct an unencrypted, unauthenticated, RFCOMM server socket.
- * Call #accept to retrieve connections to this socket.
- * @return An RFCOMM BluetoothServerSocket
- * @throws IOException On error, for example Bluetooth not available, or
- * insufficient permissions.
- */
- public static BluetoothServerSocket listenUsingInsecureRfcommOn(int port) throws IOException {
- BluetoothServerSocket socket = new BluetoothServerSocket(false, false);
- try {
- socket.mSocket.bindListenNative(port);
- } catch (IOException e) {
- try {
- socket.close();
- } catch (IOException e2) { }
- throw e;
- }
- return socket;
- }
+ /*package*/ final BluetoothSocket mSocket;
+ private Handler mHandler;
+ private int mMessage;
/**
* Construct a socket for incoming connections.
- * @param auth Require the remote device to be authenticated
- * @param encrypt Require the connection to be encrypted
+ * @param type type of socket
+ * @param auth require the remote device to be authenticated
+ * @param encrypt require the connection to be encrypted
+ * @param port remote port
* @throws IOException On error, for example Bluetooth not available, or
* insufficient priveleges
*/
- private BluetoothServerSocket(boolean auth, boolean encrypt) throws IOException {
- mSocket = new BluetoothSocket(-1, auth, encrypt, null, -1);
+ /*package*/ BluetoothServerSocket(int type, boolean auth, boolean encrypt, int port)
+ throws IOException {
+ mSocket = new BluetoothSocket(type, -1, auth, encrypt, null, port, null);
}
/**
* Block until a connection is established.
- * Returns a connected #BluetoothSocket. This server socket can be reused
- * for subsequent incoming connections by calling #accept repeatedly.
- * #close can be used to abort this call from another thread.
- * @return A connected #BluetoothSocket
- * @throws IOException On error, for example this call was aborted
+ * <p>Returns a connected {@link BluetoothSocket} on successful connection.
+ * <p>Once this call returns, it can be called again to accept subsequent
+ * incoming connections.
+ * <p>{@link #close} can be used to abort this call from another thread.
+ * @return a connected {@link BluetoothSocket}
+ * @throws IOException on error, for example this call was aborted, or
+ * timeout
*/
public BluetoothSocket accept() throws IOException {
return accept(-1);
@@ -102,23 +89,34 @@ public final class BluetoothServerSocket implements Closeable {
/**
* Block until a connection is established, with timeout.
- * Returns a connected #BluetoothSocket. This server socket can be reused
- * for subsequent incoming connections by calling #accept repeatedly.
- * #close can be used to abort this call from another thread.
- * @return A connected #BluetoothSocket
- * @throws IOException On error, for example this call was aborted, or
+ * <p>Returns a connected {@link BluetoothSocket} on successful connection.
+ * <p>Once this call returns, it can be called again to accept subsequent
+ * incoming connections.
+ * <p>{@link #close} can be used to abort this call from another thread.
+ * @return a connected {@link BluetoothSocket}
+ * @throws IOException on error, for example this call was aborted, or
* timeout
*/
public BluetoothSocket accept(int timeout) throws IOException {
- return mSocket.acceptNative(timeout);
+ return mSocket.accept(timeout);
}
/**
- * Closes this socket.
- * This will cause other blocking calls on this socket to immediately
+ * Immediately close this socket, and release all associated resources.
+ * <p>Causes blocked calls on this socket in other threads to immediately
* throw an IOException.
*/
public void close() throws IOException {
- mSocket.closeNative();
+ synchronized (this) {
+ if (mHandler != null) {
+ mHandler.obtainMessage(mMessage).sendToTarget();
+ }
+ }
+ mSocket.close();
+ }
+
+ /*package*/ synchronized void setCloseHandler(Handler handler, int message) {
+ mHandler = handler;
+ mMessage = message;
}
}
diff --git a/core/java/android/bluetooth/BluetoothSocket.java b/core/java/android/bluetooth/BluetoothSocket.java
index fd8885e..dbcc758 100644
--- a/core/java/android/bluetooth/BluetoothSocket.java
+++ b/core/java/android/bluetooth/BluetoothSocket.java
@@ -16,83 +16,128 @@
package android.bluetooth;
+import android.bluetooth.IBluetoothCallback;
+import android.os.ParcelUuid;
+import android.os.RemoteException;
+import android.util.Log;
+
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
- * Represents a connected or connecting Bluetooth Socket.
+ * A connected or connecting Bluetooth socket.
+ *
+ * <p>The interface for Bluetooth Sockets is similar to that of TCP sockets:
+ * {@link java.net.Socket} and {@link java.net.ServerSocket}. On the server
+ * side, use a {@link BluetoothServerSocket} to create a listening server
+ * socket. When a connection is accepted by the {@link BluetoothServerSocket},
+ * it will return a new {@link BluetoothSocket} to manage the connection.
+ * On the client side, use a single {@link BluetoothSocket} to both intiate
+ * an outgoing connection and to manage the connection.
+ *
+ * <p>The most common type of Bluetooth socket is RFCOMM, which is the type
+ * supported by the Android APIs. RFCOMM is a connection-oriented, streaming
+ * transport over Bluetooth. It is also known as the Serial Port Profile (SPP).
+ *
+ * <p>To create a {@link BluetoothSocket} for connecting to a known device, use
+ * {@link BluetoothDevice#createRfcommSocketToServiceRecord
+ * BluetoothDevice.createRfcommSocketToServiceRecord()}.
+ * Then call {@link #connect()} to attempt a connection to the remote device.
+ * This call will block until a connection is established or the connection
+ * fails.
+ *
+ * <p>To create a {@link BluetoothSocket} as a server (or "host"), see the
+ * {@link BluetoothServerSocket} documentation.
+ *
+ * <p>Once the socket is connected, whether initiated as a client or accepted
+ * as a server, open the IO streams by calling {@link #getInputStream} and
+ * {@link #getOutputStream} in order to retrieve {@link java.io.InputStream}
+ * and {@link java.io.OutputStream} objects, respectively, which are
+ * automatically connected to the socket.
*
- * Currently only supports RFCOMM sockets.
+ * <p>{@link BluetoothSocket} is thread
+ * safe. In particular, {@link #close} will always immediately abort ongoing
+ * operations and close the socket.
*
- * RFCOMM is a connection orientated, streaming transport over Bluetooth. It is
- * also known as the Serial Port Profile (SPP).
+ * <p class="note"><strong>Note:</strong>
+ * Requires the {@link android.Manifest.permission#BLUETOOTH} permission.
*
- * TODO: Consider implementing SCO and L2CAP sockets.
- * TODO: Clean up javadoc grammer and formatting.
- * TODO: Remove @hide
- * @hide
+ * {@see BluetoothServerSocket}
+ * {@see java.io.InputStream}
+ * {@see java.io.OutputStream}
*/
public final class BluetoothSocket implements Closeable {
- private final int mPort;
+ private static final String TAG = "BluetoothSocket";
+
+ /** @hide */
+ public static final int MAX_RFCOMM_CHANNEL = 30;
+
+ /** Keep TYPE_ fields in sync with BluetoothSocket.cpp */
+ /*package*/ static final int TYPE_RFCOMM = 1;
+ /*package*/ static final int TYPE_SCO = 2;
+ /*package*/ static final int TYPE_L2CAP = 3;
+
+ /*package*/ static final int EBADFD = 77;
+ /*package*/ static final int EADDRINUSE = 98;
+
+ private final int mType; /* one of TYPE_RFCOMM etc */
+ private final BluetoothDevice mDevice; /* remote device */
private final String mAddress; /* remote address */
private final boolean mAuth;
private final boolean mEncrypt;
private final BluetoothInputStream mInputStream;
private final BluetoothOutputStream mOutputStream;
+ private final SdpHelper mSdp;
- private int mSocketData; /* used by native code only */
+ private int mPort; /* RFCOMM channel or L2CAP psm */
- /**
- * Construct a secure RFCOMM socket ready to start an outgoing connection.
- * Call #connect on the returned #BluetoothSocket to begin the connection.
- * The remote device will be authenticated and communication on this socket
- * will be encrypted.
- * @param address remote Bluetooth address that this socket can connect to
- * @param port remote port
- * @return an RFCOMM BluetoothSocket
- * @throws IOException on error, for example Bluetooth not available, or
- * insufficient permissions.
- */
- public static BluetoothSocket createRfcommSocket(String address, int port)
- throws IOException {
- return new BluetoothSocket(-1, true, true, address, port);
- }
+ /** prevents all native calls after destroyNative() */
+ private boolean mClosed;
- /**
- * Construct an insecure RFCOMM socket ready to start an outgoing
- * connection.
- * Call #connect on the returned #BluetoothSocket to begin the connection.
- * The remote device will not be authenticated and communication on this
- * socket will not be encrypted.
- * @param address remote Bluetooth address that this socket can connect to
- * @param port remote port
- * @return An RFCOMM BluetoothSocket
- * @throws IOException On error, for example Bluetooth not available, or
- * insufficient permissions.
- */
- public static BluetoothSocket createInsecureRfcommSocket(String address, int port)
- throws IOException {
- return new BluetoothSocket(-1, false, false, address, port);
- }
+ /** protects mClosed */
+ private final ReentrantReadWriteLock mLock;
+
+ /** used by native code only */
+ private int mSocketData;
/**
- * Construct a Bluetooth.
+ * Construct a BluetoothSocket.
+ * @param type type of socket
* @param fd fd to use for connected socket, or -1 for a new socket
* @param auth require the remote device to be authenticated
* @param encrypt require the connection to be encrypted
- * @param address remote Bluetooth address that this socket can connect to
+ * @param device remote device that this socket can connect to
* @param port remote port
+ * @param uuid SDP uuid
* @throws IOException On error, for example Bluetooth not available, or
* insufficient priveleges
*/
- /*package*/ BluetoothSocket(int fd, boolean auth, boolean encrypt, String address, int port)
- throws IOException {
+ /*package*/ BluetoothSocket(int type, int fd, boolean auth, boolean encrypt,
+ BluetoothDevice device, int port, ParcelUuid uuid) throws IOException {
+ if (type == BluetoothSocket.TYPE_RFCOMM && uuid == null && fd == -1) {
+ if (port < 1 || port > MAX_RFCOMM_CHANNEL) {
+ throw new IOException("Invalid RFCOMM channel: " + port);
+ }
+ }
+ if (uuid == null) {
+ mPort = port;
+ mSdp = null;
+ } else {
+ mSdp = new SdpHelper(device, uuid);
+ mPort = -1;
+ }
+ mType = type;
mAuth = auth;
mEncrypt = encrypt;
- mAddress = address;
- mPort = port;
+ mDevice = device;
+ if (device == null) {
+ mAddress = null;
+ } else {
+ mAddress = device.getAddress();
+ }
if (fd == -1) {
initSocketNative();
} else {
@@ -100,8 +145,27 @@ public final class BluetoothSocket implements Closeable {
}
mInputStream = new BluetoothInputStream(this);
mOutputStream = new BluetoothOutputStream(this);
+ mClosed = false;
+ mLock = new ReentrantReadWriteLock();
+ }
+
+ /**
+ * Construct a BluetoothSocket from address. Used by native code.
+ * @param type type of socket
+ * @param fd fd to use for connected socket, or -1 for a new socket
+ * @param auth require the remote device to be authenticated
+ * @param encrypt require the connection to be encrypted
+ * @param address remote device that this socket can connect to
+ * @param port remote port
+ * @throws IOException On error, for example Bluetooth not available, or
+ * insufficient priveleges
+ */
+ private BluetoothSocket(int type, int fd, boolean auth, boolean encrypt, String address,
+ int port) throws IOException {
+ this(type, fd, auth, encrypt, new BluetoothDevice(address), port, null);
}
+ /** @hide */
@Override
protected void finalize() throws Throwable {
try {
@@ -113,37 +177,67 @@ public final class BluetoothSocket implements Closeable {
/**
* Attempt to connect to a remote device.
- * This method will block until a connection is made or the connection
+ * <p>This method will block until a connection is made or the connection
* fails. If this method returns without an exception then this socket
- * is now connected. #close can be used to abort this call from another
- * thread.
- * @throws IOException On error, for example connection failure
+ * is now connected.
+ * <p>{@link #close} can be used to abort this call from another thread.
+ * @throws IOException on error, for example connection failure
*/
public void connect() throws IOException {
- connectNative(mAddress, mPort, -1);
+ mLock.readLock().lock();
+ try {
+ if (mClosed) throw new IOException("socket closed");
+
+ if (mSdp != null) {
+ mPort = mSdp.doSdp(); // blocks
+ }
+
+ connectNative(); // blocks
+ } finally {
+ mLock.readLock().unlock();
+ }
}
/**
- * Closes this socket.
- * This will cause other blocking calls on this socket to immediately
+ * Immediately close this socket, and release all associated resources.
+ * <p>Causes blocked calls on this socket in other threads to immediately
* throw an IOException.
*/
public void close() throws IOException {
- closeNative();
+ // abort blocking operations on the socket
+ mLock.readLock().lock();
+ try {
+ if (mClosed) return;
+ if (mSdp != null) {
+ mSdp.cancel();
+ }
+ abortNative();
+ } finally {
+ mLock.readLock().unlock();
+ }
+
+ // all native calls are guaranteed to immediately return after
+ // abortNative(), so this lock should immediatley acquire
+ mLock.writeLock().lock();
+ try {
+ mClosed = true;
+ destroyNative();
+ } finally {
+ mLock.writeLock().unlock();
+ }
}
/**
- * Return the address we are connecting, or connected, to.
- * @return Bluetooth address, or null if this socket has not yet attempted
- * or established a connection.
+ * Get the remote device this socket is connecting, or connected, to.
+ * @return remote device
*/
- public String getAddress() {
- return mAddress;
+ public BluetoothDevice getRemoteDevice() {
+ return mDevice;
}
/**
* Get the input stream associated with this socket.
- * The input stream will be returned even if the socket is not yet
+ * <p>The input stream will be returned even if the socket is not yet
* connected, but operations on that stream will throw IOException until
* the associated socket is connected.
* @return InputStream
@@ -154,7 +248,7 @@ public final class BluetoothSocket implements Closeable {
/**
* Get the output stream associated with this socket.
- * The output stream will be returned even if the socket is not yet
+ * <p>The output stream will be returned even if the socket is not yet
* connected, but operations on that stream will throw IOException until
* the associated socket is connected.
* @return OutputStream
@@ -163,14 +257,131 @@ public final class BluetoothSocket implements Closeable {
return mOutputStream;
}
- private native void initSocketNative();
- private native void initSocketFromFdNative(int fd);
- private native void connectNative(String address, int port, int timeout);
- /*package*/ native void bindListenNative(int port) throws IOException;
- /*package*/ native BluetoothSocket acceptNative(int timeout) throws IOException;
- /*package*/ native int availableNative();
- /*package*/ native int readNative();
- /*package*/ native void writeNative(int data);
- /*package*/ native void closeNative();
- private native void destroyNative();
+ /**
+ * Currently returns unix errno instead of throwing IOException,
+ * so that BluetoothAdapter can check the error code for EADDRINUSE
+ */
+ /*package*/ int bindListen() {
+ mLock.readLock().lock();
+ try {
+ if (mClosed) return EBADFD;
+ return bindListenNative();
+ } finally {
+ mLock.readLock().unlock();
+ }
+ }
+
+ /*package*/ BluetoothSocket accept(int timeout) throws IOException {
+ mLock.readLock().lock();
+ try {
+ if (mClosed) throw new IOException("socket closed");
+ return acceptNative(timeout);
+ } finally {
+ mLock.readLock().unlock();
+ }
+ }
+
+ /*package*/ int available() throws IOException {
+ mLock.readLock().lock();
+ try {
+ if (mClosed) throw new IOException("socket closed");
+ return availableNative();
+ } finally {
+ mLock.readLock().unlock();
+ }
+ }
+
+ /*package*/ int read(byte[] b, int offset, int length) throws IOException {
+ mLock.readLock().lock();
+ try {
+ if (mClosed) throw new IOException("socket closed");
+ return readNative(b, offset, length);
+ } finally {
+ mLock.readLock().unlock();
+ }
+ }
+
+ /*package*/ int write(byte[] b, int offset, int length) throws IOException {
+ mLock.readLock().lock();
+ try {
+ if (mClosed) throw new IOException("socket closed");
+ return writeNative(b, offset, length);
+ } finally {
+ mLock.readLock().unlock();
+ }
+ }
+
+ private native void initSocketNative() throws IOException;
+ private native void initSocketFromFdNative(int fd) throws IOException;
+ private native void connectNative() throws IOException;
+ private native int bindListenNative();
+ private native BluetoothSocket acceptNative(int timeout) throws IOException;
+ private native int availableNative() throws IOException;
+ private native int readNative(byte[] b, int offset, int length) throws IOException;
+ private native int writeNative(byte[] b, int offset, int length) throws IOException;
+ private native void abortNative() throws IOException;
+ private native void destroyNative() throws IOException;
+ /**
+ * Throws an IOException for given posix errno. Done natively so we can
+ * use strerr to convert to string error.
+ */
+ /*package*/ native void throwErrnoNative(int errno) throws IOException;
+
+ /**
+ * Helper to perform blocking SDP lookup.
+ */
+ private static class SdpHelper extends IBluetoothCallback.Stub {
+ private final IBluetooth service;
+ private final ParcelUuid uuid;
+ private final BluetoothDevice device;
+ private int channel;
+ private boolean canceled;
+ public SdpHelper(BluetoothDevice device, ParcelUuid uuid) {
+ service = BluetoothDevice.getService();
+ this.device = device;
+ this.uuid = uuid;
+ canceled = false;
+ }
+ /**
+ * Returns the RFCOMM channel for the UUID, or throws IOException
+ * on failure.
+ */
+ public synchronized int doSdp() throws IOException {
+ if (canceled) throw new IOException("Service discovery canceled");
+ channel = -1;
+
+ boolean inProgress = false;
+ try {
+ inProgress = service.fetchRemoteUuids(device.getAddress(), uuid, this);
+ } catch (RemoteException e) {Log.e(TAG, "", e);}
+
+ if (!inProgress) throw new IOException("Unable to start Service Discovery");
+
+ try {
+ /* 12 second timeout as a precaution - onRfcommChannelFound
+ * should always occur before the timeout */
+ wait(12000); // block
+
+ } catch (InterruptedException e) {}
+
+ if (canceled) throw new IOException("Service discovery canceled");
+ if (channel < 1) throw new IOException("Service discovery failed");
+
+ return channel;
+ }
+ /** Object cannot be re-used after calling cancel() */
+ public synchronized void cancel() {
+ if (!canceled) {
+ canceled = true;
+ channel = -1;
+ notifyAll(); // unblock
+ }
+ }
+ public synchronized void onRfcommChannelFound(int channel) {
+ if (!canceled) {
+ this.channel = channel;
+ notifyAll(); // unblock
+ }
+ }
+ }
}
diff --git a/core/java/android/bluetooth/BluetoothUuid.java b/core/java/android/bluetooth/BluetoothUuid.java
new file mode 100644
index 0000000..4164a3d
--- /dev/null
+++ b/core/java/android/bluetooth/BluetoothUuid.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.bluetooth;
+
+import android.os.ParcelUuid;
+
+import java.util.Arrays;
+import java.util.HashSet;
+
+/**
+* Static helper methods and constants to decode the ParcelUuid of remote devices.
+* @hide
+*/
+public final class BluetoothUuid {
+
+ /* See Bluetooth Assigned Numbers document - SDP section, to get the values of UUIDs
+ * for the various services.
+ *
+ * The following 128 bit values are calculated as:
+ * uuid * 2^96 + BASE_UUID
+ */
+ public static final ParcelUuid AudioSink =
+ ParcelUuid.fromString("0000110B-0000-1000-8000-00805F9B34FB");
+ public static final ParcelUuid AudioSource =
+ ParcelUuid.fromString("0000110A-0000-1000-8000-00805F9B34FB");
+ public static final ParcelUuid AdvAudioDist =
+ ParcelUuid.fromString("0000110D-0000-1000-8000-00805F9B34FB");
+ public static final ParcelUuid HSP =
+ ParcelUuid.fromString("00001108-0000-1000-8000-00805F9B34FB");
+ public static final ParcelUuid Handsfree =
+ ParcelUuid.fromString("0000111E-0000-1000-8000-00805F9B34FB");
+ public static final ParcelUuid AvrcpController =
+ ParcelUuid.fromString("0000110E-0000-1000-8000-00805F9B34FB");
+ public static final ParcelUuid AvrcpTarget =
+ ParcelUuid.fromString("0000110C-0000-1000-8000-00805F9B34FB");
+ public static final ParcelUuid ObexObjectPush =
+ ParcelUuid.fromString("00001105-0000-1000-8000-00805f9b34fb");
+
+ public static final ParcelUuid[] RESERVED_UUIDS = {
+ AudioSink, AudioSource, AdvAudioDist, HSP, Handsfree, AvrcpController, AvrcpTarget,
+ ObexObjectPush};
+
+ public static boolean isAudioSource(ParcelUuid uuid) {
+ return uuid.equals(AudioSource);
+ }
+
+ public static boolean isAudioSink(ParcelUuid uuid) {
+ return uuid.equals(AudioSink);
+ }
+
+ public static boolean isAdvAudioDist(ParcelUuid uuid) {
+ return uuid.equals(AdvAudioDist);
+ }
+
+ public static boolean isHandsfree(ParcelUuid uuid) {
+ return uuid.equals(Handsfree);
+ }
+
+ public static boolean isHeadset(ParcelUuid uuid) {
+ return uuid.equals(HSP);
+ }
+
+ public static boolean isAvrcpController(ParcelUuid uuid) {
+ return uuid.equals(AvrcpController);
+ }
+
+ public static boolean isAvrcpTarget(ParcelUuid uuid) {
+ return uuid.equals(AvrcpTarget);
+ }
+
+ /**
+ * Returns true if ParcelUuid is present in uuidArray
+ *
+ * @param uuidArray - Array of ParcelUuids
+ * @param uuid
+ */
+ public static boolean isUuidPresent(ParcelUuid[] uuidArray, ParcelUuid uuid) {
+ if ((uuidArray == null || uuidArray.length == 0) && uuid == null)
+ return true;
+
+ if (uuidArray == null)
+ return false;
+
+ for (ParcelUuid element: uuidArray) {
+ if (element.equals(uuid)) return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if there any common ParcelUuids in uuidA and uuidB.
+ *
+ * @param uuidA - List of ParcelUuids
+ * @param uuidB - List of ParcelUuids
+ *
+ */
+ public static boolean containsAnyUuid(ParcelUuid[] uuidA, ParcelUuid[] uuidB) {
+ if (uuidA == null && uuidB == null) return true;
+
+ if (uuidA == null) {
+ return uuidB.length == 0 ? true : false;
+ }
+
+ if (uuidB == null) {
+ return uuidA.length == 0 ? true : false;
+ }
+
+ HashSet<ParcelUuid> uuidSet = new HashSet<ParcelUuid> (Arrays.asList(uuidA));
+ for (ParcelUuid uuid: uuidB) {
+ if (uuidSet.contains(uuid)) return true;
+ }
+ return false;
+ }
+
+ /**
+ * Returns true if all the ParcelUuids in ParcelUuidB are present in
+ * ParcelUuidA
+ *
+ * @param uuidA - Array of ParcelUuidsA
+ * @param uuidB - Array of ParcelUuidsB
+ *
+ */
+ public static boolean containsAllUuids(ParcelUuid[] uuidA, ParcelUuid[] uuidB) {
+ if (uuidA == null && uuidB == null) return true;
+
+ if (uuidA == null) {
+ return uuidB.length == 0 ? true : false;
+ }
+
+ if (uuidB == null) return true;
+
+ HashSet<ParcelUuid> uuidSet = new HashSet<ParcelUuid> (Arrays.asList(uuidA));
+ for (ParcelUuid uuid: uuidB) {
+ if (!uuidSet.contains(uuid)) return false;
+ }
+ return true;
+ }
+
+}
diff --git a/core/java/android/bluetooth/HeadsetBase.java b/core/java/android/bluetooth/HeadsetBase.java
index f987ffd..e2935c9 100644
--- a/core/java/android/bluetooth/HeadsetBase.java
+++ b/core/java/android/bluetooth/HeadsetBase.java
@@ -31,7 +31,7 @@ import android.util.Log;
*
* @hide
*/
-public class HeadsetBase {
+public final class HeadsetBase {
private static final String TAG = "Bluetooth HeadsetBase";
private static final boolean DBG = false;
@@ -42,8 +42,9 @@ public class HeadsetBase {
private static int sAtInputCount = 0; /* TODO: Consider not using a static variable */
- private final BluetoothDevice mBluetooth;
- private final String mAddress;
+ private final BluetoothAdapter mAdapter;
+ private final BluetoothDevice mRemoteDevice;
+ private final String mAddress; // for native code
private final int mRfcommChannel;
private int mNativeData;
private Thread mEventThread;
@@ -73,12 +74,13 @@ public class HeadsetBase {
private native void cleanupNativeDataNative();
- public HeadsetBase(PowerManager pm, BluetoothDevice bluetooth, String address,
- int rfcommChannel) {
+ public HeadsetBase(PowerManager pm, BluetoothAdapter adapter, BluetoothDevice device,
+ int rfcommChannel) {
mDirection = DIRECTION_OUTGOING;
mConnectTimestamp = System.currentTimeMillis();
- mBluetooth = bluetooth;
- mAddress = address;
+ mAdapter = adapter;
+ mRemoteDevice = device;
+ mAddress = device.getAddress();
mRfcommChannel = rfcommChannel;
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "HeadsetBase");
mWakeLock.setReferenceCounted(false);
@@ -88,12 +90,13 @@ public class HeadsetBase {
}
/* Create from an already exisiting rfcomm connection */
- public HeadsetBase(PowerManager pm, BluetoothDevice bluetooth, String address, int socketFd,
- int rfcommChannel, Handler handler) {
+ public HeadsetBase(PowerManager pm, BluetoothAdapter adapter, BluetoothDevice device,
+ int socketFd, int rfcommChannel, Handler handler) {
mDirection = DIRECTION_INCOMING;
mConnectTimestamp = System.currentTimeMillis();
- mBluetooth = bluetooth;
- mAddress = address;
+ mAdapter = adapter;
+ mRemoteDevice = device;
+ mAddress = device.getAddress();
mRfcommChannel = rfcommChannel;
mEventThreadHandler = handler;
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "HeadsetBase");
@@ -208,9 +211,10 @@ public class HeadsetBase {
*/
public boolean connectAsync() {
- return connectAsyncNative();
+ int ret = connectAsyncNative();
+ return (ret == 0) ? true : false;
}
- private native boolean connectAsyncNative();
+ private native int connectAsyncNative();
public int getRemainingAsyncConnectWaitingTimeMs() {
return mTimeoutRemainingMs;
@@ -252,12 +256,8 @@ public class HeadsetBase {
return mEventThread != null;
}
- public String getAddress() {
- return mAddress;
- }
-
- public String getName() {
- return mBluetooth.getRemoteName(mAddress);
+ public BluetoothDevice getRemoteDevice() {
+ return mRemoteDevice;
}
public int getDirection() {
diff --git a/core/java/android/bluetooth/IBluetoothDevice.aidl b/core/java/android/bluetooth/IBluetooth.aidl
index 6cd792e..7e752af 100644
--- a/core/java/android/bluetooth/IBluetoothDevice.aidl
+++ b/core/java/android/bluetooth/IBluetooth.aidl
@@ -16,14 +16,15 @@
package android.bluetooth;
-import android.bluetooth.IBluetoothDeviceCallback;
+import android.bluetooth.IBluetoothCallback;
+import android.os.ParcelUuid;
/**
* System private API for talking with the Bluetooth service.
*
* {@hide}
*/
-interface IBluetoothDevice
+interface IBluetooth
{
boolean isEnabled();
int getBluetoothState();
@@ -33,28 +34,16 @@ interface IBluetoothDevice
String getAddress();
String getName();
boolean setName(in String name);
- String getVersion();
- String getRevision();
- String getManufacturer();
- String getCompany();
int getScanMode();
- boolean setScanMode(int mode);
+ boolean setScanMode(int mode, int duration);
int getDiscoverableTimeout();
boolean setDiscoverableTimeout(int timeout);
- boolean startDiscovery(boolean resolveNames);
+ boolean startDiscovery();
boolean cancelDiscovery();
boolean isDiscovering();
- boolean startPeriodicDiscovery();
- boolean stopPeriodicDiscovery();
- boolean isPeriodicDiscovery();
- String[] listRemoteDevices();
-
- String[] listAclConnections();
- boolean isAclConnected(in String address);
- boolean disconnectRemoteDeviceAcl(in String address);
boolean createBond(in String address);
boolean cancelBondProcess(in String address);
@@ -63,16 +52,19 @@ interface IBluetoothDevice
int getBondState(in String address);
String getRemoteName(in String address);
- String getRemoteVersion(in String address);
- String getRemoteRevision(in String address);
int getRemoteClass(in String address);
- String getRemoteManufacturer(in String address);
- String getRemoteCompany(in String address);
- boolean getRemoteServiceChannel(in String address, int uuid16, in IBluetoothDeviceCallback callback);
- byte[] getRemoteFeatures(in String adddress);
- String lastSeen(in String address);
- String lastUsed(in String address);
+ ParcelUuid[] getRemoteUuids(in String address);
+ boolean fetchRemoteUuids(in String address, in ParcelUuid uuid, in IBluetoothCallback callback);
+ int getRemoteServiceChannel(in String address, in ParcelUuid uuid);
boolean setPin(in String address, in byte[] pin);
- boolean cancelPin(in String address);
+ boolean setPasskey(in String address, int passkey);
+ boolean setPairingConfirmation(in String address, boolean confirm);
+ boolean cancelPairingUserInput(in String address);
+
+ boolean setTrust(in String address, in boolean value);
+ boolean getTrustState(in String address);
+
+ int addRfcommServiceRecord(in String serviceName, in ParcelUuid uuid, int channel, IBinder b);
+ void removeServiceRecord(int handle);
}
diff --git a/core/java/android/bluetooth/IBluetoothA2dp.aidl b/core/java/android/bluetooth/IBluetoothA2dp.aidl
index 55ff27f..002cf4e 100644
--- a/core/java/android/bluetooth/IBluetoothA2dp.aidl
+++ b/core/java/android/bluetooth/IBluetoothA2dp.aidl
@@ -16,16 +16,20 @@
package android.bluetooth;
+import android.bluetooth.BluetoothDevice;
+
/**
* System private API for Bluetooth A2DP service
*
* {@hide}
*/
interface IBluetoothA2dp {
- int connectSink(in String address);
- int disconnectSink(in String address);
- List<String> listConnectedSinks();
- int getSinkState(in String address);
- int setSinkPriority(in String address, int priority);
- int getSinkPriority(in String address);
+ boolean connectSink(in BluetoothDevice device);
+ boolean disconnectSink(in BluetoothDevice device);
+ boolean suspendSink(in BluetoothDevice device);
+ boolean resumeSink(in BluetoothDevice device);
+ BluetoothDevice[] getConnectedSinks(); // change to Set<> once AIDL supports
+ int getSinkState(in BluetoothDevice device);
+ boolean setSinkPriority(in BluetoothDevice device, int priority);
+ int getSinkPriority(in BluetoothDevice device);
}
diff --git a/core/java/android/bluetooth/IBluetoothDeviceCallback.aidl b/core/java/android/bluetooth/IBluetoothCallback.aidl
index d057093..8edb3f4 100644
--- a/core/java/android/bluetooth/IBluetoothDeviceCallback.aidl
+++ b/core/java/android/bluetooth/IBluetoothCallback.aidl
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2008, The Android Open Source Project
+ * Copyright (C) 2009, The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -17,9 +17,11 @@
package android.bluetooth;
/**
+ * System private API for Bluetooth service callbacks.
+ *
* {@hide}
*/
-oneway interface IBluetoothDeviceCallback
+interface IBluetoothCallback
{
- void onGetRemoteServiceChannelResult(in String address, int channel);
+ void onRfcommChannelFound(int channel);
}
diff --git a/core/java/android/bluetooth/IBluetoothHeadset.aidl b/core/java/android/bluetooth/IBluetoothHeadset.aidl
index 5f42fd6..6cccd50 100644
--- a/core/java/android/bluetooth/IBluetoothHeadset.aidl
+++ b/core/java/android/bluetooth/IBluetoothHeadset.aidl
@@ -16,6 +16,8 @@
package android.bluetooth;
+import android.bluetooth.BluetoothDevice;
+
/**
* System private API for Bluetooth Headset service
*
@@ -23,13 +25,13 @@ package android.bluetooth;
*/
interface IBluetoothHeadset {
int getState();
- String getHeadsetAddress();
- boolean connectHeadset(in String address);
+ BluetoothDevice getCurrentHeadset();
+ boolean connectHeadset(in BluetoothDevice device);
void disconnectHeadset();
- boolean isConnected(in String address);
+ boolean isConnected(in BluetoothDevice device);
boolean startVoiceRecognition();
boolean stopVoiceRecognition();
- boolean setPriority(in String address, int priority);
- int getPriority(in String address);
+ boolean setPriority(in BluetoothDevice device, int priority);
+ int getPriority(in BluetoothDevice device);
int getBatteryUsageHint();
}
diff --git a/core/java/android/bluetooth/BluetoothError.java b/core/java/android/bluetooth/IBluetoothPbap.aidl
index 2554bea..7cc77d1 100644
--- a/core/java/android/bluetooth/BluetoothError.java
+++ b/core/java/android/bluetooth/IBluetoothPbap.aidl
@@ -16,27 +16,17 @@
package android.bluetooth;
+import android.bluetooth.BluetoothDevice;
+
/**
- * Bluetooth API error codes.
- *
- * Errors are always negative.
+ * System private API for Bluetooth pbap service
*
- * @hide
+ * {@hide}
*/
-public class BluetoothError {
- /** No error */
- public static final int SUCCESS = 0;
-
- /** Generic error */
- public static final int ERROR = -1000;
-
- /** Bluetooth currently disabled */
- public static final int ERROR_DISABLED = -1001;
-
- /** IPC is not ready, for example service is not yet bound */
- public static final int ERROR_IPC_NOT_READY = -1011;
-
- /** Some other IPC error, for example a RemoteException */
- public static final int ERROR_IPC = -1012;
-
+interface IBluetoothPbap {
+ int getState();
+ BluetoothDevice getClient();
+ boolean connect(in BluetoothDevice device);
+ void disconnect();
+ boolean isConnected(in BluetoothDevice device);
}
diff --git a/core/java/android/bluetooth/ScoSocket.java b/core/java/android/bluetooth/ScoSocket.java
index 1bf786f..116310a 100644
--- a/core/java/android/bluetooth/ScoSocket.java
+++ b/core/java/android/bluetooth/ScoSocket.java
@@ -87,7 +87,7 @@ public class ScoSocket {
* Does not block.
*/
public synchronized boolean connect(String address) {
- if (VDBG) log("connect() " + this);
+ if (DBG) log("connect() " + this);
if (mState != STATE_READY) {
if (DBG) log("connect(): Bad state");
return false;
diff --git a/core/java/android/bluetooth/package.html b/core/java/android/bluetooth/package.html
index 79abf0c..4f0755e 100644
--- a/core/java/android/bluetooth/package.html
+++ b/core/java/android/bluetooth/package.html
@@ -1,13 +1,109 @@
<HTML>
<BODY>
-Provides classes that manage Bluetooth functionality on the device.
-<p>
-The Bluetooth APIs allow applications can connect and disconnect headsets, or scan
-for other kinds of Bluetooth devices and pair them. Further control includes the
-ability to write and modify the local Service Discovery Protocol (SDP) database,
-query the SDP database of other Bluetooth devices, establish RFCOMM
-channels/sockets on Android, and connect to specified sockets on other devices.
+Provides classes that manage Bluetooth functionality, such as scanning for
+devices, connecting with devices, and managing data transfer between devices.
+
+<p>The Bluetooth APIs let applications:</p>
+<ul>
+ <li>Scan for other Bluetooth devices</li>
+ <li>Query the local Bluetooth adapter for paired Bluetooth devices</li>
+ <li>Establish RFCOMM channels/sockets</li>
+ <li>Connect to specified sockets on other devices</li>
+ <li>Transfer data to and from other devices</li>
+</ul>
+
+<p class="note"><strong>Note:</strong>
+To perform Bluetooth communication using these APIs, an application must
+declare the {@link android.Manifest.permission#BLUETOOTH} permission. Some
+additional functionality, such as requesting device discovery and
+pairing also requires the {@link android.Manifest.permission#BLUETOOTH_ADMIN}
+permission.
</p>
-<p>Remember, not all Android devices are guaranteed to have Bluetooth functionality.</p>
+
+<h3>Overview</h3>
+
+<p>Here's a basic introduction to the Bluetooth classes:</p>
+<dl>
+ <dt>{@link android.bluetooth.BluetoothAdapter}</dt>
+ <dd>This represents the local Bluetooth adapter, which is essentially the
+ entry-point to performing any interaction with Bluetooth. With it, you can
+ discover other Bluetooth devices, query a list of bonded (paired) devices,
+ initialize a {@link android.bluetooth.BluetoothDevice} using a known MAC
+ address, and create a {@link android.bluetooth.BluetoothServerSocket} to
+ listen for communications from other devices.</dd>
+
+ <dt>{@link android.bluetooth.BluetoothDevice}</dt>
+ <dd>This represents a remote Bluetooth device. Use this to request a
+ connection with a remote device through a
+ {@link android.bluetooth.BluetoothSocket}
+ or query information about the device such as its name, address, class, and
+ bonding state.</dd>
+
+ <dt>{@link android.bluetooth.BluetoothSocket}</dt>
+ <dd>This represents the interface for a Bluetooth socket
+ (similar to a TCP client-side {@link java.net.Socket}). This is the
+ connection point that allows an app to transfer data with another Bluetooth
+ device via {@link java.io.InputStream} and {@link java.io.OutputStream}.</dd>
+ <dt>{@link android.bluetooth.BluetoothServerSocket}</dt>
+
+ <dd>This represents an open server socket that listens for incoming requests
+ (similar to a TCP server-side {@link java.net.ServerSocket}).
+ When attempting to connect two Android devices, one device will need to open
+ a server socket with this class. When a connection is accepted, a new
+ {@link android.bluetooth.BluetoothSocket} will be returned,
+ which can be used to manage the connection and transfer data.</dd>
+
+ <dt>{@link android.bluetooth.BluetoothClass}</dt>
+ <dd>This represents the Bluetooth class for a device which describes general
+ characteristics and capabilities of a device. This class and its subclasses
+ don't provide any actual functionality. The sub-classes are entirely composed
+ of constants for the device and service class definitions.</dd>
+</dl>
+
+
+<h3>Example Procedure</h3>
+
+<p>For example, here's an pseudo-code procedure for discovering and
+connecting a remote device, and transfering data:</p>
+
+<ol>
+ <li>Register a {@link android.content.BroadcastReceiver} that accepts the
+ {@link android.bluetooth.BluetoothDevice#ACTION_FOUND} Intent.</li>
+ <li>Call {@link android.bluetooth.BluetoothAdapter#getDefaultAdapter} to
+ retrieve the Android system's local
+ {@link android.bluetooth.BluetoothAdapter}.</li>
+ <li>Call {@link android.bluetooth.BluetoothAdapter#startDiscovery()
+ BluetoothAdapter.startDiscovery()} to scan for local devices. This is where
+ the BroadcastReceiver comes in; Android now scans for devices and will
+ broadcast the {@link android.bluetooth.BluetoothDevice#ACTION_FOUND} Intent
+ for each remote device discovered. The
+ {@link android.content.BroadcastReceiver}
+ you created will receive each Intent.</li>
+ <li>The {@link android.bluetooth.BluetoothDevice#ACTION_FOUND} Intent
+ includes the {@link android.bluetooth.BluetoothDevice#EXTRA_DEVICE}
+ Parcelable extra, which is a {@link android.bluetooth.BluetoothDevice}
+ object. Extract this from the Intent and call
+ {@link android.bluetooth.BluetoothDevice#createRfcommSocketToServiceRecord(java.util.UUID)
+ BluetoothDevice.createRfcommSocketToServiceRecord()}
+ to open a {@link android.bluetooth.BluetoothSocket} with a chosen
+ remote device.</li>
+ <li>Call {@link android.bluetooth.BluetoothSocket#connect()
+ BluetoothSocket.connect()} to connect with the remote device.</li>
+ <li>When successfully connected, call
+ {@link android.bluetooth.BluetoothSocket#getInputStream()
+ BluetoothSocket.getInputStream()} and/or
+ {@link android.bluetooth.BluetoothSocket#getOutputStream()
+ BluetoothSocket.getOutputStream()} to retreive an
+ {@link java.io.InputStream} and {@link java.io.OutputStream}, respectively,
+ which are hooked into the socket.</li>
+ <li>Use {@link java.io.InputStream#read(byte[]) InputStream.read()} and
+ {@link java.io.OutputStream#write(byte[]) OutputStream.write()} to transfer
+ data.</li>
+</ol>
+
+
+
+<p class="note"><strong>Note:</strong>
+Not all Android devices are guaranteed to have Bluetooth functionality.</p>
</BODY>
</HTML>