Initial support of 2.1 pairing.
Note: Some cases have not been tested yet, as we would need to get proper UI support.
This commit is contained in:
@ -74,6 +74,14 @@ public class BluetoothDevice {
|
||||
/** An existing bond was explicitly revoked */
|
||||
public static final int UNBOND_REASON_REMOVED = 6;
|
||||
|
||||
/* The user will be prompted to enter a pin */
|
||||
public static final int PAIRING_VARIANT_PIN = 0;
|
||||
/* The user will be prompted to enter a passkey */
|
||||
public static final int PAIRING_VARIANT_PASSKEY = 1;
|
||||
/* The user will be prompted to confirm the passkey displayed on the screen */
|
||||
public static final int PAIRING_VARIANT_CONFIRMATION = 2;
|
||||
|
||||
|
||||
private static final String TAG = "BluetoothDevice";
|
||||
|
||||
private final IBluetoothDevice mService;
|
||||
@ -358,9 +366,24 @@ public class BluetoothDevice {
|
||||
} catch (RemoteException e) {Log.e(TAG, "", e);}
|
||||
return false;
|
||||
}
|
||||
public boolean cancelPin(String address) {
|
||||
|
||||
public boolean setPasskey(String address, int passkey) {
|
||||
try {
|
||||
return mService.cancelPin(address);
|
||||
return mService.setPasskey(address, passkey);
|
||||
} catch (RemoteException e) {Log.e(TAG, "", e);}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean setPairingConfirmation(String address, boolean confirm) {
|
||||
try {
|
||||
return mService.setPairingConfirmation(address, confirm);
|
||||
} catch (RemoteException e) {Log.e(TAG, "", e);}
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean cancelPairingUserInput(String address) {
|
||||
try {
|
||||
return mService.cancelPairingUserInput(address);
|
||||
} catch (RemoteException e) {Log.e(TAG, "", e);}
|
||||
return false;
|
||||
}
|
||||
|
@ -57,6 +57,10 @@ public interface BluetoothIntent {
|
||||
"android.bluetooth.intent.BOND_PREVIOUS_STATE";
|
||||
public static final String REASON =
|
||||
"android.bluetooth.intent.REASON";
|
||||
public static final String PAIRING_VARIANT =
|
||||
"android.bluetooth.intent.PAIRING_VARIANT";
|
||||
public static final String PASSKEY =
|
||||
"android.bluetooth.intent.PASSKEY";
|
||||
|
||||
/** Broadcast when the local Bluetooth device state changes, for example
|
||||
* when Bluetooth is enabled. Will contain int extra's BLUETOOTH_STATE and
|
||||
|
@ -54,5 +54,8 @@ interface IBluetoothDevice
|
||||
int getRemoteServiceChannel(in String address, String 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);
|
||||
|
||||
}
|
||||
|
@ -959,7 +959,38 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
|
||||
return setPinNative(address, pinString, data.intValue());
|
||||
}
|
||||
|
||||
public synchronized boolean cancelPin(String address) {
|
||||
public synchronized boolean setPasskey(String address, int passkey) {
|
||||
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
|
||||
"Need BLUETOOTH_ADMIN permission");
|
||||
if (passkey < 0 || passkey > 999999 || !BluetoothDevice.checkBluetoothAddress(address)) {
|
||||
return false;
|
||||
}
|
||||
address = address.toUpperCase();
|
||||
Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
|
||||
if (data == null) {
|
||||
Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
|
||||
"ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
|
||||
" or by bluez.\n");
|
||||
return false;
|
||||
}
|
||||
return setPasskeyNative(address, passkey, data.intValue());
|
||||
}
|
||||
|
||||
public synchronized boolean setPairingConfirmation(String address, boolean confirm) {
|
||||
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
|
||||
"Need BLUETOOTH_ADMIN permission");
|
||||
address = address.toUpperCase();
|
||||
Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
|
||||
if (data == null) {
|
||||
Log.w(TAG, "setPasskey(" + address + ") called but no native data available, " +
|
||||
"ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
|
||||
" or by bluez.\n");
|
||||
return false;
|
||||
}
|
||||
return setPairingConfirmationNative(address, confirm, data.intValue());
|
||||
}
|
||||
|
||||
public synchronized boolean cancelPairingUserInput(String address) {
|
||||
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
|
||||
"Need BLUETOOTH_ADMIN permission");
|
||||
if (!BluetoothDevice.checkBluetoothAddress(address)) {
|
||||
@ -968,12 +999,12 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
|
||||
address = address.toUpperCase();
|
||||
Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
|
||||
if (data == null) {
|
||||
Log.w(TAG, "cancelPin(" + address + ") called but no native data available, " +
|
||||
"ignoring. Maybe the PasskeyAgent Request was already cancelled by the remote " +
|
||||
"or by bluez.\n");
|
||||
Log.w(TAG, "cancelUserInputNative(" + address + ") called but no native data " +
|
||||
"available, ignoring. Maybe the PasskeyAgent Request was already cancelled " +
|
||||
"by the remote or by bluez.\n");
|
||||
return false;
|
||||
}
|
||||
return cancelPinNative(address, data.intValue());
|
||||
return cancelPairingUserInputNative(address, data.intValue());
|
||||
}
|
||||
|
||||
private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
|
||||
@ -1160,7 +1191,10 @@ public class BluetoothDeviceService extends IBluetoothDevice.Stub {
|
||||
private native int getDeviceServiceChannelNative(String objectPath, String uuid,
|
||||
int attributeId);
|
||||
|
||||
private native boolean cancelPinNative(String address, int nativeData);
|
||||
private native boolean cancelPairingUserInputNative(String address, int nativeData);
|
||||
private native boolean setPinNative(String address, String pin, int nativeData);
|
||||
private native boolean setPasskeyNative(String address, int passkey, int nativeData);
|
||||
private native boolean setPairingConfirmationNative(String address, boolean confirm,
|
||||
int nativeData);
|
||||
|
||||
}
|
||||
|
@ -317,23 +317,53 @@ class BluetoothEventLoop {
|
||||
}
|
||||
mBluetoothService.setRemoteDeviceProperty(address, name, uuid);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void onRequestPinCode(String objectPath, int nativeData) {
|
||||
private String checkPairingRequestAndGetAddress(String objectPath, int nativeData) {
|
||||
String address = mBluetoothService.getAddressFromObjectPath(objectPath);
|
||||
if (address == null) {
|
||||
Log.e(TAG, "Unable to get device address in onRequestPinCode, returning null");
|
||||
return;
|
||||
Log.e(TAG, "Unable to get device address in checkPairingRequestAndGetAddress, " +
|
||||
"returning null");
|
||||
return null;
|
||||
}
|
||||
address = address.toUpperCase();
|
||||
mPasskeyAgentRequestData.put(address, new Integer(nativeData));
|
||||
|
||||
if (mBluetoothService.getBluetoothState() == BluetoothDevice.BLUETOOTH_STATE_TURNING_OFF) {
|
||||
// shutdown path
|
||||
mBluetoothService.cancelPin(address);
|
||||
return;
|
||||
mBluetoothService.cancelPairingUserInput(address);
|
||||
return null;
|
||||
}
|
||||
return address;
|
||||
}
|
||||
|
||||
private void onRequestConfirmation(String objectPath, int passkey, int nativeData) {
|
||||
String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
|
||||
if (address == null) return;
|
||||
|
||||
Intent intent = new Intent(BluetoothIntent.PAIRING_REQUEST_ACTION);
|
||||
intent.putExtra(BluetoothIntent.ADDRESS, address);
|
||||
intent.putExtra(BluetoothIntent.PASSKEY, passkey);
|
||||
intent.putExtra(BluetoothIntent.PAIRING_VARIANT,
|
||||
BluetoothDevice.PAIRING_VARIANT_CONFIRMATION);
|
||||
mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
|
||||
return;
|
||||
}
|
||||
|
||||
private void onRequestPasskey(String objectPath, int nativeData) {
|
||||
String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
|
||||
if (address == null) return;
|
||||
|
||||
Intent intent = new Intent(BluetoothIntent.PAIRING_REQUEST_ACTION);
|
||||
intent.putExtra(BluetoothIntent.ADDRESS, address);
|
||||
intent.putExtra(BluetoothIntent.PAIRING_VARIANT, BluetoothDevice.PAIRING_VARIANT_PASSKEY);
|
||||
mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
|
||||
return;
|
||||
}
|
||||
|
||||
private void onRequestPinCode(String objectPath, int nativeData) {
|
||||
String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
|
||||
if (address == null) return;
|
||||
|
||||
if (mBluetoothService.getBondState().getBondState(address) ==
|
||||
BluetoothDevice.BOND_BONDING) {
|
||||
@ -358,6 +388,7 @@ class BluetoothEventLoop {
|
||||
}
|
||||
Intent intent = new Intent(BluetoothIntent.PAIRING_REQUEST_ACTION);
|
||||
intent.putExtra(BluetoothIntent.ADDRESS, address);
|
||||
intent.putExtra(BluetoothIntent.PAIRING_VARIANT, BluetoothDevice.PAIRING_VARIANT_PIN);
|
||||
mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
|
||||
return;
|
||||
}
|
||||
@ -386,9 +417,9 @@ class BluetoothEventLoop {
|
||||
}
|
||||
|
||||
private void onAgentCancel() {
|
||||
// We immediately response to DBUS Authorize() so this should not
|
||||
// usually happen
|
||||
log("onAgentCancel");
|
||||
Intent intent = new Intent(BluetoothIntent.PAIRING_CANCEL_ACTION);
|
||||
mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
|
||||
return;
|
||||
}
|
||||
|
||||
private void onRestartRequired() {
|
||||
|
@ -437,6 +437,65 @@ static jint isEnabledNative(JNIEnv *env, jobject object) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
static jboolean setPairingConfirmationNative(JNIEnv *env, jobject object,
|
||||
jstring address, bool confirm,
|
||||
int nativeData) {
|
||||
#ifdef HAVE_BLUETOOTH
|
||||
LOGV(__FUNCTION__);
|
||||
native_data_t *nat = get_native_data(env, object);
|
||||
if (nat) {
|
||||
DBusMessage *msg = (DBusMessage *)nativeData;
|
||||
DBusMessage *reply;
|
||||
if (confirm) {
|
||||
reply = dbus_message_new_method_return(msg);
|
||||
} else {
|
||||
reply = dbus_message_new_error(msg,
|
||||
"org.bluez.Error.Rejected", "User rejected confirmation");
|
||||
}
|
||||
|
||||
if (!reply) {
|
||||
LOGE("%s: Cannot create message reply to RequestConfirmation to "
|
||||
"D-Bus\n", __FUNCTION__);
|
||||
dbus_message_unref(msg);
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
dbus_connection_send(nat->conn, reply, NULL);
|
||||
dbus_message_unref(msg);
|
||||
dbus_message_unref(reply);
|
||||
return JNI_TRUE;
|
||||
}
|
||||
#endif
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
static jboolean setPasskeyNative(JNIEnv *env, jobject object, jstring address,
|
||||
int passkey, int nativeData) {
|
||||
#ifdef HAVE_BLUETOOTH
|
||||
LOGV(__FUNCTION__);
|
||||
native_data_t *nat = get_native_data(env, object);
|
||||
if (nat) {
|
||||
DBusMessage *msg = (DBusMessage *)nativeData;
|
||||
DBusMessage *reply = dbus_message_new_method_return(msg);
|
||||
if (!reply) {
|
||||
LOGE("%s: Cannot create message reply to return Passkey code to "
|
||||
"D-Bus\n", __FUNCTION__);
|
||||
dbus_message_unref(msg);
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
dbus_message_append_args(reply, DBUS_TYPE_UINT32, (uint32_t *)&passkey,
|
||||
DBUS_TYPE_INVALID);
|
||||
|
||||
dbus_connection_send(nat->conn, reply, NULL);
|
||||
dbus_message_unref(msg);
|
||||
dbus_message_unref(reply);
|
||||
return JNI_TRUE;
|
||||
}
|
||||
#endif
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
static jboolean setPinNative(JNIEnv *env, jobject object, jstring address,
|
||||
jstring pin, int nativeData) {
|
||||
#ifdef HAVE_BLUETOOTH
|
||||
@ -467,17 +526,17 @@ static jboolean setPinNative(JNIEnv *env, jobject object, jstring address,
|
||||
return JNI_FALSE;
|
||||
}
|
||||
|
||||
static jboolean cancelPinNative(JNIEnv *env, jobject object, jstring address,
|
||||
int nativeData) {
|
||||
static jboolean cancelPairingUserInputNative(JNIEnv *env, jobject object,
|
||||
jstring address, int nativeData) {
|
||||
#ifdef HAVE_BLUETOOTH
|
||||
LOGV(__FUNCTION__);
|
||||
native_data_t *nat = get_native_data(env, object);
|
||||
if (nat) {
|
||||
DBusMessage *msg = (DBusMessage *)nativeData;
|
||||
DBusMessage *reply = dbus_message_new_error(msg,
|
||||
"org.bluez.Error.Canceled", "PIN Entry was canceled");
|
||||
"org.bluez.Error.Canceled", "Pairing User Input was canceled");
|
||||
if (!reply) {
|
||||
LOGE("%s: Cannot create message reply to return PIN cancel to "
|
||||
LOGE("%s: Cannot create message reply to return cancelUserInput to"
|
||||
"D-BUS\n", __FUNCTION__);
|
||||
dbus_message_unref(msg);
|
||||
return JNI_FALSE;
|
||||
@ -665,8 +724,12 @@ static JNINativeMethod sMethods[] = {
|
||||
{"getDeviceServiceChannelNative", "(Ljava/lang/String;Ljava/lang/String;I)I",
|
||||
(void *)getDeviceServiceChannelNative},
|
||||
|
||||
{"setPairingConfirmationNative", "(Ljava/lang/String;ZI)Z",
|
||||
(void *)setPairingConfirmationNative},
|
||||
{"setPasskeyNative", "(Ljava/lang/String;II)Z", (void *)setPasskeyNative},
|
||||
{"setPinNative", "(Ljava/lang/String;Ljava/lang/String;I)Z", (void *)setPinNative},
|
||||
{"cancelPinNative", "(Ljava/lang/String;I)Z", (void *)cancelPinNative},
|
||||
{"cancelPairingUserInputNative", "(Ljava/lang/String;I)Z",
|
||||
(void *)cancelPairingUserInputNative},
|
||||
};
|
||||
|
||||
int register_android_server_BluetoothDeviceService(JNIEnv *env) {
|
||||
|
@ -50,6 +50,8 @@ static jmethodID method_onCreatePairedDeviceResult;
|
||||
static jmethodID method_onGetDeviceServiceChannelResult;
|
||||
|
||||
static jmethodID method_onRequestPinCode;
|
||||
static jmethodID method_onRequestPasskey;
|
||||
static jmethodID method_onRequestConfirmation;
|
||||
static jmethodID method_onAgentAuthorize;
|
||||
static jmethodID method_onAgentCancel;
|
||||
|
||||
@ -89,6 +91,10 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
|
||||
method_onAgentCancel = env->GetMethodID(clazz, "onAgentCancel", "()V");
|
||||
method_onRequestPinCode = env->GetMethodID(clazz, "onRequestPinCode",
|
||||
"(Ljava/lang/String;I)V");
|
||||
method_onRequestPasskey = env->GetMethodID(clazz, "onRequestPasskey",
|
||||
"(Ljava/lang/String;I)V");
|
||||
method_onRequestConfirmation = env->GetMethodID(clazz, "onRequestConfirmation",
|
||||
"(Ljava/lang/String;II)V");
|
||||
|
||||
field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I");
|
||||
#endif
|
||||
@ -871,6 +877,38 @@ DBusHandlerResult agent_event_filter(DBusConnection *conn,
|
||||
env->NewStringUTF(object_path),
|
||||
int(msg));
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
} else if (dbus_message_is_method_call(msg,
|
||||
"org.bluez.Agent", "RequestPasskey")) {
|
||||
char *object_path;
|
||||
if (!dbus_message_get_args(msg, NULL,
|
||||
DBUS_TYPE_OBJECT_PATH, &object_path,
|
||||
DBUS_TYPE_INVALID)) {
|
||||
LOGE("%s: Invalid arguments for RequestPasskey() method", __FUNCTION__);
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
||||
dbus_message_ref(msg); // increment refcount because we pass to java
|
||||
env->CallVoidMethod(nat->me, method_onRequestPasskey,
|
||||
env->NewStringUTF(object_path),
|
||||
int(msg));
|
||||
} else if (dbus_message_is_method_call(msg,
|
||||
"org.bluez.Agent", "RequestConfirmation")) {
|
||||
char *object_path;
|
||||
uint32_t passkey;
|
||||
if (!dbus_message_get_args(msg, NULL,
|
||||
DBUS_TYPE_OBJECT_PATH, &object_path,
|
||||
DBUS_TYPE_UINT32, &passkey,
|
||||
DBUS_TYPE_INVALID)) {
|
||||
LOGE("%s: Invalid arguments for RequestConfirmation() method", __FUNCTION__);
|
||||
return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
|
||||
}
|
||||
|
||||
dbus_message_ref(msg); // increment refcount because we pass to java
|
||||
env->CallVoidMethod(nat->me, method_onRequestConfirmation,
|
||||
env->NewStringUTF(object_path),
|
||||
passkey,
|
||||
int(msg));
|
||||
return DBUS_HANDLER_RESULT_HANDLED;
|
||||
} else if (dbus_message_is_method_call(msg,
|
||||
"org.bluez.Agent", "Release")) {
|
||||
// reply
|
||||
|
Reference in New Issue
Block a user