Merge change 25779 into eclair
* changes: Add new API for fetching UUIDs using SDP.
This commit is contained in:
@ -28,6 +28,7 @@ import android.util.Log;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Represents a remote Bluetooth device.
|
||||
@ -225,6 +226,20 @@ public final class BluetoothDevice implements Parcelable {
|
||||
/** @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 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.
|
||||
@ -292,6 +307,15 @@ public final class BluetoothDevice implements Parcelable {
|
||||
* @hide */
|
||||
public static final int PAIRING_VARIANT_DISPLAY_PASSKEY = 4;
|
||||
|
||||
/**
|
||||
* Used as an extra field in {@link #ACTION_UUID} intents,
|
||||
* Contains the {@link 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";
|
||||
|
||||
|
||||
private static IBluetooth sService; /* Guarenteed constant after first object constructed */
|
||||
|
||||
private final String mAddress;
|
||||
@ -507,6 +531,27 @@ public final class BluetoothDevice implements Parcelable {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.fetchRemoteUuidsWithSdp(mAddress);
|
||||
} catch (RemoteException e) {Log.e(TAG, "", e);}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public int getServiceChannel(ParcelUuid uuid) {
|
||||
try {
|
||||
|
@ -53,6 +53,7 @@ interface IBluetooth
|
||||
String getRemoteName(in String address);
|
||||
int getRemoteClass(in String address);
|
||||
ParcelUuid[] getRemoteUuids(in String address);
|
||||
boolean fetchRemoteUuidsWithSdp(in String address);
|
||||
int getRemoteServiceChannel(in String address,in ParcelUuid uuid);
|
||||
|
||||
boolean setPin(in String address, in byte[] pin);
|
||||
|
@ -330,6 +330,9 @@ class BluetoothEventLoop {
|
||||
Log.e(TAG, "onDevicePropertyChanged: Address of the remote device in null");
|
||||
return;
|
||||
}
|
||||
if (DBG) {
|
||||
log("Device property changed:" + address + "property:" + name);
|
||||
}
|
||||
BluetoothDevice device = mAdapter.getRemoteDevice(address);
|
||||
if (name.equals("Name")) {
|
||||
Intent intent = new Intent(BluetoothDevice.ACTION_NAME_CHANGED);
|
||||
@ -366,6 +369,7 @@ class BluetoothEventLoop {
|
||||
uuid = str.toString();
|
||||
}
|
||||
mBluetoothService.setRemoteDeviceProperty(address, name, uuid);
|
||||
mBluetoothService.sendUuidIntent(address);
|
||||
} else if (name.equals("Paired")) {
|
||||
if (propValues[1].equals("true")) {
|
||||
mBluetoothService.getBondState().setBondState(address, BluetoothDevice.BOND_BONDED);
|
||||
@ -528,6 +532,25 @@ class BluetoothEventLoop {
|
||||
return;
|
||||
}
|
||||
|
||||
private void onDiscoverServicesResult(String deviceObjectPath, boolean result) {
|
||||
String address = mBluetoothService.getAddressFromObjectPath(deviceObjectPath);
|
||||
// We don't parse the xml here, instead just query Bluez for the properties.
|
||||
if (result) {
|
||||
String[] properties = mBluetoothService.getRemoteDeviceProperties(address);
|
||||
mBluetoothService.addRemoteDeviceProperties(address, properties);
|
||||
}
|
||||
mBluetoothService.sendUuidIntent(address);
|
||||
}
|
||||
|
||||
private void onCreateDeviceResult(String address, boolean result) {
|
||||
if (DBG) {
|
||||
log("Result of onCreateDeviceResult:" + result);
|
||||
}
|
||||
if (!result) {
|
||||
mBluetoothService.sendUuidIntent(address);
|
||||
}
|
||||
}
|
||||
|
||||
private void onRestartRequired() {
|
||||
if (mBluetoothService.isEnabled()) {
|
||||
Log.e(TAG, "*** A serious error occured (did bluetoothd crash?) - " +
|
||||
|
@ -76,10 +76,17 @@ public class BluetoothService extends IBluetooth.Stub {
|
||||
|
||||
private static final int MESSAGE_REGISTER_SDP_RECORDS = 1;
|
||||
private static final int MESSAGE_FINISH_DISABLE = 2;
|
||||
private static final int MESSAGE_UUID_INTENT = 3;
|
||||
|
||||
// The timeout used to sent the UUIDs Intent
|
||||
// This timeout should be greater than the page timeout
|
||||
private static final int UUID_INTENT_DELAY = 6000;
|
||||
|
||||
private final Map<String, String> mAdapterProperties;
|
||||
private final HashMap <String, Map<String, String>> mDeviceProperties;
|
||||
|
||||
private final ArrayList <String> mUuidIntentTracker;
|
||||
|
||||
static {
|
||||
classInitNative();
|
||||
}
|
||||
@ -104,6 +111,7 @@ public class BluetoothService extends IBluetooth.Stub {
|
||||
mIsDiscovering = false;
|
||||
mAdapterProperties = new HashMap<String, String>();
|
||||
mDeviceProperties = new HashMap<String, Map<String,String>>();
|
||||
mUuidIntentTracker = new ArrayList<String>();
|
||||
registerForAirplaneMode();
|
||||
}
|
||||
|
||||
@ -291,6 +299,11 @@ public class BluetoothService extends IBluetooth.Stub {
|
||||
case MESSAGE_FINISH_DISABLE:
|
||||
finishDisable(msg.arg1 != 0);
|
||||
break;
|
||||
case MESSAGE_UUID_INTENT:
|
||||
String address = (String)msg.obj;
|
||||
if (address != null)
|
||||
sendUuidIntent(address);
|
||||
break;
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -976,6 +989,10 @@ public class BluetoothService extends IBluetooth.Stub {
|
||||
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
|
||||
return null;
|
||||
}
|
||||
return getUuidFromCache(address);
|
||||
}
|
||||
|
||||
private ParcelUuid[] getUuidFromCache(String address) {
|
||||
String value = getRemoteDeviceProperty(address, "UUIDs");
|
||||
if (value == null) return null;
|
||||
|
||||
@ -990,6 +1007,36 @@ public class BluetoothService extends IBluetooth.Stub {
|
||||
return uuids;
|
||||
}
|
||||
|
||||
public synchronized boolean fetchRemoteUuidsWithSdp(String address) {
|
||||
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM, "Need BLUETOOTH permission");
|
||||
if (!BluetoothAdapter.checkBluetoothAddress(address)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (mUuidIntentTracker.contains(address)) {
|
||||
// An SDP query for this address is already in progress
|
||||
return true;
|
||||
}
|
||||
|
||||
boolean ret;
|
||||
if (getBondState(address) == BluetoothDevice.BOND_BONDED) {
|
||||
String path = getObjectPathFromAddress(address);
|
||||
if (path == null) return false;
|
||||
|
||||
// Use an empty string for the UUID pattern
|
||||
ret = discoverServicesNative(path, "");
|
||||
} else {
|
||||
ret = createDeviceNative(address);
|
||||
}
|
||||
|
||||
mUuidIntentTracker.add(address);
|
||||
|
||||
Message message = mHandler.obtainMessage(MESSAGE_UUID_INTENT);
|
||||
message.obj = address;
|
||||
mHandler.sendMessageDelayed(message, UUID_INTENT_DELAY);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the rfcomm channel associated with the UUID.
|
||||
*
|
||||
@ -1121,6 +1168,18 @@ public class BluetoothService extends IBluetooth.Stub {
|
||||
Settings.System.AIRPLANE_MODE_ON, 0) == 1;
|
||||
}
|
||||
|
||||
/* Broadcast the Uuid intent */
|
||||
/*package*/ synchronized void sendUuidIntent(String address) {
|
||||
if (mUuidIntentTracker.contains(address)) {
|
||||
ParcelUuid[] uuid = getUuidFromCache(address);
|
||||
Intent intent = new Intent(BluetoothDevice.ACTION_UUID);
|
||||
intent.putExtra(BluetoothDevice.EXTRA_UUID, uuid);
|
||||
mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
|
||||
|
||||
mUuidIntentTracker.remove(address);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
|
||||
pw.println("\nmIsAirplaneSensitive = " + mIsAirplaneSensitive + "\n");
|
||||
@ -1284,4 +1343,6 @@ public class BluetoothService extends IBluetooth.Stub {
|
||||
private native boolean setPairingConfirmationNative(String address, boolean confirm,
|
||||
int nativeData);
|
||||
private native boolean setDevicePropertyBooleanNative(String objectPath, String key, int value);
|
||||
private native boolean createDeviceNative(String address);
|
||||
private native boolean discoverServicesNative(String objectPath, String pattern);
|
||||
}
|
||||
|
@ -48,6 +48,8 @@ static jmethodID method_onDeviceRemoved;
|
||||
static jmethodID method_onDeviceDisconnectRequested;
|
||||
|
||||
static jmethodID method_onCreatePairedDeviceResult;
|
||||
static jmethodID method_onCreateDeviceResult;
|
||||
static jmethodID method_onDiscoverServicesResult;
|
||||
static jmethodID method_onGetDeviceServiceChannelResult;
|
||||
|
||||
static jmethodID method_onRequestPinCode;
|
||||
@ -92,6 +94,10 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
|
||||
|
||||
method_onCreatePairedDeviceResult = env->GetMethodID(clazz, "onCreatePairedDeviceResult",
|
||||
"(Ljava/lang/String;I)V");
|
||||
method_onCreateDeviceResult = env->GetMethodID(clazz, "onCreateDeviceResult",
|
||||
"(Ljava/lang/String;Z)V");
|
||||
method_onDiscoverServicesResult = env->GetMethodID(clazz, "onDiscoverServicesResult",
|
||||
"(Ljava/lang/String;Z)V");
|
||||
|
||||
method_onAgentAuthorize = env->GetMethodID(clazz, "onAgentAuthorize",
|
||||
"(Ljava/lang/String;Ljava/lang/String;)Z");
|
||||
@ -1097,6 +1103,54 @@ done:
|
||||
free(user);
|
||||
}
|
||||
|
||||
void onCreateDeviceResult(DBusMessage *msg, void *user, void *n) {
|
||||
LOGV(__FUNCTION__);
|
||||
|
||||
native_data_t *nat = (native_data_t *)n;
|
||||
const char *address= (const char *)user;
|
||||
DBusError err;
|
||||
dbus_error_init(&err);
|
||||
JNIEnv *env;
|
||||
nat->vm->GetEnv((void**)&env, nat->envVer);
|
||||
|
||||
LOGV("... Address = %s", address);
|
||||
|
||||
bool result = JNI_TRUE;
|
||||
if (dbus_set_error_from_message(&err, msg)) {
|
||||
LOG_AND_FREE_DBUS_ERROR(&err);
|
||||
result = JNI_FALSE;
|
||||
}
|
||||
env->CallVoidMethod(nat->me,
|
||||
method_onCreateDeviceResult,
|
||||
env->NewStringUTF(address),
|
||||
result);
|
||||
free(user);
|
||||
}
|
||||
|
||||
void onDiscoverServicesResult(DBusMessage *msg, void *user, void *n) {
|
||||
LOGV(__FUNCTION__);
|
||||
|
||||
native_data_t *nat = (native_data_t *)n;
|
||||
const char *path = (const char *)user;
|
||||
DBusError err;
|
||||
dbus_error_init(&err);
|
||||
JNIEnv *env;
|
||||
nat->vm->GetEnv((void**)&env, nat->envVer);
|
||||
|
||||
LOGV("... Device Path = %s", path);
|
||||
|
||||
bool result = JNI_TRUE;
|
||||
if (dbus_set_error_from_message(&err, msg)) {
|
||||
LOG_AND_FREE_DBUS_ERROR(&err);
|
||||
result = JNI_FALSE;
|
||||
}
|
||||
env->CallVoidMethod(nat->me,
|
||||
method_onDiscoverServicesResult,
|
||||
env->NewStringUTF(path),
|
||||
result);
|
||||
free(user);
|
||||
}
|
||||
|
||||
void onGetDeviceServiceChannelResult(DBusMessage *msg, void *user, void *n) {
|
||||
LOGV(__FUNCTION__);
|
||||
|
||||
|
@ -66,6 +66,8 @@ extern DBusHandlerResult agent_event_filter(DBusConnection *conn,
|
||||
DBusMessage *msg,
|
||||
void *data);
|
||||
void onCreatePairedDeviceResult(DBusMessage *msg, void *user, void *nat);
|
||||
void onDiscoverServicesResult(DBusMessage *msg, void *user, void *nat);
|
||||
void onCreateDeviceResult(DBusMessage *msg, void *user, void *nat);
|
||||
|
||||
|
||||
/** Get native data stored in the opaque (Java code maintained) pointer mNativeData
|
||||
@ -757,6 +759,75 @@ static jboolean setDevicePropertyBooleanNative(JNIEnv *env, jobject object,
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
static jboolean createDeviceNative(JNIEnv *env, jobject object,
|
||||
jstring address) {
|
||||
LOGV(__FUNCTION__);
|
||||
#ifdef HAVE_BLUETOOTH
|
||||
native_data_t *nat = get_native_data(env, object);
|
||||
jobject eventLoop = env->GetObjectField(object, field_mEventLoop);
|
||||
struct event_loop_native_data_t *eventLoopNat =
|
||||
get_EventLoop_native_data(env, eventLoop);
|
||||
|
||||
if (nat && eventLoopNat) {
|
||||
const char *c_address = env->GetStringUTFChars(address, NULL);
|
||||
LOGV("... address = %s", c_address);
|
||||
char *context_address = (char *)calloc(BTADDR_SIZE, sizeof(char));
|
||||
strlcpy(context_address, c_address, BTADDR_SIZE); // for callback
|
||||
|
||||
bool ret = dbus_func_args_async(env, nat->conn, -1,
|
||||
onCreateDeviceResult,
|
||||
context_address,
|
||||
eventLoopNat,
|
||||
get_adapter_path(env, object),
|
||||
DBUS_ADAPTER_IFACE,
|
||||
"CreateDevice",
|
||||
DBUS_TYPE_STRING, &c_address,
|
||||
DBUS_TYPE_INVALID);
|
||||
env->ReleaseStringUTFChars(address, c_address);
|
||||
return ret ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
#endif
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
static jboolean discoverServicesNative(JNIEnv *env, jobject object,
|
||||
jstring path, jstring pattern) {
|
||||
LOGV(__FUNCTION__);
|
||||
#ifdef HAVE_BLUETOOTH
|
||||
native_data_t *nat = get_native_data(env, object);
|
||||
jobject eventLoop = env->GetObjectField(object, field_mEventLoop);
|
||||
struct event_loop_native_data_t *eventLoopNat =
|
||||
get_EventLoop_native_data(env, eventLoop);
|
||||
|
||||
if (nat && eventLoopNat) {
|
||||
const char *c_path = env->GetStringUTFChars(path, NULL);
|
||||
const char *c_pattern = env->GetStringUTFChars(pattern, NULL);
|
||||
int len = env->GetStringLength(path) + 1;
|
||||
char *context_path = (char *)calloc(len, sizeof(char));
|
||||
strlcpy(context_path, c_path, len); // for callback
|
||||
|
||||
LOGV("... Object Path = %s", c_path);
|
||||
LOGV("... Pattern = %s, strlen = %d", c_pattern, strlen(c_pattern));
|
||||
|
||||
bool ret = dbus_func_args_async(env, nat->conn, -1,
|
||||
onDiscoverServicesResult,
|
||||
context_path,
|
||||
eventLoopNat,
|
||||
c_path,
|
||||
DBUS_DEVICE_IFACE,
|
||||
"DiscoverServices",
|
||||
DBUS_TYPE_STRING, &c_pattern,
|
||||
DBUS_TYPE_INVALID);
|
||||
env->ReleaseStringUTFChars(path, c_path);
|
||||
env->ReleaseStringUTFChars(pattern, c_pattern);
|
||||
return ret ? JNI_TRUE : JNI_FALSE;
|
||||
}
|
||||
#endif
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
|
||||
static JNINativeMethod sMethods[] = {
|
||||
/* name, signature, funcPtr */
|
||||
{"classInitNative", "()V", (void*)classInitNative},
|
||||
@ -797,6 +868,8 @@ static JNINativeMethod sMethods[] = {
|
||||
(void *)cancelPairingUserInputNative},
|
||||
{"setDevicePropertyBooleanNative", "(Ljava/lang/String;Ljava/lang/String;I)Z",
|
||||
(void *)setDevicePropertyBooleanNative},
|
||||
{"createDeviceNative", "(Ljava/lang/String;)Z", (void *)createDeviceNative},
|
||||
{"discoverServicesNative", "(Ljava/lang/String;Ljava/lang/String;)Z", (void *)discoverServicesNative},
|
||||
};
|
||||
|
||||
int register_android_server_BluetoothService(JNIEnv *env) {
|
||||
|
Reference in New Issue
Block a user