Out Of Band API for Secure Simple Pairing.
Change-Id: I54ded27ab85d46eef3d2cca84f2394b1ffe88ced
This commit is contained in:
@ -26,8 +26,10 @@ import android.os.ParcelUuid;
|
|||||||
import android.os.RemoteException;
|
import android.os.RemoteException;
|
||||||
import android.os.ServiceManager;
|
import android.os.ServiceManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
@ -863,6 +865,37 @@ public final class BluetoothAdapter {
|
|||||||
return socket;
|
return socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read the local Out of Band Pairing Data
|
||||||
|
* <p>Requires {@link android.Manifest.permission#BLUETOOTH}
|
||||||
|
*
|
||||||
|
* @return Pair<byte[], byte[]> of Hash and Randomizer
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public Pair<byte[], byte[]> readOutOfBandData() {
|
||||||
|
if (getState() != STATE_ON) return null;
|
||||||
|
try {
|
||||||
|
byte[] hash = new byte[16];
|
||||||
|
byte[] randomizer = new byte[16];
|
||||||
|
|
||||||
|
byte[] ret = mService.readOutOfBandData();
|
||||||
|
|
||||||
|
if (ret == null || ret.length != 32) return null;
|
||||||
|
|
||||||
|
hash = Arrays.copyOfRange(ret, 0, 16);
|
||||||
|
randomizer = Arrays.copyOfRange(ret, 16, 32);
|
||||||
|
|
||||||
|
if (DBG) {
|
||||||
|
Log.d(TAG, "readOutOfBandData:" + Arrays.toString(hash) +
|
||||||
|
":" + Arrays.toString(randomizer));
|
||||||
|
}
|
||||||
|
return new Pair<byte[], byte[]>(hash, randomizer);
|
||||||
|
|
||||||
|
} catch (RemoteException e) {Log.e(TAG, "", e);}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private Set<BluetoothDevice> toDeviceSet(String[] addresses) {
|
private Set<BluetoothDevice> toDeviceSet(String[] addresses) {
|
||||||
Set<BluetoothDevice> devices = new HashSet<BluetoothDevice>(addresses.length);
|
Set<BluetoothDevice> devices = new HashSet<BluetoothDevice>(addresses.length);
|
||||||
for (int i = 0; i < addresses.length; i++) {
|
for (int i = 0; i < addresses.length; i++) {
|
||||||
|
@ -325,7 +325,9 @@ public final class BluetoothDevice implements Parcelable {
|
|||||||
/** The user will be prompted to enter the passkey displayed on remote device
|
/** The user will be prompted to enter the passkey displayed on remote device
|
||||||
* @hide */
|
* @hide */
|
||||||
public static final int PAIRING_VARIANT_DISPLAY_PASSKEY = 4;
|
public static final int PAIRING_VARIANT_DISPLAY_PASSKEY = 4;
|
||||||
|
/** The user will be prompted to accept or deny the OOB pairing request
|
||||||
|
* @hide */
|
||||||
|
public static final int PAIRING_VARIANT_OOB_CONSENT = 5;
|
||||||
/**
|
/**
|
||||||
* Used as an extra field in {@link #ACTION_UUID} intents,
|
* Used as an extra field in {@link #ACTION_UUID} intents,
|
||||||
* Contains the {@link android.os.ParcelUuid}s of the remote device which
|
* Contains the {@link android.os.ParcelUuid}s of the remote device which
|
||||||
@ -463,6 +465,52 @@ public final class BluetoothDevice implements Parcelable {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Start the bonding (pairing) process with the remote device using the
|
||||||
|
* Out Of Band mechanism.
|
||||||
|
*
|
||||||
|
* <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 hash - Simple Secure pairing hash
|
||||||
|
* @param randomizer - The random key obtained using OOB
|
||||||
|
* @return false on immediate error, true if bonding will begin
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public boolean createBondOutOfBand(byte[] hash, byte[] randomizer) {
|
||||||
|
try {
|
||||||
|
return sService.createBondOutOfBand(mAddress, hash, randomizer);
|
||||||
|
} catch (RemoteException e) {Log.e(TAG, "", e);}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the Out Of Band data for a remote device to be used later
|
||||||
|
* in the pairing mechanism. Users can obtain this data through other
|
||||||
|
* trusted channels
|
||||||
|
*
|
||||||
|
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
|
||||||
|
*
|
||||||
|
* @param hash Simple Secure pairing hash
|
||||||
|
* @param randomizer The random key obtained using OOB
|
||||||
|
* @return false on error; true otherwise
|
||||||
|
*
|
||||||
|
* @hide
|
||||||
|
*/
|
||||||
|
public boolean setDeviceOutOfBandData(byte[] hash, byte[] randomizer) {
|
||||||
|
try {
|
||||||
|
return sService.setDeviceOutOfBandData(mAddress, hash, randomizer);
|
||||||
|
} catch (RemoteException e) {Log.e(TAG, "", e);}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Cancel an in-progress bonding request started with {@link #createBond}.
|
* Cancel an in-progress bonding request started with {@link #createBond}.
|
||||||
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
|
* <p>Requires {@link android.Manifest.permission#BLUETOOTH_ADMIN}.
|
||||||
@ -616,6 +664,14 @@ public final class BluetoothDevice implements Parcelable {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** @hide */
|
||||||
|
public boolean setRemoteOutOfBandData() {
|
||||||
|
try {
|
||||||
|
return sService.setRemoteOutOfBandData(mAddress);
|
||||||
|
} catch (RemoteException e) {Log.e(TAG, "", e);}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
/** @hide */
|
/** @hide */
|
||||||
public boolean cancelPairingUserInput() {
|
public boolean cancelPairingUserInput() {
|
||||||
try {
|
try {
|
||||||
|
@ -44,12 +44,15 @@ interface IBluetooth
|
|||||||
boolean startDiscovery();
|
boolean startDiscovery();
|
||||||
boolean cancelDiscovery();
|
boolean cancelDiscovery();
|
||||||
boolean isDiscovering();
|
boolean isDiscovering();
|
||||||
|
byte[] readOutOfBandData();
|
||||||
|
|
||||||
boolean createBond(in String address);
|
boolean createBond(in String address);
|
||||||
|
boolean createBondOutOfBand(in String address, in byte[] hash, in byte[] randomizer);
|
||||||
boolean cancelBondProcess(in String address);
|
boolean cancelBondProcess(in String address);
|
||||||
boolean removeBond(in String address);
|
boolean removeBond(in String address);
|
||||||
String[] listBonds();
|
String[] listBonds();
|
||||||
int getBondState(in String address);
|
int getBondState(in String address);
|
||||||
|
boolean setDeviceOutOfBandData(in String address, in byte[] hash, in byte[] randomizer);
|
||||||
|
|
||||||
String getRemoteName(in String address);
|
String getRemoteName(in String address);
|
||||||
int getRemoteClass(in String address);
|
int getRemoteClass(in String address);
|
||||||
@ -60,6 +63,7 @@ interface IBluetooth
|
|||||||
boolean setPin(in String address, in byte[] pin);
|
boolean setPin(in String address, in byte[] pin);
|
||||||
boolean setPasskey(in String address, int passkey);
|
boolean setPasskey(in String address, int passkey);
|
||||||
boolean setPairingConfirmation(in String address, boolean confirm);
|
boolean setPairingConfirmation(in String address, boolean confirm);
|
||||||
|
boolean setRemoteOutOfBandData(in String addres);
|
||||||
boolean cancelPairingUserInput(in String address);
|
boolean cancelPairingUserInput(in String address);
|
||||||
|
|
||||||
boolean setTrust(in String address, in boolean value);
|
boolean setTrust(in String address, in boolean value);
|
||||||
|
@ -551,6 +551,17 @@ class BluetoothEventLoop {
|
|||||||
mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
|
mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void onRequestOobData(String objectPath , int nativeData) {
|
||||||
|
String address = checkPairingRequestAndGetAddress(objectPath, nativeData);
|
||||||
|
if (address == null) return;
|
||||||
|
|
||||||
|
Intent intent = new Intent(BluetoothDevice.ACTION_PAIRING_REQUEST);
|
||||||
|
intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mAdapter.getRemoteDevice(address));
|
||||||
|
intent.putExtra(BluetoothDevice.EXTRA_PAIRING_VARIANT,
|
||||||
|
BluetoothDevice.PAIRING_VARIANT_OOB_CONSENT);
|
||||||
|
mContext.sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
|
||||||
|
}
|
||||||
|
|
||||||
private boolean onAgentAuthorize(String objectPath, String deviceUuid) {
|
private boolean onAgentAuthorize(String objectPath, String deviceUuid) {
|
||||||
String address = mBluetoothService.getAddressFromObjectPath(objectPath);
|
String address = mBluetoothService.getAddressFromObjectPath(objectPath);
|
||||||
if (address == null) {
|
if (address == null) {
|
||||||
@ -583,7 +594,21 @@ class BluetoothEventLoop {
|
|||||||
return authorized;
|
return authorized;
|
||||||
}
|
}
|
||||||
|
|
||||||
boolean isOtherSinkInNonDisconnectingState(String address) {
|
private boolean onAgentOutOfBandDataAvailable(String objectPath) {
|
||||||
|
if (!mBluetoothService.isEnabled()) return false;
|
||||||
|
|
||||||
|
String address = mBluetoothService.getAddressFromObjectPath(objectPath);
|
||||||
|
if (address == null) return false;
|
||||||
|
|
||||||
|
if (mBluetoothService.getDeviceOutOfBandData(
|
||||||
|
mAdapter.getRemoteDevice(address)) != null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean isOtherSinkInNonDisconnectingState(String address) {
|
||||||
BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
|
BluetoothA2dp a2dp = new BluetoothA2dp(mContext);
|
||||||
Set<BluetoothDevice> devices = a2dp.getNonDisconnectedSinks();
|
Set<BluetoothDevice> devices = a2dp.getNonDisconnectedSinks();
|
||||||
if (devices.size() == 0) return false;
|
if (devices.size() == 0) return false;
|
||||||
|
@ -50,6 +50,7 @@ import android.os.ServiceManager;
|
|||||||
import android.os.SystemService;
|
import android.os.SystemService;
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.util.Pair;
|
||||||
|
|
||||||
import com.android.internal.app.IBatteryStats;
|
import com.android.internal.app.IBatteryStats;
|
||||||
|
|
||||||
@ -129,6 +130,8 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
private final BluetoothProfileState mHfpProfileState;
|
private final BluetoothProfileState mHfpProfileState;
|
||||||
|
|
||||||
private BluetoothA2dpService mA2dpService;
|
private BluetoothA2dpService mA2dpService;
|
||||||
|
private final HashMap<String, Pair<byte[], byte[]>> mDeviceOobData;
|
||||||
|
|
||||||
private static String mDockAddress;
|
private static String mDockAddress;
|
||||||
private String mDockPin;
|
private String mDockPin;
|
||||||
|
|
||||||
@ -183,6 +186,7 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
mDeviceProperties = new HashMap<String, Map<String,String>>();
|
mDeviceProperties = new HashMap<String, Map<String,String>>();
|
||||||
|
|
||||||
mDeviceServiceChannelCache = new HashMap<String, Map<ParcelUuid, Integer>>();
|
mDeviceServiceChannelCache = new HashMap<String, Map<ParcelUuid, Integer>>();
|
||||||
|
mDeviceOobData = new HashMap<String, Pair<byte[], byte[]>>();
|
||||||
mUuidIntentTracker = new ArrayList<String>();
|
mUuidIntentTracker = new ArrayList<String>();
|
||||||
mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>();
|
mUuidCallbackTracker = new HashMap<RemoteService, IBluetoothCallback>();
|
||||||
mServiceRecordToPid = new HashMap<Integer, Integer>();
|
mServiceRecordToPid = new HashMap<Integer, Integer>();
|
||||||
@ -1119,7 +1123,7 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
mIsDiscovering = isDiscovering;
|
mIsDiscovering = isDiscovering;
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized boolean createBond(String address) {
|
private boolean isBondingFeasible(String address) {
|
||||||
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
|
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
|
||||||
"Need BLUETOOTH_ADMIN permission");
|
"Need BLUETOOTH_ADMIN permission");
|
||||||
if (!isEnabledInternal()) return false;
|
if (!isEnabledInternal()) return false;
|
||||||
@ -1149,6 +1153,11 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean createBond(String address) {
|
||||||
|
if (!isBondingFeasible(address)) return false;
|
||||||
|
|
||||||
if (!createPairedDeviceNative(address, 60000 /*1 minute*/ )) {
|
if (!createPairedDeviceNative(address, 60000 /*1 minute*/ )) {
|
||||||
return false;
|
return false;
|
||||||
@ -1160,6 +1169,51 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized boolean createBondOutOfBand(String address, byte[] hash,
|
||||||
|
byte[] randomizer) {
|
||||||
|
if (!isBondingFeasible(address)) return false;
|
||||||
|
|
||||||
|
if (!createPairedDeviceOutOfBandNative(address, 60000 /* 1 minute */)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDeviceOutOfBandData(address, hash, randomizer);
|
||||||
|
mBondState.setPendingOutgoingBonding(address);
|
||||||
|
mBondState.setBondState(address, BluetoothDevice.BOND_BONDING);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public synchronized boolean setDeviceOutOfBandData(String address, byte[] hash,
|
||||||
|
byte[] randomizer) {
|
||||||
|
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
|
||||||
|
"Need BLUETOOTH_ADMIN permission");
|
||||||
|
if (!isEnabledInternal()) return false;
|
||||||
|
|
||||||
|
Pair <byte[], byte[]> value = new Pair<byte[], byte[]>(hash, randomizer);
|
||||||
|
|
||||||
|
if (DBG) {
|
||||||
|
log("Setting out of band data for:" + address + ":" +
|
||||||
|
Arrays.toString(hash) + ":" + Arrays.toString(randomizer));
|
||||||
|
}
|
||||||
|
|
||||||
|
mDeviceOobData.put(address, value);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Pair<byte[], byte[]> getDeviceOutOfBandData(BluetoothDevice device) {
|
||||||
|
return mDeviceOobData.get(device.getAddress());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public synchronized byte[] readOutOfBandData() {
|
||||||
|
mContext.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
|
||||||
|
"Need BLUETOOTH permission");
|
||||||
|
if (!isEnabledInternal()) return null;
|
||||||
|
|
||||||
|
return readAdapterOutOfBandDataNative();
|
||||||
|
}
|
||||||
|
|
||||||
public synchronized boolean cancelBondProcess(String address) {
|
public synchronized boolean cancelBondProcess(String address) {
|
||||||
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
|
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
|
||||||
"Need BLUETOOTH_ADMIN permission");
|
"Need BLUETOOTH_ADMIN permission");
|
||||||
@ -1551,6 +1605,32 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
return setPairingConfirmationNative(address, confirm, data.intValue());
|
return setPairingConfirmationNative(address, confirm, data.intValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public synchronized boolean setRemoteOutOfBandData(String address) {
|
||||||
|
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
|
||||||
|
"Need BLUETOOTH_ADMIN permission");
|
||||||
|
if (!isEnabledInternal()) return false;
|
||||||
|
address = address.toUpperCase();
|
||||||
|
Integer data = mEventLoop.getPasskeyAgentRequestData().remove(address);
|
||||||
|
if (data == null) {
|
||||||
|
Log.w(TAG, "setRemoteOobData(" + address + ") called but no native data available, " +
|
||||||
|
"ignoring. Maybe the PasskeyAgent Request was cancelled by the remote device" +
|
||||||
|
" or by bluez.\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Pair<byte[], byte[]> val = mDeviceOobData.get(address);
|
||||||
|
byte[] hash, randomizer;
|
||||||
|
if (val == null) {
|
||||||
|
// TODO: check what should be passed in this case.
|
||||||
|
hash = new byte[16];
|
||||||
|
randomizer = new byte[16];
|
||||||
|
} else {
|
||||||
|
hash = val.first;
|
||||||
|
randomizer = val.second;
|
||||||
|
}
|
||||||
|
return setRemoteOutOfBandDataNative(address, hash, randomizer, data.intValue());
|
||||||
|
}
|
||||||
|
|
||||||
public synchronized boolean cancelPairingUserInput(String address) {
|
public synchronized boolean cancelPairingUserInput(String address) {
|
||||||
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
|
mContext.enforceCallingOrSelfPermission(BLUETOOTH_ADMIN_PERM,
|
||||||
"Need BLUETOOTH_ADMIN permission");
|
"Need BLUETOOTH_ADMIN permission");
|
||||||
@ -2084,6 +2164,9 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
private native boolean stopDiscoveryNative();
|
private native boolean stopDiscoveryNative();
|
||||||
|
|
||||||
private native boolean createPairedDeviceNative(String address, int timeout_ms);
|
private native boolean createPairedDeviceNative(String address, int timeout_ms);
|
||||||
|
private native boolean createPairedDeviceOutOfBandNative(String address, int timeout_ms);
|
||||||
|
private native byte[] readAdapterOutOfBandDataNative();
|
||||||
|
|
||||||
private native boolean cancelDeviceCreationNative(String address);
|
private native boolean cancelDeviceCreationNative(String address);
|
||||||
private native boolean removeDeviceNative(String objectPath);
|
private native boolean removeDeviceNative(String objectPath);
|
||||||
private native int getDeviceServiceChannelNative(String objectPath, String uuid,
|
private native int getDeviceServiceChannelNative(String objectPath, String uuid,
|
||||||
@ -2094,6 +2177,9 @@ public class BluetoothService extends IBluetooth.Stub {
|
|||||||
private native boolean setPasskeyNative(String address, int passkey, int nativeData);
|
private native boolean setPasskeyNative(String address, int passkey, int nativeData);
|
||||||
private native boolean setPairingConfirmationNative(String address, boolean confirm,
|
private native boolean setPairingConfirmationNative(String address, boolean confirm,
|
||||||
int nativeData);
|
int nativeData);
|
||||||
|
private native boolean setRemoteOutOfBandDataNative(String address, byte[] hash,
|
||||||
|
byte[] randomizer, int nativeData);
|
||||||
|
|
||||||
private native boolean setDevicePropertyBooleanNative(String objectPath, String key,
|
private native boolean setDevicePropertyBooleanNative(String objectPath, String key,
|
||||||
int value);
|
int value);
|
||||||
private native boolean createDeviceNative(String address);
|
private native boolean createDeviceNative(String address);
|
||||||
|
@ -61,6 +61,8 @@ static jmethodID method_onRequestPasskey;
|
|||||||
static jmethodID method_onRequestPasskeyConfirmation;
|
static jmethodID method_onRequestPasskeyConfirmation;
|
||||||
static jmethodID method_onRequestPairingConsent;
|
static jmethodID method_onRequestPairingConsent;
|
||||||
static jmethodID method_onDisplayPasskey;
|
static jmethodID method_onDisplayPasskey;
|
||||||
|
static jmethodID method_onRequestOobData;
|
||||||
|
static jmethodID method_onAgentOutOfBandDataAvailable;
|
||||||
static jmethodID method_onAgentAuthorize;
|
static jmethodID method_onAgentAuthorize;
|
||||||
static jmethodID method_onAgentCancel;
|
static jmethodID method_onAgentCancel;
|
||||||
|
|
||||||
@ -105,6 +107,8 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
|
|||||||
|
|
||||||
method_onAgentAuthorize = env->GetMethodID(clazz, "onAgentAuthorize",
|
method_onAgentAuthorize = env->GetMethodID(clazz, "onAgentAuthorize",
|
||||||
"(Ljava/lang/String;Ljava/lang/String;)Z");
|
"(Ljava/lang/String;Ljava/lang/String;)Z");
|
||||||
|
method_onAgentOutOfBandDataAvailable = env->GetMethodID(clazz, "onAgentOutOfBandDataAvailable",
|
||||||
|
"(Ljava/lang/String;)Z");
|
||||||
method_onAgentCancel = env->GetMethodID(clazz, "onAgentCancel", "()V");
|
method_onAgentCancel = env->GetMethodID(clazz, "onAgentCancel", "()V");
|
||||||
method_onRequestPinCode = env->GetMethodID(clazz, "onRequestPinCode",
|
method_onRequestPinCode = env->GetMethodID(clazz, "onRequestPinCode",
|
||||||
"(Ljava/lang/String;I)V");
|
"(Ljava/lang/String;I)V");
|
||||||
@ -116,6 +120,8 @@ static void classInitNative(JNIEnv* env, jclass clazz) {
|
|||||||
"(Ljava/lang/String;I)V");
|
"(Ljava/lang/String;I)V");
|
||||||
method_onDisplayPasskey = env->GetMethodID(clazz, "onDisplayPasskey",
|
method_onDisplayPasskey = env->GetMethodID(clazz, "onDisplayPasskey",
|
||||||
"(Ljava/lang/String;II)V");
|
"(Ljava/lang/String;II)V");
|
||||||
|
method_onRequestOobData = env->GetMethodID(clazz, "onRequestOobData",
|
||||||
|
"(Ljava/lang/String;I)V");
|
||||||
|
|
||||||
field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I");
|
field_mNativeData = env->GetFieldID(clazz, "mNativeData", "I");
|
||||||
#endif
|
#endif
|
||||||
@ -305,6 +311,7 @@ static int register_agent(native_data_t *nat,
|
|||||||
{
|
{
|
||||||
DBusMessage *msg, *reply;
|
DBusMessage *msg, *reply;
|
||||||
DBusError err;
|
DBusError err;
|
||||||
|
bool oob = TRUE;
|
||||||
|
|
||||||
if (!dbus_connection_register_object_path(nat->conn, agent_path,
|
if (!dbus_connection_register_object_path(nat->conn, agent_path,
|
||||||
&agent_vtable, nat)) {
|
&agent_vtable, nat)) {
|
||||||
@ -326,6 +333,7 @@ static int register_agent(native_data_t *nat,
|
|||||||
}
|
}
|
||||||
dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path,
|
dbus_message_append_args(msg, DBUS_TYPE_OBJECT_PATH, &agent_path,
|
||||||
DBUS_TYPE_STRING, &capabilities,
|
DBUS_TYPE_STRING, &capabilities,
|
||||||
|
DBUS_TYPE_BOOLEAN, &oob,
|
||||||
DBUS_TYPE_INVALID);
|
DBUS_TYPE_INVALID);
|
||||||
|
|
||||||
dbus_error_init(&err);
|
dbus_error_init(&err);
|
||||||
@ -933,6 +941,43 @@ DBusHandlerResult agent_event_filter(DBusConnection *conn,
|
|||||||
dbus_message_unref(reply);
|
dbus_message_unref(reply);
|
||||||
}
|
}
|
||||||
goto success;
|
goto success;
|
||||||
|
} else if (dbus_message_is_method_call(msg,
|
||||||
|
"org.bluez.Agent", "OutOfBandAvailable")) {
|
||||||
|
char *object_path;
|
||||||
|
if (!dbus_message_get_args(msg, NULL,
|
||||||
|
DBUS_TYPE_OBJECT_PATH, &object_path,
|
||||||
|
DBUS_TYPE_INVALID)) {
|
||||||
|
LOGE("%s: Invalid arguments for OutOfBandData available() method", __FUNCTION__);
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOGV("... object_path = %s", object_path);
|
||||||
|
|
||||||
|
bool available =
|
||||||
|
env->CallBooleanMethod(nat->me, method_onAgentOutOfBandDataAvailable,
|
||||||
|
env->NewStringUTF(object_path));
|
||||||
|
|
||||||
|
|
||||||
|
// reply
|
||||||
|
if (available) {
|
||||||
|
DBusMessage *reply = dbus_message_new_method_return(msg);
|
||||||
|
if (!reply) {
|
||||||
|
LOGE("%s: Cannot create message reply\n", __FUNCTION__);
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
dbus_connection_send(nat->conn, reply, NULL);
|
||||||
|
dbus_message_unref(reply);
|
||||||
|
} else {
|
||||||
|
DBusMessage *reply = dbus_message_new_error(msg,
|
||||||
|
"org.bluez.Error.DoesNotExist", "OutofBand data not available");
|
||||||
|
if (!reply) {
|
||||||
|
LOGE("%s: Cannot create message reply\n", __FUNCTION__);
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
dbus_connection_send(nat->conn, reply, NULL);
|
||||||
|
dbus_message_unref(reply);
|
||||||
|
}
|
||||||
|
goto success;
|
||||||
} else if (dbus_message_is_method_call(msg,
|
} else if (dbus_message_is_method_call(msg,
|
||||||
"org.bluez.Agent", "RequestPinCode")) {
|
"org.bluez.Agent", "RequestPinCode")) {
|
||||||
char *object_path;
|
char *object_path;
|
||||||
@ -963,6 +1008,21 @@ DBusHandlerResult agent_event_filter(DBusConnection *conn,
|
|||||||
env->NewStringUTF(object_path),
|
env->NewStringUTF(object_path),
|
||||||
int(msg));
|
int(msg));
|
||||||
goto success;
|
goto success;
|
||||||
|
} else if (dbus_message_is_method_call(msg,
|
||||||
|
"org.bluez.Agent", "RequestOobData")) {
|
||||||
|
char *object_path;
|
||||||
|
if (!dbus_message_get_args(msg, NULL,
|
||||||
|
DBUS_TYPE_OBJECT_PATH, &object_path,
|
||||||
|
DBUS_TYPE_INVALID)) {
|
||||||
|
LOGE("%s: Invalid arguments for RequestOobData() method", __FUNCTION__);
|
||||||
|
goto failure;
|
||||||
|
}
|
||||||
|
|
||||||
|
dbus_message_ref(msg); // increment refcount because we pass to java
|
||||||
|
env->CallVoidMethod(nat->me, method_onRequestOobData,
|
||||||
|
env->NewStringUTF(object_path),
|
||||||
|
int(msg));
|
||||||
|
goto success;
|
||||||
} else if (dbus_message_is_method_call(msg,
|
} else if (dbus_message_is_method_call(msg,
|
||||||
"org.bluez.Agent", "DisplayPasskey")) {
|
"org.bluez.Agent", "DisplayPasskey")) {
|
||||||
char *object_path;
|
char *object_path;
|
||||||
|
@ -288,6 +288,46 @@ done:
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static jbyteArray readAdapterOutOfBandDataNative(JNIEnv *env, jobject object) {
|
||||||
|
LOGV(__FUNCTION__);
|
||||||
|
#ifdef HAVE_BLUETOOTH
|
||||||
|
native_data_t *nat = get_native_data(env, object);
|
||||||
|
DBusError err;
|
||||||
|
jbyte *hash, *randomizer;
|
||||||
|
jbyteArray byteArray = NULL;
|
||||||
|
int hash_len, r_len;
|
||||||
|
if (nat) {
|
||||||
|
DBusMessage *reply = dbus_func_args(env, nat->conn,
|
||||||
|
get_adapter_path(env, object),
|
||||||
|
DBUS_ADAPTER_IFACE, "ReadLocalOutOfBandData",
|
||||||
|
DBUS_TYPE_INVALID);
|
||||||
|
if (!reply) return NULL;
|
||||||
|
|
||||||
|
dbus_error_init(&err);
|
||||||
|
if (dbus_message_get_args(reply, &err,
|
||||||
|
DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &hash, &hash_len,
|
||||||
|
DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &randomizer, &r_len,
|
||||||
|
DBUS_TYPE_INVALID)) {
|
||||||
|
if (hash_len == 16 && r_len == 16) {
|
||||||
|
byteArray = env->NewByteArray(32);
|
||||||
|
if (byteArray) {
|
||||||
|
env->SetByteArrayRegion(byteArray, 0, 16, hash);
|
||||||
|
env->SetByteArrayRegion(byteArray, 16, 16, randomizer);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOGE("readAdapterOutOfBandDataNative: Hash len = %d, R len = %d",
|
||||||
|
hash_len, r_len);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
LOG_AND_FREE_DBUS_ERROR(&err);
|
||||||
|
}
|
||||||
|
dbus_message_unref(reply);
|
||||||
|
return byteArray;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
static jboolean createPairedDeviceNative(JNIEnv *env, jobject object,
|
static jboolean createPairedDeviceNative(JNIEnv *env, jobject object,
|
||||||
jstring address, jint timeout_ms) {
|
jstring address, jint timeout_ms) {
|
||||||
LOGV(__FUNCTION__);
|
LOGV(__FUNCTION__);
|
||||||
@ -324,6 +364,41 @@ static jboolean createPairedDeviceNative(JNIEnv *env, jobject object,
|
|||||||
return JNI_FALSE;
|
return JNI_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static jboolean createPairedDeviceOutOfBandNative(JNIEnv *env, jobject object,
|
||||||
|
jstring address, jint timeout_ms) {
|
||||||
|
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));
|
||||||
|
const char *capabilities = "DisplayYesNo";
|
||||||
|
const char *agent_path = "/android/bluetooth/remote_device_agent";
|
||||||
|
|
||||||
|
strlcpy(context_address, c_address, BTADDR_SIZE); // for callback
|
||||||
|
bool ret = dbus_func_args_async(env, nat->conn, (int)timeout_ms,
|
||||||
|
onCreatePairedDeviceResult, // callback
|
||||||
|
context_address,
|
||||||
|
eventLoopNat,
|
||||||
|
get_adapter_path(env, object),
|
||||||
|
DBUS_ADAPTER_IFACE,
|
||||||
|
"CreatePairedDeviceOutOfBand",
|
||||||
|
DBUS_TYPE_STRING, &c_address,
|
||||||
|
DBUS_TYPE_OBJECT_PATH, &agent_path,
|
||||||
|
DBUS_TYPE_STRING, &capabilities,
|
||||||
|
DBUS_TYPE_INVALID);
|
||||||
|
env->ReleaseStringUTFChars(address, c_address);
|
||||||
|
return ret ? JNI_TRUE : JNI_FALSE;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return JNI_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
static jint getDeviceServiceChannelNative(JNIEnv *env, jobject object,
|
static jint getDeviceServiceChannelNative(JNIEnv *env, jobject object,
|
||||||
jstring path,
|
jstring path,
|
||||||
jstring pattern, jint attr_id) {
|
jstring pattern, jint attr_id) {
|
||||||
@ -490,6 +565,40 @@ static jboolean setPasskeyNative(JNIEnv *env, jobject object, jstring address,
|
|||||||
return JNI_FALSE;
|
return JNI_FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static jboolean setRemoteOutOfBandDataNative(JNIEnv *env, jobject object, jstring address,
|
||||||
|
jbyteArray hash, jbyteArray randomizer, 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);
|
||||||
|
jbyte *h_ptr = env->GetByteArrayElements(hash, NULL);
|
||||||
|
jbyte *r_ptr = env->GetByteArrayElements(randomizer, NULL);
|
||||||
|
if (!reply) {
|
||||||
|
LOGE("%s: Cannot create message reply to return remote OOB data to "
|
||||||
|
"D-Bus\n", __FUNCTION__);
|
||||||
|
dbus_message_unref(msg);
|
||||||
|
return JNI_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
dbus_message_append_args(reply,
|
||||||
|
DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &h_ptr, 16,
|
||||||
|
DBUS_TYPE_ARRAY, DBUS_TYPE_BYTE, &r_ptr, 16,
|
||||||
|
DBUS_TYPE_INVALID);
|
||||||
|
|
||||||
|
env->ReleaseByteArrayElements(hash, h_ptr, 0);
|
||||||
|
env->ReleaseByteArrayElements(randomizer, r_ptr, 0);
|
||||||
|
|
||||||
|
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,
|
static jboolean setPinNative(JNIEnv *env, jobject object, jstring address,
|
||||||
jstring pin, int nativeData) {
|
jstring pin, int nativeData) {
|
||||||
#ifdef HAVE_BLUETOOTH
|
#ifdef HAVE_BLUETOOTH
|
||||||
@ -907,7 +1016,10 @@ static JNINativeMethod sMethods[] = {
|
|||||||
{"startDiscoveryNative", "()Z", (void*)startDiscoveryNative},
|
{"startDiscoveryNative", "()Z", (void*)startDiscoveryNative},
|
||||||
{"stopDiscoveryNative", "()Z", (void *)stopDiscoveryNative},
|
{"stopDiscoveryNative", "()Z", (void *)stopDiscoveryNative},
|
||||||
|
|
||||||
|
{"readAdapterOutOfBandDataNative", "()[B", (void *)readAdapterOutOfBandDataNative},
|
||||||
{"createPairedDeviceNative", "(Ljava/lang/String;I)Z", (void *)createPairedDeviceNative},
|
{"createPairedDeviceNative", "(Ljava/lang/String;I)Z", (void *)createPairedDeviceNative},
|
||||||
|
{"createPairedDeviceOutOfBandNative", "(Ljava/lang/String;I)Z",
|
||||||
|
(void *)createPairedDeviceOutOfBandNative},
|
||||||
{"cancelDeviceCreationNative", "(Ljava/lang/String;)Z", (void *)cancelDeviceCreationNative},
|
{"cancelDeviceCreationNative", "(Ljava/lang/String;)Z", (void *)cancelDeviceCreationNative},
|
||||||
{"removeDeviceNative", "(Ljava/lang/String;)Z", (void *)removeDeviceNative},
|
{"removeDeviceNative", "(Ljava/lang/String;)Z", (void *)removeDeviceNative},
|
||||||
{"getDeviceServiceChannelNative", "(Ljava/lang/String;Ljava/lang/String;I)I",
|
{"getDeviceServiceChannelNative", "(Ljava/lang/String;Ljava/lang/String;I)I",
|
||||||
@ -916,6 +1028,7 @@ static JNINativeMethod sMethods[] = {
|
|||||||
{"setPairingConfirmationNative", "(Ljava/lang/String;ZI)Z",
|
{"setPairingConfirmationNative", "(Ljava/lang/String;ZI)Z",
|
||||||
(void *)setPairingConfirmationNative},
|
(void *)setPairingConfirmationNative},
|
||||||
{"setPasskeyNative", "(Ljava/lang/String;II)Z", (void *)setPasskeyNative},
|
{"setPasskeyNative", "(Ljava/lang/String;II)Z", (void *)setPasskeyNative},
|
||||||
|
{"setRemoteOutOfBandDataNative", "(Ljava/lang/String;[B[BI)Z", (void *)setRemoteOutOfBandDataNative},
|
||||||
{"setPinNative", "(Ljava/lang/String;Ljava/lang/String;I)Z", (void *)setPinNative},
|
{"setPinNative", "(Ljava/lang/String;Ljava/lang/String;I)Z", (void *)setPinNative},
|
||||||
{"cancelPairingUserInputNative", "(Ljava/lang/String;I)Z",
|
{"cancelPairingUserInputNative", "(Ljava/lang/String;I)Z",
|
||||||
(void *)cancelPairingUserInputNative},
|
(void *)cancelPairingUserInputNative},
|
||||||
|
Reference in New Issue
Block a user