[go: nahoru, domu]

blob: 8a06436418776f57ba9b65007575662dd21ec01e [file] [log] [blame]
// Copyright 2015 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
package org.chromium.device.bluetooth;
import android.Manifest;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.le.ScanFilter;
import android.bluetooth.le.ScanSettings;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.ParcelUuid;
import android.test.mock.MockContext;
import android.util.SparseArray;
import org.jni_zero.CalledByNative;
import org.jni_zero.JNINamespace;
import org.jni_zero.NativeMethods;
import org.chromium.base.Log;
import org.chromium.components.location.LocationUtils;
import org.chromium.device.bluetooth.test.TestRSSI;
import org.chromium.device.bluetooth.test.TestTxPower;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/**
* Fake implementations of android.bluetooth.* classes for testing.
*
* Fakes are contained in a single file to simplify code. Only one C++ file may
* access a Java file via JNI, and all of these classes are accessed by
* bluetooth_test_android.cc. The alternative would be a C++ .h, .cc file for
* each of these classes.
*/
@JNINamespace("device")
class Fakes {
private static final String TAG = "Bluetooth";
// Android uses Integer.MIN_VALUE to signal no Tx Power in advertisement
// packet.
// https://developer.android.com/reference/android/bluetooth/le/ScanRecord.html#getTxPowerLevel()
private static final int NO_TX_POWER = Integer.MIN_VALUE;
/**
* Sets the factory for LocationUtils to return an instance whose
* isSystemLocationSettingEnabled method returns |isEnabled|.
*/
@CalledByNative
public static void setLocationServicesState(final boolean isEnabled) {
LocationUtils.setFactory(new LocationUtils.Factory() {
@Override
public LocationUtils create() {
return new LocationUtils() {
@Override
public boolean isSystemLocationSettingEnabled() {
return isEnabled;
}
};
}
});
}
/**
* Sets the factory for ThreadUtilsWrapper to always post a task to the UI thread
* rather than running the task immediately. This simulates events arriving on a separate
* thread on Android.
* runOnUiThread uses FakesJni.get().postTaskFromJava. This allows java to post tasks to the
* message loop that the test is using rather than to the Java message loop which
* is not running during tests.
*/
@CalledByNative
public static void initFakeThreadUtilsWrapper(final long nativeBluetoothTestAndroid) {
Wrappers.ThreadUtilsWrapper.setFactory(new Wrappers.ThreadUtilsWrapper.Factory() {
@Override
public Wrappers.ThreadUtilsWrapper create() {
return new Wrappers.ThreadUtilsWrapper() {
@Override
public void runOnUiThread(Runnable r) {
FakesJni.get().postTaskFromJava(nativeBluetoothTestAndroid, r);
}
};
}
});
}
@CalledByNative
public static void runRunnable(Runnable r) {
r.run();
}
/**
* Fakes android.bluetooth.BluetoothAdapter.
*/
static class FakeBluetoothAdapter extends Wrappers.BluetoothAdapterWrapper {
private final FakeContext mFakeContext;
private final FakeBluetoothLeScanner mFakeScanner;
private boolean mPowered = true;
final long mNativeBluetoothTestAndroid;
/**
* Creates a FakeBluetoothAdapter.
*/
@CalledByNative("FakeBluetoothAdapter")
public static FakeBluetoothAdapter create(long nativeBluetoothTestAndroid) {
Log.v(TAG, "FakeBluetoothAdapter created.");
return new FakeBluetoothAdapter(nativeBluetoothTestAndroid);
}
private FakeBluetoothAdapter(long nativeBluetoothTestAndroid) {
super(null, new FakeContext());
mNativeBluetoothTestAndroid = nativeBluetoothTestAndroid;
mFakeContext = (FakeContext) mContext;
mFakeScanner = new FakeBluetoothLeScanner();
}
@CalledByNative("FakeBluetoothAdapter")
public void setFakeContextLocationPermission(boolean enabled) {
mFakeContext.setLocationPermission(enabled);
}
/**
* Creates and discovers a new device.
*/
@CalledByNative("FakeBluetoothAdapter")
public void simulateLowEnergyDevice(int deviceOrdinal) {
if (mFakeScanner == null) {
return;
}
switch (deviceOrdinal) {
case 1: {
ArrayList<ParcelUuid> uuids = new ArrayList<ParcelUuid>(2);
uuids.add(ParcelUuid.fromString("00001800-0000-1000-8000-00805f9b34fb"));
uuids.add(ParcelUuid.fromString("00001801-0000-1000-8000-00805f9b34fb"));
HashMap<ParcelUuid, byte[]> serviceData = new HashMap<>();
serviceData.put(ParcelUuid.fromString("0000180d-0000-1000-8000-00805f9b34fb"),
new byte[] {1});
SparseArray<byte[]> manufacturerData = new SparseArray<>();
manufacturerData.put(0x00E0, new byte[] {0x01, 0x02, 0x03, 0x04});
mFakeScanner.mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES,
new FakeScanResult(new FakeBluetoothDevice(this, "01:00:00:90:1E:BE",
"FakeBluetoothDevice"),
"FakeBluetoothDevice", TestRSSI.LOWEST, 4, uuids,
TestTxPower.LOWEST, serviceData, manufacturerData));
break;
}
case 2: {
ArrayList<ParcelUuid> uuids = new ArrayList<ParcelUuid>(2);
uuids.add(ParcelUuid.fromString("00001802-0000-1000-8000-00805f9b34fb"));
uuids.add(ParcelUuid.fromString("00001803-0000-1000-8000-00805f9b34fb"));
HashMap<ParcelUuid, byte[]> serviceData = new HashMap<>();
serviceData.put(ParcelUuid.fromString("0000180d-0000-1000-8000-00805f9b34fb"),
new byte[] {});
serviceData.put(ParcelUuid.fromString("00001802-0000-1000-8000-00805f9b34fb"),
new byte[] {0, 2});
SparseArray<byte[]> manufacturerData = new SparseArray<>();
manufacturerData.put(0x00E0, new byte[] {});
mFakeScanner.mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES,
new FakeScanResult(new FakeBluetoothDevice(this, "01:00:00:90:1E:BE",
"FakeBluetoothDevice"),
"Local Device Name", TestRSSI.LOWER, 5, uuids,
TestTxPower.LOWER, serviceData, manufacturerData));
break;
}
case 3: {
ArrayList<ParcelUuid> uuids = null;
mFakeScanner.mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES,
new FakeScanResult(
new FakeBluetoothDevice(this, "01:00:00:90:1E:BE", ""),
"Local Device Name", TestRSSI.LOW, -1, uuids, NO_TX_POWER, null,
null));
break;
}
case 4: {
ArrayList<ParcelUuid> uuids = null;
mFakeScanner.mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES,
new FakeScanResult(
new FakeBluetoothDevice(this, "02:00:00:8B:74:63", ""),
"Local Device Name", TestRSSI.MEDIUM, -1, uuids, NO_TX_POWER,
null, null));
break;
}
case 5: {
ArrayList<ParcelUuid> uuids = null;
mFakeScanner.mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES,
new FakeScanResult(
new FakeBluetoothDevice(this, "01:00:00:90:1E:BE", null),
"Local Device Name", TestRSSI.HIGH, -1, uuids, NO_TX_POWER,
null, null));
break;
}
case 6: {
ArrayList<ParcelUuid> uuids = null;
mFakeScanner.mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES,
new FakeScanResult(
new FakeBluetoothDevice(this, "02:00:00:8B:74:63", null),
"Local Device Name", TestRSSI.LOWEST, -1, uuids, NO_TX_POWER,
null, null));
break;
}
case 7: {
ArrayList<ParcelUuid> uuids = new ArrayList<ParcelUuid>(2);
uuids.add(ParcelUuid.fromString("f1d0fff3-deaa-ecee-b42f-c9ba7ed623bb"));
HashMap<ParcelUuid, byte[]> serviceData = new HashMap<>();
serviceData.put(ParcelUuid.fromString("f1d0fff3-deaa-ecee-b42f-c9ba7ed623bb"),
new byte[] {0, 20});
mFakeScanner.mScanCallback.onScanResult(ScanSettings.CALLBACK_TYPE_ALL_MATCHES,
new FakeScanResult(new FakeBluetoothDevice(
this, "01:00:00:90:1E:BE", "U2F FakeDevice"),
"Local Device Name", TestRSSI.LOWEST, -1, uuids, NO_TX_POWER,
serviceData, null));
break;
}
}
}
@CalledByNative("FakeBluetoothAdapter")
public void forceIllegalStateException() {
if (mFakeScanner != null) {
mFakeScanner.forceIllegalStateException();
}
}
// -----------------------------------------------------------------------------------------
// BluetoothAdapterWrapper overrides:
@Override
public boolean disable() {
// android.bluetooth.BluetoothAdapter::disable() is an async call, so we simulate this
// by posting a task to the UI thread.
FakesJni.get().postTaskFromJava(mNativeBluetoothTestAndroid, new Runnable() {
@Override
public void run() {
mPowered = false;
FakesJni.get().onFakeAdapterStateChanged(mNativeBluetoothTestAndroid, false);
}
});
return true;
}
@Override
public boolean enable() {
// android.bluetooth.BluetoothAdapter::enable() is an async call, so we simulate this by
// posting a task to the UI thread.
FakesJni.get().postTaskFromJava(mNativeBluetoothTestAndroid, new Runnable() {
@Override
public void run() {
mPowered = true;
FakesJni.get().onFakeAdapterStateChanged(mNativeBluetoothTestAndroid, true);
}
});
return true;
}
@Override
public String getAddress() {
return "A1:B2:C3:D4:E5:F6";
}
@Override
public Wrappers.BluetoothLeScannerWrapper getBluetoothLeScanner() {
if (isEnabled()) {
return mFakeScanner;
}
return null;
}
@Override
public String getName() {
return "FakeBluetoothAdapter";
}
@Override
public int getScanMode() {
return android.bluetooth.BluetoothAdapter.SCAN_MODE_NONE;
}
@Override
public boolean isEnabled() {
return mPowered;
}
@Override
public boolean isDiscovering() {
return false;
}
}
/**
* Fakes android.content.Context by extending MockContext.
*/
static class FakeContext extends MockContext {
private boolean mLocationPermission;
public FakeContext() {
super();
mLocationPermission = true;
}
public void setLocationPermission(boolean enabled) {
mLocationPermission = enabled;
}
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
String permission, Handler scheduler) {
return null;
}
@Override
public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter,
String permission, Handler scheduler, int flags) {
return null;
}
@Override
public void unregisterReceiver(BroadcastReceiver receiver) {}
@Override
public int checkCallingOrSelfPermission(String permission) {
if (permission.equals(Manifest.permission.ACCESS_FINE_LOCATION)
|| permission.equals(Manifest.permission.ACCESS_COARSE_LOCATION)) {
return mLocationPermission ? PackageManager.PERMISSION_GRANTED
: PackageManager.PERMISSION_DENIED;
}
return PackageManager.PERMISSION_DENIED;
}
}
/**
* Fakes android.bluetooth.le.BluetoothLeScanner.
*/
static class FakeBluetoothLeScanner extends Wrappers.BluetoothLeScannerWrapper {
public Wrappers.ScanCallbackWrapper mScanCallback;
private boolean mThrowException;
private FakeBluetoothLeScanner() {
super(null);
}
@Override
public void startScan(List<ScanFilter> filters, int scanSettingsScanMode,
Wrappers.ScanCallbackWrapper callback) {
if (mScanCallback != null) {
throw new IllegalArgumentException(
"FakeBluetoothLeScanner does not support multiple scans.");
}
if (mThrowException) {
throw new IllegalStateException("Adapter is off.");
}
mScanCallback = callback;
}
@Override
public void stopScan(Wrappers.ScanCallbackWrapper callback) {
if (mScanCallback != callback) {
throw new IllegalArgumentException("No scan in progress.");
}
if (mThrowException) {
throw new IllegalStateException("Adapter is off.");
}
mScanCallback = null;
}
void forceIllegalStateException() {
mThrowException = true;
}
}
/**
* Fakes android.bluetooth.le.ScanResult
*/
static class FakeScanResult extends Wrappers.ScanResultWrapper {
private final FakeBluetoothDevice mDevice;
private final String mLocalName;
private final int mRssi;
private final int mTxPower;
private final int mAdvertisementFlags;
private final ArrayList<ParcelUuid> mUuids;
private final Map<ParcelUuid, byte[]> mServiceData;
private final SparseArray<byte[]> mManufacturerData;
FakeScanResult(FakeBluetoothDevice device, String localName, int rssi,
int advertisementFlags, ArrayList<ParcelUuid> uuids, int txPower,
Map<ParcelUuid, byte[]> serviceData, SparseArray<byte[]> manufacturerData) {
super(null);
mDevice = device;
mLocalName = localName;
mRssi = rssi;
mAdvertisementFlags = advertisementFlags;
mUuids = uuids;
mTxPower = txPower;
mServiceData = serviceData;
mManufacturerData = manufacturerData;
}
@Override
public Wrappers.BluetoothDeviceWrapper getDevice() {
return mDevice;
}
@Override
public int getRssi() {
return mRssi;
}
@Override
public List<ParcelUuid> getScanRecord_getServiceUuids() {
return mUuids;
}
@Override
public int getScanRecord_getTxPowerLevel() {
return mTxPower;
}
@Override
public Map<ParcelUuid, byte[]> getScanRecord_getServiceData() {
return mServiceData;
}
@Override
public SparseArray<byte[]> getScanRecord_getManufacturerSpecificData() {
return mManufacturerData;
}
@Override
public String getScanRecord_getDeviceName() {
return mLocalName;
}
@Override
public int getScanRecord_getAdvertiseFlags() {
return mAdvertisementFlags;
}
}
/**
* Fakes android.bluetooth.BluetoothDevice.
*/
static class FakeBluetoothDevice extends Wrappers.BluetoothDeviceWrapper {
final FakeBluetoothAdapter mAdapter;
private String mAddress;
private String mName;
final FakeBluetoothGatt mGatt;
private Wrappers.BluetoothGattCallbackWrapper mGattCallback;
static FakeBluetoothDevice sRememberedDevice;
public FakeBluetoothDevice(FakeBluetoothAdapter adapter, String address, String name) {
super(null);
mAdapter = adapter;
mAddress = address;
mName = name;
mGatt = new FakeBluetoothGatt(this);
}
// Implements BluetoothTestAndroid::RememberDeviceForSubsequentAction.
@CalledByNative("FakeBluetoothDevice")
private static void rememberDeviceForSubsequentAction(ChromeBluetoothDevice chromeDevice) {
sRememberedDevice = (FakeBluetoothDevice) chromeDevice.mDevice;
}
// Create a call to onConnectionStateChange on the |chrome_device| using parameters
// |status| & |connected|.
@CalledByNative("FakeBluetoothDevice")
private static void connectionStateChange(
ChromeBluetoothDevice chromeDevice, int status, boolean connected) {
FakeBluetoothDevice fakeDevice = (FakeBluetoothDevice) chromeDevice.mDevice;
fakeDevice.mGattCallback.onConnectionStateChange(status, connected
? android.bluetooth.BluetoothProfile.STATE_CONNECTED
: android.bluetooth.BluetoothProfile.STATE_DISCONNECTED);
}
// Create a call to onServicesDiscovered on the |chrome_device| using parameter
// |status|.
@CalledByNative("FakeBluetoothDevice")
private static void servicesDiscovered(
ChromeBluetoothDevice chromeDevice, int status, String uuidsSpaceDelimited) {
if (chromeDevice == null && sRememberedDevice == null) {
throw new IllegalArgumentException("rememberDevice wasn't called previously.");
}
FakeBluetoothDevice fakeDevice = (chromeDevice == null)
? sRememberedDevice
: (FakeBluetoothDevice) chromeDevice.mDevice;
if (status == android.bluetooth.BluetoothGatt.GATT_SUCCESS) {
fakeDevice.mGatt.mServices.clear();
HashMap<String, Integer> uuidsToInstanceIdMap = new HashMap<String, Integer>();
for (String uuid : uuidsSpaceDelimited.split(" ")) {
// String.split() can return empty strings. Ignore them.
if (uuid.isEmpty()) continue;
Integer previousId = uuidsToInstanceIdMap.get(uuid);
int instanceId = (previousId == null) ? 0 : previousId + 1;
uuidsToInstanceIdMap.put(uuid, instanceId);
fakeDevice.mGatt.mServices.add(new FakeBluetoothGattService(
fakeDevice, UUID.fromString(uuid), instanceId));
}
}
fakeDevice.mGattCallback.onServicesDiscovered(status);
}
// -----------------------------------------------------------------------------------------
// Wrappers.BluetoothDeviceWrapper overrides:
@Override
public Wrappers.BluetoothGattWrapper connectGatt(Context context, boolean autoConnect,
Wrappers.BluetoothGattCallbackWrapper callback, int transport) {
if (mGattCallback != null && mGattCallback != callback) {
throw new IllegalArgumentException(
"BluetoothGattWrapper doesn't support calls to connectGatt() with "
+ "multiple distinct callbacks.");
}
FakesJni.get().onFakeBluetoothDeviceConnectGattCalled(
mAdapter.mNativeBluetoothTestAndroid);
mGattCallback = callback;
return mGatt;
}
@Override
public String getAddress() {
return mAddress;
}
@Override
public int getBluetoothClass_getDeviceClass() {
return Wrappers.DEVICE_CLASS_UNSPECIFIED;
}
@Override
public int getBondState() {
return BluetoothDevice.BOND_NONE;
}
@Override
public String getName() {
return mName;
}
}
/**
* Fakes android.bluetooth.BluetoothGatt.
*/
static class FakeBluetoothGatt extends Wrappers.BluetoothGattWrapper {
final FakeBluetoothDevice mDevice;
final ArrayList<Wrappers.BluetoothGattServiceWrapper> mServices;
boolean mReadCharacteristicWillFailSynchronouslyOnce;
boolean mSetCharacteristicNotificationWillFailSynchronouslyOnce;
boolean mWriteCharacteristicWillFailSynchronouslyOnce;
boolean mReadDescriptorWillFailSynchronouslyOnce;
boolean mWriteDescriptorWillFailSynchronouslyOnce;
public FakeBluetoothGatt(FakeBluetoothDevice device) {
super(null, null);
mDevice = device;
mServices = new ArrayList<Wrappers.BluetoothGattServiceWrapper>();
}
@Override
public void disconnect() {
FakesJni.get().onFakeBluetoothGattDisconnect(
mDevice.mAdapter.mNativeBluetoothTestAndroid);
}
@Override
public void close() {
FakesJni.get().onFakeBluetoothGattClose(mDevice.mAdapter.mNativeBluetoothTestAndroid);
}
@Override
public boolean requestMtu(int mtu) {
return false;
}
@Override
public void discoverServices() {
FakesJni.get().onFakeBluetoothGattDiscoverServices(
mDevice.mAdapter.mNativeBluetoothTestAndroid);
}
@Override
public List<Wrappers.BluetoothGattServiceWrapper> getServices() {
return mServices;
}
@Override
boolean readCharacteristic(Wrappers.BluetoothGattCharacteristicWrapper characteristic) {
if (mReadCharacteristicWillFailSynchronouslyOnce) {
mReadCharacteristicWillFailSynchronouslyOnce = false;
return false;
}
FakesJni.get().onFakeBluetoothGattReadCharacteristic(
mDevice.mAdapter.mNativeBluetoothTestAndroid);
return true;
}
@Override
boolean setCharacteristicNotification(
Wrappers.BluetoothGattCharacteristicWrapper characteristic, boolean enable) {
if (mSetCharacteristicNotificationWillFailSynchronouslyOnce) {
mSetCharacteristicNotificationWillFailSynchronouslyOnce = false;
return false;
}
FakesJni.get().onFakeBluetoothGattSetCharacteristicNotification(
mDevice.mAdapter.mNativeBluetoothTestAndroid);
return true;
}
@Override
boolean writeCharacteristic(Wrappers.BluetoothGattCharacteristicWrapper characteristic) {
if (mWriteCharacteristicWillFailSynchronouslyOnce) {
mWriteCharacteristicWillFailSynchronouslyOnce = false;
return false;
}
FakesJni.get().onFakeBluetoothGattWriteCharacteristic(
mDevice.mAdapter.mNativeBluetoothTestAndroid, characteristic.getValue());
return true;
}
@Override
boolean readDescriptor(Wrappers.BluetoothGattDescriptorWrapper descriptor) {
if (mReadDescriptorWillFailSynchronouslyOnce) {
mReadDescriptorWillFailSynchronouslyOnce = false;
return false;
}
FakesJni.get().onFakeBluetoothGattReadDescriptor(
mDevice.mAdapter.mNativeBluetoothTestAndroid);
return true;
}
@Override
boolean writeDescriptor(Wrappers.BluetoothGattDescriptorWrapper descriptor) {
if (mWriteDescriptorWillFailSynchronouslyOnce) {
mWriteDescriptorWillFailSynchronouslyOnce = false;
return false;
}
FakesJni.get().onFakeBluetoothGattWriteDescriptor(
mDevice.mAdapter.mNativeBluetoothTestAndroid, descriptor.getValue());
return true;
}
}
/**
* Fakes android.bluetooth.BluetoothGattService.
*/
static class FakeBluetoothGattService extends Wrappers.BluetoothGattServiceWrapper {
final FakeBluetoothDevice mDevice;
final int mInstanceId;
final UUID mUuid;
final ArrayList<Wrappers.BluetoothGattCharacteristicWrapper> mCharacteristics;
public FakeBluetoothGattService(FakeBluetoothDevice device, UUID uuid, int instanceId) {
super(null, null);
mDevice = device;
mUuid = uuid;
mInstanceId = instanceId;
mCharacteristics = new ArrayList<Wrappers.BluetoothGattCharacteristicWrapper>();
}
// Create a characteristic and add it to this service.
@CalledByNative("FakeBluetoothGattService")
private static void addCharacteristic(
ChromeBluetoothRemoteGattService chromeService, String uuidString, int properties) {
FakeBluetoothGattService fakeService =
(FakeBluetoothGattService) chromeService.mService;
UUID uuid = UUID.fromString(uuidString);
int countOfDuplicateUUID = 0;
for (Wrappers.BluetoothGattCharacteristicWrapper characteristic :
fakeService.mCharacteristics) {
if (characteristic.getUuid().equals(uuid)) {
countOfDuplicateUUID++;
}
}
fakeService.mCharacteristics.add(new FakeBluetoothGattCharacteristic(fakeService,
/* instanceId */ countOfDuplicateUUID, properties, uuid));
}
// -----------------------------------------------------------------------------------------
// Wrappers.BluetoothGattServiceWrapper overrides:
@Override
public List<Wrappers.BluetoothGattCharacteristicWrapper> getCharacteristics() {
return mCharacteristics;
}
@Override
public int getInstanceId() {
return mInstanceId;
}
@Override
public UUID getUuid() {
return mUuid;
}
}
/**
* Fakes android.bluetooth.BluetoothGattCharacteristic.
*/
static class FakeBluetoothGattCharacteristic
extends Wrappers.BluetoothGattCharacteristicWrapper {
final FakeBluetoothGattService mService;
final int mInstanceId;
final int mProperties;
final UUID mUuid;
byte[] mValue;
int mWriteType;
static FakeBluetoothGattCharacteristic sRememberedCharacteristic;
final ArrayList<Wrappers.BluetoothGattDescriptorWrapper> mDescriptors;
public FakeBluetoothGattCharacteristic(
FakeBluetoothGattService service, int instanceId, int properties, UUID uuid) {
super(null, null);
mService = service;
mInstanceId = instanceId;
mProperties = properties;
mUuid = uuid;
mValue = new byte[0];
mDescriptors = new ArrayList<Wrappers.BluetoothGattDescriptorWrapper>();
}
// Simulate a characteristic value notified as changed.
@CalledByNative("FakeBluetoothGattCharacteristic")
private static void valueChanged(
ChromeBluetoothRemoteGattCharacteristic chromeCharacteristic, byte[] value) {
if (chromeCharacteristic == null && sRememberedCharacteristic == null) {
throw new IllegalArgumentException(
"rememberCharacteristic wasn't called previously.");
}
FakeBluetoothGattCharacteristic fakeCharacteristic = (chromeCharacteristic == null)
? sRememberedCharacteristic
: (FakeBluetoothGattCharacteristic) chromeCharacteristic.mCharacteristic;
fakeCharacteristic.mValue = value;
fakeCharacteristic.mService.mDevice.mGattCallback.onCharacteristicChanged(
fakeCharacteristic);
}
// Implements BluetoothTestAndroid::RememberCharacteristicForSubsequentAction.
@CalledByNative("FakeBluetoothGattCharacteristic")
private static void rememberCharacteristicForSubsequentAction(
ChromeBluetoothRemoteGattCharacteristic chromeCharacteristic) {
sRememberedCharacteristic =
(FakeBluetoothGattCharacteristic) chromeCharacteristic.mCharacteristic;
}
// Simulate a value being read from a characteristic.
@CalledByNative("FakeBluetoothGattCharacteristic")
private static void valueRead(ChromeBluetoothRemoteGattCharacteristic chromeCharacteristic,
int status, byte[] value) {
if (chromeCharacteristic == null && sRememberedCharacteristic == null) {
throw new IllegalArgumentException(
"rememberCharacteristic wasn't called previously.");
}
FakeBluetoothGattCharacteristic fakeCharacteristic = (chromeCharacteristic == null)
? sRememberedCharacteristic
: (FakeBluetoothGattCharacteristic) chromeCharacteristic.mCharacteristic;
fakeCharacteristic.mValue = value;
fakeCharacteristic.mService.mDevice.mGattCallback.onCharacteristicRead(
fakeCharacteristic, status);
}
// Simulate a value being written to a characteristic.
@CalledByNative("FakeBluetoothGattCharacteristic")
private static void valueWrite(
ChromeBluetoothRemoteGattCharacteristic chromeCharacteristic, int status) {
if (chromeCharacteristic == null && sRememberedCharacteristic == null) {
throw new IllegalArgumentException(
"rememberCharacteristic wasn't called previously.");
}
FakeBluetoothGattCharacteristic fakeCharacteristic = (chromeCharacteristic == null)
? sRememberedCharacteristic
: (FakeBluetoothGattCharacteristic) chromeCharacteristic.mCharacteristic;
fakeCharacteristic.mService.mDevice.mGattCallback.onCharacteristicWrite(
fakeCharacteristic, status);
}
// Cause subsequent notification of a characteristic to fail synchronously.
@CalledByNative("FakeBluetoothGattCharacteristic")
private static void setCharacteristicNotificationWillFailSynchronouslyOnce(
ChromeBluetoothRemoteGattCharacteristic chromeCharacteristic) {
FakeBluetoothGattCharacteristic fakeCharacteristic =
(FakeBluetoothGattCharacteristic) chromeCharacteristic.mCharacteristic;
fakeCharacteristic.mService.mDevice.mGatt
.mSetCharacteristicNotificationWillFailSynchronouslyOnce = true;
}
// Cause subsequent value read of a characteristic to fail synchronously.
@CalledByNative("FakeBluetoothGattCharacteristic")
private static void setReadCharacteristicWillFailSynchronouslyOnce(
ChromeBluetoothRemoteGattCharacteristic chromeCharacteristic) {
FakeBluetoothGattCharacteristic fakeCharacteristic =
(FakeBluetoothGattCharacteristic) chromeCharacteristic.mCharacteristic;
fakeCharacteristic.mService.mDevice.mGatt.mReadCharacteristicWillFailSynchronouslyOnce =
true;
}
// Cause subsequent value write of a characteristic to fail synchronously.
@CalledByNative("FakeBluetoothGattCharacteristic")
private static void setWriteCharacteristicWillFailSynchronouslyOnce(
ChromeBluetoothRemoteGattCharacteristic chromeCharacteristic) {
FakeBluetoothGattCharacteristic fakeCharacteristic =
(FakeBluetoothGattCharacteristic) chromeCharacteristic.mCharacteristic;
fakeCharacteristic.mService.mDevice.mGatt
.mWriteCharacteristicWillFailSynchronouslyOnce = true;
}
// Create a descriptor and add it to this characteristic.
@CalledByNative("FakeBluetoothGattCharacteristic")
private static void addDescriptor(
ChromeBluetoothRemoteGattCharacteristic chromeCharacteristic, String uuidString) {
FakeBluetoothGattCharacteristic fakeCharacteristic =
(FakeBluetoothGattCharacteristic) chromeCharacteristic.mCharacteristic;
UUID uuid = UUID.fromString(uuidString);
fakeCharacteristic.mDescriptors.add(
new FakeBluetoothGattDescriptor(fakeCharacteristic, uuid));
}
// -----------------------------------------------------------------------------------------
// Wrappers.BluetoothGattCharacteristicWrapper overrides:
@Override
public List<Wrappers.BluetoothGattDescriptorWrapper> getDescriptors() {
return mDescriptors;
}
@Override
public int getInstanceId() {
return mInstanceId;
}
@Override
public int getProperties() {
return mProperties;
}
@Override
public UUID getUuid() {
return mUuid;
}
@Override
public byte[] getValue() {
return mValue;
}
@Override
public boolean setValue(byte[] value) {
mValue = value;
return true;
}
@Override
public void setWriteType(int writeType) {
mWriteType = writeType;
}
}
/**
* Fakes android.bluetooth.BluetoothGattDescriptor.
*/
static class FakeBluetoothGattDescriptor extends Wrappers.BluetoothGattDescriptorWrapper {
final FakeBluetoothGattCharacteristic mCharacteristic;
final UUID mUuid;
byte[] mValue;
static FakeBluetoothGattDescriptor sRememberedDescriptor;
public FakeBluetoothGattDescriptor(
FakeBluetoothGattCharacteristic characteristic, UUID uuid) {
super(null, null);
mCharacteristic = characteristic;
mUuid = uuid;
mValue = new byte[0];
}
// Implements BluetoothTestAndroid::RememberDescriptorForSubsequentAction.
@CalledByNative("FakeBluetoothGattDescriptor")
private static void rememberDescriptorForSubsequentAction(
ChromeBluetoothRemoteGattDescriptor chromeDescriptor) {
sRememberedDescriptor = (FakeBluetoothGattDescriptor) chromeDescriptor.mDescriptor;
}
// Simulate a value being read from a descriptor.
@CalledByNative("FakeBluetoothGattDescriptor")
private static void valueRead(
ChromeBluetoothRemoteGattDescriptor chromeDescriptor, int status, byte[] value) {
if (chromeDescriptor == null && sRememberedDescriptor == null) {
throw new IllegalArgumentException("rememberDescriptor wasn't called previously.");
}
FakeBluetoothGattDescriptor fakeDescriptor = (chromeDescriptor == null)
? sRememberedDescriptor
: (FakeBluetoothGattDescriptor) chromeDescriptor.mDescriptor;
fakeDescriptor.mValue = value;
fakeDescriptor.mCharacteristic.mService.mDevice.mGattCallback.onDescriptorRead(
fakeDescriptor, status);
}
// Simulate a value being written to a descriptor.
@CalledByNative("FakeBluetoothGattDescriptor")
private static void valueWrite(
ChromeBluetoothRemoteGattDescriptor chromeDescriptor, int status) {
if (chromeDescriptor == null && sRememberedDescriptor == null) {
throw new IllegalArgumentException("rememberDescriptor wasn't called previously.");
}
FakeBluetoothGattDescriptor fakeDescriptor = (chromeDescriptor == null)
? sRememberedDescriptor
: (FakeBluetoothGattDescriptor) chromeDescriptor.mDescriptor;
fakeDescriptor.mCharacteristic.mService.mDevice.mGattCallback.onDescriptorWrite(
fakeDescriptor, status);
}
// Cause subsequent value read of a descriptor to fail synchronously.
@CalledByNative("FakeBluetoothGattDescriptor")
private static void setReadDescriptorWillFailSynchronouslyOnce(
ChromeBluetoothRemoteGattDescriptor chromeDescriptor) {
FakeBluetoothGattDescriptor fakeDescriptor =
(FakeBluetoothGattDescriptor) chromeDescriptor.mDescriptor;
fakeDescriptor.mCharacteristic.mService.mDevice.mGatt
.mReadDescriptorWillFailSynchronouslyOnce = true;
}
// Cause subsequent value write of a descriptor to fail synchronously.
@CalledByNative("FakeBluetoothGattDescriptor")
private static void setWriteDescriptorWillFailSynchronouslyOnce(
ChromeBluetoothRemoteGattDescriptor chromeDescriptor) {
FakeBluetoothGattDescriptor fakeDescriptor =
(FakeBluetoothGattDescriptor) chromeDescriptor.mDescriptor;
fakeDescriptor.mCharacteristic.mService.mDevice.mGatt
.mWriteDescriptorWillFailSynchronouslyOnce = true;
}
// -----------------------------------------------------------------------------------------
// Wrappers.BluetoothGattDescriptorWrapper overrides:
@Override
public Wrappers.BluetoothGattCharacteristicWrapper getCharacteristic() {
return mCharacteristic;
}
@Override
public UUID getUuid() {
return mUuid;
}
@Override
public byte[] getValue() {
return mValue;
}
@Override
public boolean setValue(byte[] value) {
mValue = value;
return true;
}
}
// ---------------------------------------------------------------------------------------------
// BluetoothTestAndroid C++ methods declared for access from java:
@NativeMethods
interface Natives {
// Bind to BluetoothTestAndroid::PostTaskFromJava.
void postTaskFromJava(long nativeBluetoothTestAndroid, Runnable r);
// Binds to BluetoothTestAndroid::OnFakeAdapterStateChanged.
void onFakeAdapterStateChanged(long nativeBluetoothTestAndroid, boolean powered);
// Binds to BluetoothTestAndroid::OnFakeBluetoothDeviceConnectGattCalled.
void onFakeBluetoothDeviceConnectGattCalled(long nativeBluetoothTestAndroid);
// Binds to BluetoothTestAndroid::OnFakeBluetoothGattDisconnect.
void onFakeBluetoothGattDisconnect(long nativeBluetoothTestAndroid);
// Binds to BluetoothTestAndroid::OnFakeBluetoothGattClose.
void onFakeBluetoothGattClose(long nativeBluetoothTestAndroid);
// Binds to BluetoothTestAndroid::OnFakeBluetoothGattDiscoverServices.
void onFakeBluetoothGattDiscoverServices(long nativeBluetoothTestAndroid);
// Binds to BluetoothTestAndroid::OnFakeBluetoothGattSetCharacteristicNotification.
void onFakeBluetoothGattSetCharacteristicNotification(long nativeBluetoothTestAndroid);
// Binds to BluetoothTestAndroid::OnFakeBluetoothGattReadCharacteristic.
void onFakeBluetoothGattReadCharacteristic(long nativeBluetoothTestAndroid);
// Binds to BluetoothTestAndroid::OnFakeBluetoothGattWriteCharacteristic.
void onFakeBluetoothGattWriteCharacteristic(long nativeBluetoothTestAndroid, byte[] value);
// Binds to BluetoothTestAndroid::OnFakeBluetoothGattReadDescriptor.
void onFakeBluetoothGattReadDescriptor(long nativeBluetoothTestAndroid);
// Binds to BluetoothTestAndroid::OnFakeBluetoothGattWriteDescriptor.
void onFakeBluetoothGattWriteDescriptor(long nativeBluetoothTestAndroid, byte[] value);
}
}