Merge change 25779 into eclair

* changes:
  Add new API for fetching UUIDs using SDP.
This commit is contained in:
Android (Google) Code Review
2009-09-19 14:30:37 -04:00
6 changed files with 257 additions and 0 deletions

View File

@ -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 {

View File

@ -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);

View File

@ -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?) - " +

View File

@ -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);
}

View File

@ -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__);

View File

@ -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) {